-doxygen
[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   na->connect_timestamp = GNUNET_TIME_absolute_get ();
1587   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1588   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
1589   connect_msg.reserved = htonl (0);
1590   connect_msg.timestamp = GNUNET_TIME_absolute_hton (na->connect_timestamp);
1591   (void) papi->send (papi->cls,
1592                      na->session,
1593                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1594                      UINT_MAX,
1595                      GNUNET_TIME_UNIT_FOREVER_REL,
1596                      NULL, NULL);
1597   GST_neighbours_notify_data_sent (&na->address->peer,
1598       na->address, na->session, sizeof (struct SessionConnectMessage));
1599
1600 }
1601
1602
1603 /**
1604  * Send a SESSION_CONNECT_ACK message via the given address.
1605  *
1606  * @param address address to use
1607  * @param session session to use
1608  * @param timestamp timestamp to use for the ACK message
1609  */
1610 static void
1611 send_session_connect_ack_message (const struct GNUNET_HELLO_Address *address,
1612                                   struct Session *session,
1613                                   struct GNUNET_TIME_Absolute timestamp)
1614 {
1615   struct GNUNET_TRANSPORT_PluginFunctions *papi;
1616   struct SessionConnectMessage connect_msg;
1617
1618   if (NULL == (papi = GST_plugins_find (address->transport_name)))
1619   {
1620     GNUNET_break (0);
1621     return;
1622   }
1623   if (NULL == session)
1624     session = papi->get_session (papi->cls, address);
1625   if (NULL == session)
1626   {
1627     GNUNET_break (0);
1628     return;
1629   }
1630   connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1631   connect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1632   connect_msg.reserved = htonl (0);
1633   connect_msg.timestamp = GNUNET_TIME_absolute_hton (timestamp);
1634   (void) papi->send (papi->cls,
1635                      session,
1636                      (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1637                      UINT_MAX,
1638                      GNUNET_TIME_UNIT_FOREVER_REL,
1639                      NULL, NULL);
1640
1641 }
1642
1643
1644 /**
1645  * Create a fresh entry in the neighbour map for the given peer
1646  *
1647  * @param peer peer to create an entry for
1648  * @return new neighbour map entry
1649  */
1650 static struct NeighbourMapEntry *
1651 setup_neighbour (const struct GNUNET_PeerIdentity *peer)
1652 {
1653   struct NeighbourMapEntry *n;
1654
1655   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1656               "Creating new neighbour entry for `%s'\n",
1657               GNUNET_i2s (peer));
1658   n = GNUNET_new (struct NeighbourMapEntry);
1659   n->id = *peer;
1660   n->state = S_NOT_CONNECTED;
1661   n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
1662   n->last_util_transmission = GNUNET_TIME_absolute_get();
1663   n->util_payload_bytes_recv = 0;
1664   n->util_payload_bytes_sent = 0;
1665   n->util_total_bytes_recv = 0;
1666   n->util_total_bytes_sent = 0;
1667   GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
1668                                  GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
1669                                  MAX_BANDWIDTH_CARRY_S);
1670   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
1671   GNUNET_assert (GNUNET_OK ==
1672                  GNUNET_CONTAINER_multipeermap_put (neighbours,
1673                                                     &n->id, n,
1674                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1675   return n;
1676 }
1677
1678
1679 /**
1680  * Check if the two given addresses are the same.
1681  * Actually only checks if the sessions are non-NULL
1682  * (which they should be) and then if they are identical;
1683  * the actual addresses don't matter if the session
1684  * pointers match anyway, and we must have session pointers
1685  * at this time.
1686  *
1687  * @param a1 first address to compare
1688  * @param a2 other address to compare
1689  * @return GNUNET_NO if the addresses do not match, GNUNET_YES if they do match
1690  */
1691 static int
1692 address_matches (const struct NeighbourAddress *a1,
1693                  const struct NeighbourAddress *a2)
1694 {
1695   if ( (NULL == a1->session) ||
1696        (NULL == a2->session) )
1697   {
1698     GNUNET_break (0);
1699     return 0;
1700   }
1701   return (a1->session == a2->session) ? GNUNET_YES : GNUNET_NO;
1702 }
1703
1704
1705 /**
1706  * Try to create a connection to the given target (eventually).
1707  *
1708  * @param target peer to try to connect to
1709  */
1710 void
1711 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
1712 {
1713   struct NeighbourMapEntry *n;
1714
1715   if (NULL == neighbours)
1716   {
1717           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1718                       "Asked to connect to peer `%s' during shutdown\n",
1719                       GNUNET_i2s (target));
1720                 return; /* during shutdown, do nothing */
1721   }
1722   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1723               "Asked to connect to peer `%s'\n",
1724               GNUNET_i2s (target));
1725   if (0 == memcmp (target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity)))
1726   {
1727     /* refuse to connect to myself */
1728     /* FIXME: can this happen? Is this not an API violation? */
1729     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1730                 "Refusing to try to connect to myself.\n");
1731     return;
1732   }
1733   n = lookup_neighbour (target);
1734   if (NULL != n)
1735   {
1736     switch (n->state)
1737     {
1738     case S_NOT_CONNECTED:
1739       /* this should not be possible */
1740       GNUNET_break (0);
1741       free_neighbour (n, GNUNET_NO);
1742       break;
1743     case S_INIT_ATS:
1744     case S_INIT_BLACKLIST:
1745     case S_CONNECT_SENT:
1746     case S_CONNECT_RECV_BLACKLIST_INBOUND:
1747     case S_CONNECT_RECV_ATS:
1748     case S_CONNECT_RECV_BLACKLIST:
1749     case S_CONNECT_RECV_ACK:
1750       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1751                 "Ignoring request to try to connect to `%s', already trying!\n",
1752                   GNUNET_i2s (target));
1753       return; /* already trying */
1754     case S_CONNECTED:
1755     case S_RECONNECT_ATS:
1756     case S_RECONNECT_BLACKLIST:
1757     case S_RECONNECT_SENT:
1758     case S_CONNECTED_SWITCHING_BLACKLIST:
1759     case S_CONNECTED_SWITCHING_CONNECT_SENT:
1760       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1761                 "Ignoring request to try to connect, already connected to `%s'!\n",
1762                   GNUNET_i2s (target));
1763       return; /* already connected */
1764     case S_DISCONNECT:
1765       /* get rid of remains, ready to re-try immediately */
1766       free_neighbour (n, GNUNET_NO);
1767       break;
1768     case S_DISCONNECT_FINISHED:
1769       /* should not be possible */
1770       GNUNET_assert (0);
1771     default:
1772       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1773                   "Unhandled state `%s'\n",
1774                   print_state (n->state));
1775       GNUNET_break (0);
1776       free_neighbour (n, GNUNET_NO);
1777       break;
1778     }
1779   }
1780   n = setup_neighbour (target);
1781   n->state = S_INIT_ATS;
1782   n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1783
1784   GNUNET_ATS_reset_backoff (GST_ats, target);
1785   n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, target);
1786 }
1787
1788
1789 /**
1790  * Function called with the result of a blacklist check.
1791  *
1792  * @param cls closure with the 'struct BlackListCheckContext'
1793  * @param peer peer this check affects
1794  * @param result GNUNET_OK if the address is allowed
1795  */
1796 static void
1797 handle_test_blacklist_cont (void *cls,
1798                             const struct GNUNET_PeerIdentity *peer,
1799                             int result)
1800 {
1801   struct BlackListCheckContext *bcc = cls;
1802   struct NeighbourMapEntry *n;
1803
1804   bcc->bc = NULL;
1805   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1806               "Connection to new address of peer `%s' based on blacklist is `%s'\n",
1807               GNUNET_i2s (peer),
1808               (GNUNET_OK == result) ? "allowed" : "FORBIDDEN");
1809   if (NULL == (n = lookup_neighbour (peer)))
1810     goto cleanup; /* nobody left to care about new address */
1811   switch (n->state)
1812   {
1813   case S_NOT_CONNECTED:
1814     /* this should not be possible */
1815     GNUNET_break (0);
1816     free_neighbour (n, GNUNET_NO);
1817     break;
1818   case S_INIT_ATS:
1819     /* still waiting on ATS suggestion */
1820     break;
1821   case S_INIT_BLACKLIST:
1822     /* check if the address the blacklist was fine with matches
1823        ATS suggestion, if so, we can move on! */
1824     if ( (GNUNET_OK == result) &&
1825          (1 == n->send_connect_ack) )
1826     {
1827       n->send_connect_ack = 2;
1828       send_session_connect_ack_message (bcc->na.address,
1829                                         bcc->na.session,
1830                                         n->connect_ack_timestamp);
1831     }
1832     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1833       break; /* result for an address we currently don't care about */
1834     if (GNUNET_OK == result)
1835     {
1836       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1837       n->state = S_CONNECT_SENT;
1838       send_session_connect (&n->primary_address);
1839     }
1840     else
1841     {
1842       // FIXME: should also possibly destroy session with plugin!?
1843       GNUNET_ATS_address_destroyed (GST_ats,
1844                                     bcc->na.address,
1845                                     NULL);
1846       free_address (&n->primary_address);
1847       n->state = S_INIT_ATS;
1848       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1849       // FIXME: do we need to ask ATS again for suggestions?
1850       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1851     }
1852     break;
1853   case S_CONNECT_SENT:
1854     /* waiting on CONNECT_ACK, send ACK if one is pending */
1855     if ( (GNUNET_OK == result) &&
1856          (1 == n->send_connect_ack) )
1857     {
1858       n->send_connect_ack = 2;
1859       send_session_connect_ack_message (n->primary_address.address,
1860                                         n->primary_address.session,
1861                                         n->connect_ack_timestamp);
1862     }
1863     break;
1864   case S_CONNECT_RECV_BLACKLIST_INBOUND:
1865     if (GNUNET_OK == result)
1866         GST_ats_add_address (bcc->na.address, bcc->na.session);
1867
1868     n->state = S_CONNECT_RECV_ATS;
1869     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1870     GNUNET_ATS_reset_backoff (GST_ats, peer);
1871     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
1872     break;
1873   case S_CONNECT_RECV_ATS:
1874     /* still waiting on ATS suggestion, don't care about blacklist */
1875     break;
1876   case S_CONNECT_RECV_BLACKLIST:
1877     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1878       break; /* result for an address we currently don't care about */
1879     if (GNUNET_OK == result)
1880     {
1881       n->timeout = GNUNET_TIME_relative_to_absolute (SETUP_CONNECTION_TIMEOUT);
1882       n->state = S_CONNECT_RECV_ACK;
1883       send_session_connect_ack_message (bcc->na.address,
1884                                         bcc->na.session,
1885                                         n->connect_ack_timestamp);
1886       if (1 == n->send_connect_ack)
1887         n->send_connect_ack = 2;
1888     }
1889     else
1890     {
1891       // FIXME: should also possibly destroy session with plugin!?
1892       GNUNET_ATS_address_destroyed (GST_ats,
1893                                     bcc->na.address,
1894                                     NULL);
1895       free_address (&n->primary_address);
1896       n->state = S_INIT_ATS;
1897       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1898       // FIXME: do we need to ask ATS again for suggestions?
1899       GNUNET_ATS_reset_backoff (GST_ats, peer);
1900       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1901     }
1902     break;
1903   case S_CONNECT_RECV_ACK:
1904     /* waiting on SESSION_ACK, send ACK if one is pending */
1905     if ( (GNUNET_OK == result) &&
1906          (1 == n->send_connect_ack) )
1907     {
1908       n->send_connect_ack = 2;
1909       send_session_connect_ack_message (n->primary_address.address,
1910                                         n->primary_address.session,
1911                                         n->connect_ack_timestamp);
1912     }
1913     break;
1914   case S_CONNECTED:
1915     /* already connected, don't care about blacklist */
1916     break;
1917   case S_RECONNECT_ATS:
1918     /* still waiting on ATS suggestion, don't care about blacklist */
1919     break;
1920   case S_RECONNECT_BLACKLIST:
1921     if ( (GNUNET_OK == result) &&
1922          (1 == n->send_connect_ack) )
1923     {
1924       n->send_connect_ack = 2;
1925       send_session_connect_ack_message (bcc->na.address,
1926                                         bcc->na.session,
1927                                         n->connect_ack_timestamp);
1928     }
1929     if (GNUNET_YES != address_matches (&bcc->na, &n->primary_address))
1930       break; /* result for an address we currently don't care about */
1931     if (GNUNET_OK == result)
1932     {
1933       send_session_connect (&n->primary_address);
1934       n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
1935       n->state = S_RECONNECT_SENT;
1936     }
1937     else
1938     {
1939       GNUNET_ATS_address_destroyed (GST_ats,
1940                                     bcc->na.address,
1941                                     NULL);
1942       n->state = S_RECONNECT_ATS;
1943       n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
1944       // FIXME: do we need to ask ATS again for suggestions?
1945       n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
1946     }
1947     break;
1948   case S_RECONNECT_SENT:
1949     /* waiting on CONNECT_ACK, don't care about blacklist */
1950     if ( (GNUNET_OK == result) &&
1951          (1 == n->send_connect_ack) )
1952     {
1953       n->send_connect_ack = 2;
1954       send_session_connect_ack_message (n->primary_address.address,
1955                                         n->primary_address.session,
1956                                         n->connect_ack_timestamp);
1957     }
1958     break;
1959   case S_CONNECTED_SWITCHING_BLACKLIST:
1960     if (GNUNET_YES != address_matches (&bcc->na, &n->alternative_address))
1961       break; /* result for an address we currently don't care about */
1962     if (GNUNET_OK == result)
1963     {
1964       send_session_connect (&n->alternative_address);
1965       n->state = S_CONNECTED_SWITCHING_CONNECT_SENT;
1966     }
1967     else
1968     {
1969       GNUNET_ATS_address_destroyed (GST_ats,
1970                                     bcc->na.address,
1971                                     NULL);
1972       free_address (&n->alternative_address);
1973       n->state = S_CONNECTED;
1974     }
1975     break;
1976   case S_CONNECTED_SWITCHING_CONNECT_SENT:
1977     /* waiting on CONNECT_ACK, don't care about blacklist */
1978     if ( (GNUNET_OK == result) &&
1979          (1 == n->send_connect_ack) )
1980     {
1981       n->send_connect_ack = 2;
1982       send_session_connect_ack_message (n->primary_address.address,
1983                                         n->primary_address.session,
1984                                         n->connect_ack_timestamp);
1985     }
1986     break;
1987   case S_DISCONNECT:
1988     /* Nothing to do here, ATS will already do what can be done */
1989     break;
1990   case S_DISCONNECT_FINISHED:
1991     /* should not be possible */
1992     GNUNET_assert (0);
1993     break;
1994   default:
1995     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1996                 "Unhandled state `%s'\n",
1997                 print_state (n->state));
1998     GNUNET_break (0);
1999     free_neighbour (n, GNUNET_NO);
2000     break;
2001   }
2002  cleanup:
2003   GNUNET_HELLO_address_free (bcc->na.address);
2004   GNUNET_CONTAINER_DLL_remove (bc_head,
2005                                bc_tail,
2006                                bcc);
2007   GNUNET_free (bcc);
2008 }
2009
2010
2011 /**
2012  * We want to know if connecting to a particular peer via
2013  * a particular address is allowed.  Check it!
2014  *
2015  * @param peer identity of the peer to switch the address for
2016  * @param ts time at which the check was initiated
2017  * @param address address of the other peer, NULL if other peer
2018  *                       connected to us
2019  * @param session session to use (or NULL)
2020  */
2021 static void
2022 check_blacklist (const struct GNUNET_PeerIdentity *peer,
2023                  struct GNUNET_TIME_Absolute ts,
2024                  const struct GNUNET_HELLO_Address *address,
2025                  struct Session *session)
2026 {
2027   struct BlackListCheckContext *bcc;
2028   struct GST_BlacklistCheck *bc;
2029
2030   bcc = GNUNET_malloc (sizeof (struct BlackListCheckContext));
2031   bcc->na.address = GNUNET_HELLO_address_copy (address);
2032   bcc->na.session = session;
2033   bcc->na.connect_timestamp = ts;
2034   GNUNET_CONTAINER_DLL_insert (bc_head,
2035                                bc_tail,
2036                                bcc);
2037   if (NULL != (bc = GST_blacklist_test_allowed (peer,
2038                                                 address->transport_name,
2039                                                 &handle_test_blacklist_cont, bcc)))
2040     bcc->bc = bc;
2041   /* if NULL == bc, 'cont' was already called and 'bcc' already free'd, so
2042      we must only store 'bc' if 'bc' is non-NULL... */
2043 }
2044
2045
2046 /**
2047  * We received a 'SESSION_CONNECT' message from the other peer.
2048  * Consider switching to it.
2049  *
2050  * @param message possibly a 'struct SessionConnectMessage' (check format)
2051  * @param peer identity of the peer to switch the address for
2052  * @param address address of the other peer, NULL if other peer
2053  *                       connected to us
2054  * @param session session to use (or NULL)
2055  */
2056 void
2057 GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
2058                                const struct GNUNET_PeerIdentity *peer,
2059                                const struct GNUNET_HELLO_Address *address,
2060                                struct Session *session)
2061 {
2062   const struct SessionConnectMessage *scm;
2063   struct NeighbourMapEntry *n;
2064   struct GNUNET_TIME_Absolute ts;
2065
2066   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2067               "Received CONNECT message from peer `%s'\n",
2068               GNUNET_i2s (peer));
2069
2070   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2071   {
2072     GNUNET_break_op (0);
2073     return;
2074   }
2075   if (NULL == neighbours)
2076     return; /* we're shutting down */
2077   scm = (const struct SessionConnectMessage *) message;
2078   GNUNET_break_op (0 == ntohl (scm->reserved));
2079   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2080   n = lookup_neighbour (peer);
2081   if (NULL == n)
2082     n = setup_neighbour (peer);
2083   n->send_connect_ack = 1;
2084   n->connect_ack_timestamp = ts;
2085
2086   switch (n->state)
2087   {
2088   case S_NOT_CONNECTED:
2089     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2090     /* Do a blacklist check for the new address */
2091     check_blacklist (peer, ts, address, session);
2092     break;
2093   case S_INIT_ATS:
2094     /* CONNECT message takes priority over us asking ATS for address */
2095     n->state = S_CONNECT_RECV_BLACKLIST_INBOUND;
2096     /* fallthrough */
2097   case S_INIT_BLACKLIST:
2098   case S_CONNECT_SENT:
2099   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2100   case S_CONNECT_RECV_ATS:
2101   case S_CONNECT_RECV_BLACKLIST:
2102   case S_CONNECT_RECV_ACK:
2103     /* It can never hurt to have an alternative address in the above cases,
2104        see if it is allowed */
2105     check_blacklist (peer, ts, address, session);
2106     break;
2107   case S_CONNECTED:
2108     /* we are already connected and can thus send the ACK immediately;
2109        still, it can never hurt to have an alternative address, so also
2110        tell ATS  about it */
2111     GNUNET_assert (NULL != n->primary_address.address);
2112     GNUNET_assert (NULL != n->primary_address.session);
2113     n->send_connect_ack = 0;
2114     send_session_connect_ack_message (n->primary_address.address,
2115                                       n->primary_address.session, ts);
2116     check_blacklist (peer, ts, address, session);
2117     break;
2118   case S_RECONNECT_ATS:
2119   case S_RECONNECT_BLACKLIST:
2120   case S_RECONNECT_SENT:
2121     /* It can never hurt to have an alternative address in the above cases,
2122        see if it is allowed */
2123     check_blacklist (peer, ts, address, session);
2124     break;
2125   case S_CONNECTED_SWITCHING_BLACKLIST:
2126   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2127     /* we are already connected and can thus send the ACK immediately;
2128        still, it can never hurt to have an alternative address, so also
2129        tell ATS  about it */
2130     GNUNET_assert (NULL != n->primary_address.address);
2131     GNUNET_assert (NULL != n->primary_address.session);
2132     n->send_connect_ack = 0;
2133     send_session_connect_ack_message (n->primary_address.address,
2134                                       n->primary_address.session, ts);
2135     check_blacklist (peer, ts, address, session);
2136     break;
2137   case S_DISCONNECT:
2138     /* get rid of remains without terminating sessions, ready to re-try */
2139     free_neighbour (n, GNUNET_YES);
2140     n = setup_neighbour (peer);
2141     n->state = S_CONNECT_RECV_ATS;
2142     GNUNET_ATS_reset_backoff (GST_ats, peer);
2143     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, peer);
2144     break;
2145   case S_DISCONNECT_FINISHED:
2146     /* should not be possible */
2147     GNUNET_assert (0);
2148     break;
2149   default:
2150     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2151                 "Unhandled state `%s'\n",
2152                 print_state (n->state));
2153     GNUNET_break (0);
2154     free_neighbour (n, GNUNET_NO);
2155     break;
2156   }
2157 }
2158
2159
2160 /**
2161  * For an existing neighbour record, set the active connection to
2162  * use the given address.
2163  *
2164  * @param peer identity of the peer to switch the address for
2165  * @param address address of the other peer, NULL if other peer
2166  *                       connected to us
2167  * @param session session to use (or NULL)
2168  * @param ats performance data
2169  * @param ats_count number of entries in ats
2170  * @param bandwidth_in inbound quota to be used when connection is up,
2171  *      0 to disconnect from peer
2172  * @param bandwidth_out outbound quota to be used when connection is up,
2173  *      0 to disconnect from peer
2174  */
2175 void
2176 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
2177                                   const struct GNUNET_HELLO_Address *address,
2178                                   struct Session *session,
2179                                   const struct GNUNET_ATS_Information *ats,
2180                                   uint32_t ats_count,
2181                                   struct GNUNET_BANDWIDTH_Value32NBO
2182                                   bandwidth_in,
2183                                   struct GNUNET_BANDWIDTH_Value32NBO
2184                                   bandwidth_out)
2185 {
2186   struct NeighbourMapEntry *n;
2187   struct GNUNET_TRANSPORT_PluginFunctions *papi;
2188
2189   GNUNET_assert (address->transport_name != NULL);
2190   if (NULL == (n = lookup_neighbour (peer)))
2191     return;
2192
2193   /* Obtain an session for this address from plugin */
2194   if (NULL == (papi = GST_plugins_find (address->transport_name)))
2195   {
2196     /* we don't have the plugin for this address */
2197     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2198     return;
2199   }
2200   if ((NULL == session) && (0 == address->address_length))
2201   {
2202     GNUNET_break (0);
2203     if (strlen (address->transport_name) > 0)
2204       GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2205     return;
2206   }
2207
2208   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2209               "ATS tells us to switch to address '%s' session %p for "
2210               "peer `%s' in state %s (quota in/out %u %u )\n",
2211               (address->address_length != 0) ? GST_plugins_a2s (address): "<inbound>",
2212               session,
2213               GNUNET_i2s (peer),
2214               print_state (n->state),
2215               ntohl (bandwidth_in.value__),
2216               ntohl (bandwidth_out.value__));
2217
2218   if (NULL == session)
2219   {
2220     session = papi->get_session (papi->cls, address);
2221     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2222                 "Obtained new session for peer `%s' and  address '%s': %p\n",
2223                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session);
2224   }
2225   if (NULL == session)
2226   {
2227     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2228                 "Failed to obtain new session for peer `%s' and  address '%s'\n",
2229                 GNUNET_i2s (&address->peer), GST_plugins_a2s (address));
2230     GNUNET_ATS_address_destroyed (GST_ats, address, NULL);
2231     return;
2232   }
2233   switch (n->state)
2234   {
2235   case S_NOT_CONNECTED:
2236     GNUNET_break (0);
2237     free_neighbour (n, GNUNET_NO);
2238     return;
2239   case S_INIT_ATS:
2240     set_address (&n->primary_address,
2241                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2242     n->state = S_INIT_BLACKLIST;
2243     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2244     check_blacklist (&n->id,
2245                      n->connect_ack_timestamp,
2246                      address, session);
2247     break;
2248   case S_INIT_BLACKLIST:
2249     /* ATS suggests a different address, switch again */
2250     set_address (&n->primary_address,
2251                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2252     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2253     check_blacklist (&n->id,
2254                      n->connect_ack_timestamp,
2255                      address, session);
2256     break;
2257   case S_CONNECT_SENT:
2258     /* ATS suggests a different address, switch again */
2259     set_address (&n->primary_address,
2260                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2261     n->state = S_INIT_BLACKLIST;
2262     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2263     check_blacklist (&n->id,
2264                      n->connect_ack_timestamp,
2265                      address, session);
2266     break;
2267   case S_CONNECT_RECV_ATS:
2268     set_address (&n->primary_address,
2269                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2270     n->state = S_CONNECT_RECV_BLACKLIST;
2271     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2272     check_blacklist (&n->id,
2273                      n->connect_ack_timestamp,
2274                      address, session);
2275     break;
2276   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2277     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2278     check_blacklist (&n->id,
2279                      n->connect_ack_timestamp,
2280                      address, session);
2281     break;
2282   case S_CONNECT_RECV_BLACKLIST:
2283   case S_CONNECT_RECV_ACK:
2284     /* ATS asks us to switch while we were trying to connect; switch to new
2285        address and check blacklist again */
2286     set_address (&n->primary_address,
2287                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2288     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2289     check_blacklist (&n->id,
2290                      n->connect_ack_timestamp,
2291                      address, session);
2292     break;
2293   case S_CONNECTED:
2294     GNUNET_assert (NULL != n->primary_address.address);
2295     GNUNET_assert (NULL != n->primary_address.session);
2296     if (n->primary_address.session == session)
2297     {
2298       /* not an address change, just a quota change */
2299       set_address (&n->primary_address,
2300                    address, session, bandwidth_in, bandwidth_out, GNUNET_YES);
2301       break;
2302     }
2303     /* ATS asks us to switch a life connection; see if we can get
2304        a CONNECT_ACK on it before we actually do this! */
2305     set_address (&n->alternative_address,
2306                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2307     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2308     check_blacklist (&n->id,
2309                      GNUNET_TIME_absolute_get (),
2310                      address, session);
2311     break;
2312   case S_RECONNECT_ATS:
2313     set_address (&n->primary_address,
2314                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2315     n->state = S_RECONNECT_BLACKLIST;
2316     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2317     check_blacklist (&n->id,
2318                      n->connect_ack_timestamp,
2319                      address, session);
2320     break;
2321   case S_RECONNECT_BLACKLIST:
2322     /* ATS asks us to switch while we were trying to reconnect; switch to new
2323        address and check blacklist again */
2324     set_address (&n->primary_address,
2325                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2326     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2327     check_blacklist (&n->id,
2328                      n->connect_ack_timestamp,
2329                      address, session);
2330     break;
2331   case S_RECONNECT_SENT:
2332     /* ATS asks us to switch while we were trying to reconnect; switch to new
2333        address and check blacklist again */
2334     set_address (&n->primary_address,
2335                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2336     n->state = S_RECONNECT_BLACKLIST;
2337     n->timeout = GNUNET_TIME_relative_to_absolute (BLACKLIST_RESPONSE_TIMEOUT);
2338     check_blacklist (&n->id,
2339                      n->connect_ack_timestamp,
2340                      address, session);
2341     break;
2342   case S_CONNECTED_SWITCHING_BLACKLIST:
2343     if (n->primary_address.session == session)
2344     {
2345       /* ATS switches back to still-active session */
2346       free_address (&n->alternative_address);
2347       n->state = S_CONNECTED;
2348       break;
2349     }
2350     /* ATS asks us to switch a life connection, update blacklist check */
2351     set_address (&n->alternative_address,
2352                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2353     check_blacklist (&n->id,
2354                      GNUNET_TIME_absolute_get (),
2355                      address, session);
2356     break;
2357   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2358     if (n->primary_address.session == session)
2359     {
2360       /* ATS switches back to still-active session */
2361       free_address (&n->alternative_address);
2362       n->state = S_CONNECTED;
2363       break;
2364     }
2365     /* ATS asks us to switch a life connection, update blacklist check */
2366     set_address (&n->alternative_address,
2367                  address, session, bandwidth_in, bandwidth_out, GNUNET_NO);
2368     n->state = S_CONNECTED_SWITCHING_BLACKLIST;
2369     check_blacklist (&n->id,
2370                      GNUNET_TIME_absolute_get (),
2371                      address, session);
2372     break;
2373   case S_DISCONNECT:
2374     /* not going to switch addresses while disconnecting */
2375     return;
2376   case S_DISCONNECT_FINISHED:
2377     GNUNET_assert (0);
2378     break;
2379   default:
2380     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2381                 "Unhandled state `%s'\n",
2382                 print_state (n->state));
2383     GNUNET_break (0);
2384     break;
2385   }
2386 }
2387
2388
2389 static int
2390 send_utilization_data (void *cls,
2391                        const struct GNUNET_PeerIdentity *key,
2392                        void *value)
2393 {
2394   struct NeighbourMapEntry *n = value;
2395   struct GNUNET_ATS_Information atsi[4];
2396   uint32_t bps_pl_in;
2397   uint32_t bps_pl_out;
2398   uint32_t bps_in;
2399   uint32_t bps_out;
2400   struct GNUNET_TIME_Relative delta;
2401
2402   delta = GNUNET_TIME_absolute_get_difference (n->last_util_transmission,
2403                                                GNUNET_TIME_absolute_get ());
2404
2405   bps_pl_in = 0;
2406
2407   if ((0 != n->util_payload_bytes_recv) && (0 != delta.rel_value_us))
2408     bps_pl_in =  (1000LL * 1000LL *  n->util_payload_bytes_recv) / (delta.rel_value_us);
2409   bps_pl_out = 0;
2410   if ((0 != n->util_payload_bytes_sent) && (0 != delta.rel_value_us))
2411     bps_pl_out = (1000LL * 1000LL * n->util_payload_bytes_sent) / delta.rel_value_us;
2412   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2413               "`%s' payload: received %u Bytes/s, sent %u Bytes/s\n",
2414               GNUNET_i2s (key),
2415               bps_pl_in,
2416               bps_pl_out);
2417   bps_in = 0;
2418   if ((0 != n->util_total_bytes_recv) && (0 != delta.rel_value_us))
2419     bps_in =  (1000LL * 1000LL *  n->util_total_bytes_recv) / (delta.rel_value_us);
2420   bps_out = 0;
2421   if ((0 != n->util_total_bytes_sent) && (0 != delta.rel_value_us))
2422     bps_out = (1000LL * 1000LL * n->util_total_bytes_sent) / delta.rel_value_us;
2423
2424
2425   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2426               "`%s' total: received %u Bytes/s, sent %u Bytes/s\n",
2427               GNUNET_i2s (key),
2428               bps_in,
2429               bps_out);
2430   atsi[0].type = htonl (GNUNET_ATS_UTILIZATION_OUT);
2431   atsi[0].value = htonl (bps_out);
2432   atsi[1].type = htonl (GNUNET_ATS_UTILIZATION_IN);
2433   atsi[1].value = htonl (bps_in);
2434
2435   atsi[2].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_OUT);
2436   atsi[2].value = htonl (bps_pl_out);
2437   atsi[3].type = htonl (GNUNET_ATS_UTILIZATION_PAYLOAD_IN);
2438   atsi[3].value = htonl (bps_pl_in);
2439
2440   GST_ats_update_metrics (key, n->primary_address.address,
2441       n->primary_address.session, atsi, 4);
2442   n->util_payload_bytes_recv = 0;
2443   n->util_payload_bytes_sent = 0;
2444   n->util_total_bytes_recv = 0;
2445   n->util_total_bytes_sent = 0;
2446   n->last_util_transmission = GNUNET_TIME_absolute_get();
2447   return GNUNET_OK;
2448 }
2449
2450
2451 /**
2452  * Task transmitting utilization in a regular interval
2453  *
2454  * @param cls the 'struct NeighbourMapEntry' for which we are running
2455  * @param tc scheduler context (unused)
2456  */
2457 static void
2458 utilization_transmission (void *cls,
2459                           const struct GNUNET_SCHEDULER_TaskContext *tc)
2460 {
2461   util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
2462
2463   if (0 < GNUNET_CONTAINER_multipeermap_size (neighbours))
2464     GNUNET_CONTAINER_multipeermap_iterate (neighbours, send_utilization_data, NULL);
2465
2466   util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
2467       utilization_transmission, NULL);
2468
2469 }
2470
2471 void
2472 GST_neighbours_notify_data_recv (const struct GNUNET_PeerIdentity *peer,
2473                  const struct GNUNET_HELLO_Address *address,
2474                  struct Session *session,
2475                  const struct GNUNET_MessageHeader *message)
2476 {
2477   struct NeighbourMapEntry *n;
2478   n = lookup_neighbour (peer);
2479   if (NULL == n)
2480   {
2481       return;
2482   }
2483   n->util_total_bytes_recv += ntohs(message->size);
2484 }
2485
2486 void
2487 GST_neighbours_notify_payload_recv (const struct GNUNET_PeerIdentity *peer,
2488                  const struct GNUNET_HELLO_Address *address,
2489                  struct Session *session,
2490                  const struct GNUNET_MessageHeader *message)
2491 {
2492   struct NeighbourMapEntry *n;
2493   n = lookup_neighbour (peer);
2494   if (NULL == n)
2495   {
2496       return;
2497   }
2498   n->util_payload_bytes_recv += ntohs(message->size);
2499 }
2500
2501
2502 void
2503 GST_neighbours_notify_data_sent (const struct GNUNET_PeerIdentity *peer,
2504                      const struct GNUNET_HELLO_Address *address,
2505                      struct Session *session,
2506                      size_t size)
2507 {
2508   struct NeighbourMapEntry *n;
2509   n = lookup_neighbour (peer);
2510   if (NULL == n)
2511       return;
2512   if (n->primary_address.session != session)
2513     return;
2514   n->util_total_bytes_sent += size;
2515 }
2516
2517 void
2518 GST_neighbours_notify_payload_sent (const struct GNUNET_PeerIdentity *peer,
2519     size_t size)
2520 {
2521   struct NeighbourMapEntry *n;
2522   n = lookup_neighbour (peer);
2523   if (NULL == n)
2524   {
2525       return;
2526   }
2527   n->util_payload_bytes_sent += size;
2528 }
2529
2530
2531 /**
2532  * Master task run for every neighbour.  Performs all of the time-related
2533  * activities (keep alive, send next message, disconnect if idle, finish
2534  * clean up after disconnect).
2535  *
2536  * @param cls the 'struct NeighbourMapEntry' for which we are running
2537  * @param tc scheduler context (unused)
2538  */
2539 static void
2540 master_task (void *cls,
2541              const struct GNUNET_SCHEDULER_TaskContext *tc)
2542 {
2543   struct NeighbourMapEntry *n = cls;
2544   struct GNUNET_TIME_Relative delay;
2545
2546   n->task = GNUNET_SCHEDULER_NO_TASK;
2547   delay = GNUNET_TIME_absolute_get_remaining (n->timeout);
2548   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2549               "Master task runs for neighbour `%s' in state %s with timeout in %s\n",
2550               GNUNET_i2s (&n->id),
2551               print_state(n->state),
2552               GNUNET_STRINGS_relative_time_to_string (delay,
2553                                                       GNUNET_YES));
2554   switch (n->state)
2555   {
2556   case S_NOT_CONNECTED:
2557     /* invalid state for master task, clean up */
2558     GNUNET_break (0);
2559     n->state = S_DISCONNECT_FINISHED;
2560     free_neighbour (n, GNUNET_NO);
2561     return;
2562   case S_INIT_ATS:
2563     if (0 == delay.rel_value_us)
2564     {
2565       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2566                   "Connection to `%s' timed out waiting for ATS to provide address\n",
2567                   GNUNET_i2s (&n->id));
2568       n->state = S_DISCONNECT_FINISHED;
2569       free_neighbour (n, GNUNET_NO);
2570       return;
2571     }
2572     break;
2573   case S_INIT_BLACKLIST:
2574     if (0 == delay.rel_value_us)
2575     {
2576       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2577                   "Connection to `%s' timed out waiting for BLACKLIST to approve address\n",
2578                   GNUNET_i2s (&n->id));
2579       n->state = S_DISCONNECT_FINISHED;
2580       free_neighbour (n, GNUNET_NO);
2581       return;
2582     }
2583     break;
2584   case S_CONNECT_SENT:
2585     if (0 == delay.rel_value_us)
2586     {
2587       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2588                   "Connection to `%s' timed out waiting for other peer to send CONNECT_ACK\n",
2589                   GNUNET_i2s (&n->id));
2590       disconnect_neighbour (n);
2591       return;
2592     }
2593     break;
2594   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2595     if (0 == delay.rel_value_us)
2596     {
2597       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2598                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for received CONNECT\n",
2599                   GNUNET_i2s (&n->id));
2600       n->state = S_DISCONNECT_FINISHED;
2601       free_neighbour (n, GNUNET_NO);
2602       return;
2603     }
2604     break;
2605   case S_CONNECT_RECV_ATS:
2606     if (0 == delay.rel_value_us)
2607     {
2608       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2609                   "Connection to `%s' timed out waiting ATS to provide address to use for CONNECT_ACK\n",
2610                   GNUNET_i2s (&n->id));
2611       n->state = S_DISCONNECT_FINISHED;
2612       free_neighbour (n, GNUNET_NO);
2613       return;
2614     }
2615     break;
2616   case S_CONNECT_RECV_BLACKLIST:
2617     if (0 == delay.rel_value_us)
2618     {
2619       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2620                   "Connection to `%s' timed out waiting BLACKLIST to approve address to use for CONNECT_ACK\n",
2621                   GNUNET_i2s (&n->id));
2622       n->state = S_DISCONNECT_FINISHED;
2623       free_neighbour (n, GNUNET_NO);
2624       return;
2625     }
2626     break;
2627   case S_CONNECT_RECV_ACK:
2628     if (0 == delay.rel_value_us)
2629     {
2630       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2631                   "Connection to `%s' timed out waiting for other peer to send SESSION_ACK\n",
2632                   GNUNET_i2s (&n->id));
2633       disconnect_neighbour (n);
2634       return;
2635     }
2636     break;
2637   case S_CONNECTED:
2638     if (0 == delay.rel_value_us)
2639     {
2640       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2641                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2642                   GNUNET_i2s (&n->id));
2643       disconnect_neighbour (n);
2644       return;
2645     }
2646     try_transmission_to_peer (n);
2647     send_keepalive (n);
2648     break;
2649   case S_RECONNECT_ATS:
2650     if (0 == delay.rel_value_us)
2651     {
2652       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2653                   "Connection to `%s' timed out, waiting for ATS replacement address\n",
2654                   GNUNET_i2s (&n->id));
2655       disconnect_neighbour (n);
2656       return;
2657     }
2658     break;
2659   case S_RECONNECT_BLACKLIST:
2660     if (0 == delay.rel_value_us)
2661     {
2662       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2663                   "Connection to `%s' timed out, waiting for BLACKLIST to approve replacement address\n",
2664                   GNUNET_i2s (&n->id));
2665       disconnect_neighbour (n);
2666       return;
2667     }
2668     break;
2669   case S_RECONNECT_SENT:
2670     if (0 == delay.rel_value_us)
2671     {
2672       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2673                   "Connection to `%s' timed out, waiting for other peer to CONNECT_ACK replacement address\n",
2674                   GNUNET_i2s (&n->id));
2675       disconnect_neighbour (n);
2676       return;
2677     }
2678     break;
2679   case S_CONNECTED_SWITCHING_BLACKLIST:
2680     if (0 == delay.rel_value_us)
2681     {
2682       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2683                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs\n",
2684                   GNUNET_i2s (&n->id));
2685       disconnect_neighbour (n);
2686       return;
2687     }
2688     try_transmission_to_peer (n);
2689     send_keepalive (n);
2690     break;
2691   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2692     if (0 == delay.rel_value_us)
2693     {
2694       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2695                   "Connection to `%s' timed out, missing KEEPALIVE_RESPONSEs (after trying to CONNECT on alternative address)\n",
2696                   GNUNET_i2s (&n->id));
2697       disconnect_neighbour (n);
2698       return;
2699     }
2700     try_transmission_to_peer (n);
2701     send_keepalive (n);
2702     break;
2703   case S_DISCONNECT:
2704     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2705                 "Cleaning up connection to `%s' after sending DISCONNECT\n",
2706                 GNUNET_i2s (&n->id));
2707     free_neighbour (n, GNUNET_NO);
2708     return;
2709   case S_DISCONNECT_FINISHED:
2710     /* how did we get here!? */
2711     GNUNET_assert (0);
2712     break;
2713   default:
2714     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2715                 "Unhandled state `%s'\n",
2716                 print_state (n->state));
2717     GNUNET_break (0);
2718     break;
2719   }
2720   if ( (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) ||
2721        (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2722        (S_CONNECTED == n->state) )
2723   {
2724     /* if we are *now* in one of these three states, we're sending
2725        keep alive messages, so we need to consider the keepalive
2726        delay, not just the connection timeout */
2727     delay = GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining (n->keep_alive_time),
2728                                       delay);
2729   }
2730   if (GNUNET_SCHEDULER_NO_TASK == n->task)
2731     n->task = GNUNET_SCHEDULER_add_delayed (delay,
2732                                             &master_task,
2733                                             n);
2734 }
2735
2736
2737 /**
2738  * Send a SESSION_ACK message to the neighbour to confirm that we
2739  * got his CONNECT_ACK.
2740  *
2741  * @param n neighbour to send the SESSION_ACK to
2742  */
2743 static void
2744 send_session_ack_message (struct NeighbourMapEntry *n)
2745 {
2746   struct GNUNET_MessageHeader msg;
2747
2748   msg.size = htons (sizeof (struct GNUNET_MessageHeader));
2749   msg.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK);
2750   (void) send_with_session(n,
2751                            (const char *) &msg, sizeof (struct GNUNET_MessageHeader),
2752                            UINT32_MAX, GNUNET_TIME_UNIT_FOREVER_REL,
2753                            NULL, NULL);
2754 }
2755
2756
2757 /**
2758  * We received a 'SESSION_CONNECT_ACK' message from the other peer.
2759  * Consider switching to it.
2760  *
2761  * @param message possibly a 'struct SessionConnectMessage' (check format)
2762  * @param peer identity of the peer to switch the address for
2763  * @param address address of the other peer, NULL if other peer
2764  *                       connected to us
2765  * @param session session to use (or NULL)
2766  */
2767 void
2768 GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
2769                                    const struct GNUNET_PeerIdentity *peer,
2770                                    const struct GNUNET_HELLO_Address *address,
2771                                    struct Session *session)
2772 {
2773   const struct SessionConnectMessage *scm;
2774   struct GNUNET_TIME_Absolute ts;
2775   struct NeighbourMapEntry *n;
2776
2777   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2778               "Received CONNECT_ACK message from peer `%s'\n",
2779               GNUNET_i2s (peer));
2780
2781   if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
2782   {
2783     GNUNET_break_op (0);
2784     return;
2785   }
2786   scm = (const struct SessionConnectMessage *) message;
2787   GNUNET_break_op (ntohl (scm->reserved) == 0);
2788   if (NULL == (n = lookup_neighbour (peer)))
2789   {
2790     GNUNET_STATISTICS_update (GST_stats,
2791                               gettext_noop
2792                               ("# unexpected CONNECT_ACK messages (no peer)"),
2793                               1, GNUNET_NO);
2794     return;
2795   }
2796   ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
2797   switch (n->state)
2798   {
2799   case S_NOT_CONNECTED:
2800     GNUNET_break (0);
2801     free_neighbour (n, GNUNET_NO);
2802     return;
2803   case S_INIT_ATS:
2804   case S_INIT_BLACKLIST:
2805     GNUNET_STATISTICS_update (GST_stats,
2806                               gettext_noop
2807                               ("# unexpected CONNECT_ACK messages (not ready)"),
2808                               1, GNUNET_NO);
2809     break;
2810   case S_CONNECT_SENT:
2811     if (ts.abs_value_us != n->primary_address.connect_timestamp.abs_value_us)
2812       break; /* ACK does not match our original CONNECT message */
2813     n->state = S_CONNECTED;
2814     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2815     GNUNET_STATISTICS_set (GST_stats,
2816                            gettext_noop ("# peers connected"),
2817                            ++neighbours_connected,
2818                            GNUNET_NO);
2819     connect_notify_cb (callback_cls, &n->id,
2820                        n->primary_address.bandwidth_in,
2821                        n->primary_address.bandwidth_out);
2822     /* Tell ATS that the outbound session we created to send CONNECT was successfull */
2823     GST_ats_add_address (n->primary_address.address, n->primary_address.session);
2824     set_address (&n->primary_address,
2825                  n->primary_address.address,
2826                  n->primary_address.session,
2827                  n->primary_address.bandwidth_in,
2828                  n->primary_address.bandwidth_out,
2829                  GNUNET_YES);
2830     send_session_ack_message (n);
2831     break;
2832   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2833   case S_CONNECT_RECV_ATS:
2834   case S_CONNECT_RECV_BLACKLIST:
2835   case S_CONNECT_RECV_ACK:
2836     GNUNET_STATISTICS_update (GST_stats,
2837                               gettext_noop
2838                               ("# unexpected CONNECT_ACK messages (not ready)"),
2839                               1, GNUNET_NO);
2840     break;
2841   case S_CONNECTED:
2842     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2843     send_session_ack_message (n);
2844     break;
2845   case S_RECONNECT_ATS:
2846   case S_RECONNECT_BLACKLIST:
2847     /* we didn't expect any CONNECT_ACK, as we are waiting for ATS
2848        to give us a new address... */
2849     GNUNET_STATISTICS_update (GST_stats,
2850                               gettext_noop
2851                               ("# unexpected CONNECT_ACK messages (waiting on ATS)"),
2852                               1, GNUNET_NO);
2853     break;
2854   case S_RECONNECT_SENT:
2855     /* new address worked; go back to connected! */
2856     n->state = S_CONNECTED;
2857     send_session_ack_message (n);
2858     break;
2859   case S_CONNECTED_SWITCHING_BLACKLIST:
2860     /* duplicate CONNECT_ACK, let's answer by duplciate SESSION_ACK just in case */
2861     send_session_ack_message (n);
2862     break;
2863   case S_CONNECTED_SWITCHING_CONNECT_SENT:
2864     /* new address worked; adopt it and go back to connected! */
2865     n->state = S_CONNECTED;
2866     n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
2867     GNUNET_break (GNUNET_NO == n->alternative_address.ats_active);
2868
2869     GST_ats_add_address (n->alternative_address.address, n->alternative_address.session);
2870     set_address (&n->primary_address,
2871                  n->alternative_address.address,
2872                  n->alternative_address.session,
2873                  n->alternative_address.bandwidth_in,
2874                  n->alternative_address.bandwidth_out,
2875                  GNUNET_YES);
2876     free_address (&n->alternative_address);
2877     send_session_ack_message (n);
2878     break;
2879   case S_DISCONNECT:
2880     GNUNET_STATISTICS_update (GST_stats,
2881                               gettext_noop
2882                               ("# unexpected CONNECT_ACK messages (disconnecting)"),
2883                               1, GNUNET_NO);
2884     break;
2885   case S_DISCONNECT_FINISHED:
2886     GNUNET_assert (0);
2887     break;
2888   default:
2889     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2890                 "Unhandled state `%s'\n",
2891                 print_state (n->state));
2892     GNUNET_break (0);
2893     break;
2894   }
2895 }
2896
2897
2898 /**
2899  * A session was terminated. Take note; if needed, try to get
2900  * an alternative address from ATS.
2901  *
2902  * @param peer identity of the peer where the session died
2903  * @param session session that is gone
2904  * @return GNUNET_YES if this was a session used, GNUNET_NO if
2905  *        this session was not in use
2906  */
2907 int
2908 GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
2909                                    struct Session *session)
2910 {
2911   struct NeighbourMapEntry *n;
2912   struct BlackListCheckContext *bcc;
2913   struct BlackListCheckContext *bcc_next;
2914
2915   /* make sure to cancel all ongoing blacklist checks involving 'session' */
2916   bcc_next = bc_head;
2917   while (NULL != (bcc = bcc_next))
2918   {
2919     bcc_next = bcc->next;
2920     if (bcc->na.session == session)
2921     {
2922       GST_blacklist_test_cancel (bcc->bc);
2923       GNUNET_HELLO_address_free (bcc->na.address);
2924       GNUNET_CONTAINER_DLL_remove (bc_head,
2925                                    bc_tail,
2926                                    bcc);
2927       GNUNET_free (bcc);
2928     }
2929   }
2930   if (NULL == (n = lookup_neighbour (peer)))
2931     return GNUNET_NO; /* can't affect us */
2932   if (session != n->primary_address.session)
2933   {
2934     if (session == n->alternative_address.session)
2935     {
2936       free_address (&n->alternative_address);
2937       if ( (S_CONNECTED_SWITCHING_BLACKLIST == n->state) ||
2938            (S_CONNECTED_SWITCHING_CONNECT_SENT == n->state) )
2939         n->state = S_CONNECTED;
2940       else
2941         GNUNET_break (0);
2942     }
2943     return GNUNET_NO; /* doesn't affect us further */
2944   }
2945
2946   n->expect_latency_response = GNUNET_NO;
2947   switch (n->state)
2948   {
2949   case S_NOT_CONNECTED:
2950     GNUNET_break (0);
2951     free_neighbour (n, GNUNET_NO);
2952     return GNUNET_YES;
2953   case S_INIT_ATS:
2954     GNUNET_break (0);
2955     free_neighbour (n, GNUNET_NO);
2956     return GNUNET_YES;
2957   case S_INIT_BLACKLIST:
2958   case S_CONNECT_SENT:
2959     free_address (&n->primary_address);
2960     n->state = S_INIT_ATS;
2961     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2962     // FIXME: need to ask ATS for suggestions again?
2963     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2964     break;
2965   case S_CONNECT_RECV_BLACKLIST_INBOUND:
2966   case S_CONNECT_RECV_ATS:
2967   case S_CONNECT_RECV_BLACKLIST:
2968   case S_CONNECT_RECV_ACK:
2969     /* error on inbound session; free neighbour entirely */
2970     free_address (&n->primary_address);
2971     free_neighbour (n, GNUNET_NO);
2972     return GNUNET_YES;
2973   case S_CONNECTED:
2974     free_address (&n->primary_address);
2975     n->state = S_RECONNECT_ATS;
2976     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2977     /* FIXME: is this ATS call needed? */
2978     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2979     break;
2980   case S_RECONNECT_ATS:
2981     /* we don't have an address, how can it go down? */
2982     GNUNET_break (0);
2983     break;
2984   case S_RECONNECT_BLACKLIST:
2985   case S_RECONNECT_SENT:
2986     n->state = S_RECONNECT_ATS;
2987     n->timeout = GNUNET_TIME_relative_to_absolute (ATS_RESPONSE_TIMEOUT);
2988     // FIXME: need to ask ATS for suggestions again?
2989     n->suggest_handle = GNUNET_ATS_suggest_address (GST_ats, &n->id);
2990     break;
2991   case S_CONNECTED_SWITCHING_BLACKLIST:
2992     /* primary went down while we were checking secondary against
2993        blacklist, adopt secondary as primary */
2994     free_address (&n->primary_address);
2995     n->primary_address = n->alternative_address;
2996     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
2997     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
2998     n->state = S_RECONNECT_BLACKLIST;
2999     break;
3000   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3001     /* primary went down while we were waiting for CONNECT_ACK on secondary;
3002        secondary as primary */
3003     free_address (&n->primary_address);
3004     n->primary_address = n->alternative_address;
3005     memset (&n->alternative_address, 0, sizeof (struct NeighbourAddress));
3006     n->timeout = GNUNET_TIME_relative_to_absolute (FAST_RECONNECT_TIMEOUT);
3007     n->state = S_RECONNECT_SENT;
3008     break;
3009   case S_DISCONNECT:
3010     free_address (&n->primary_address);
3011     break;
3012   case S_DISCONNECT_FINISHED:
3013     /* neighbour was freed and plugins told to terminate session */
3014     return GNUNET_NO;
3015     break;
3016   default:
3017     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3018                 "Unhandled state `%s'\n",
3019                 print_state (n->state));
3020     GNUNET_break (0);
3021     break;
3022   }
3023   if (GNUNET_SCHEDULER_NO_TASK != n->task)
3024     GNUNET_SCHEDULER_cancel (n->task);
3025   n->task = GNUNET_SCHEDULER_add_now (&master_task, n);
3026   return GNUNET_YES;
3027 }
3028
3029
3030 /**
3031  * We received a 'SESSION_ACK' message from the other peer.
3032  * If we sent a 'CONNECT_ACK' last, this means we are now
3033  * connected.  Otherwise, do nothing.
3034  *
3035  * @param message possibly a 'struct SessionConnectMessage' (check format)
3036  * @param peer identity of the peer to switch the address for
3037  * @param address address of the other peer, NULL if other peer
3038  *                       connected to us
3039  * @param session session to use (or NULL)
3040  */
3041 void
3042 GST_neighbours_handle_session_ack (const struct GNUNET_MessageHeader *message,
3043                                    const struct GNUNET_PeerIdentity *peer,
3044                                    const struct GNUNET_HELLO_Address *address,
3045                                    struct Session *session)
3046 {
3047   struct NeighbourMapEntry *n;
3048
3049   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3050               "Received SESSION_ACK message from peer `%s'\n",
3051               GNUNET_i2s (peer));
3052   if (ntohs (message->size) != sizeof (struct GNUNET_MessageHeader))
3053   {
3054     GNUNET_break_op (0);
3055     return;
3056   }
3057   if (NULL == (n = lookup_neighbour (peer)))
3058     return;
3059   /* check if we are in a plausible state for having sent
3060      a CONNECT_ACK.  If not, return, otherwise break */
3061   if ( ( (S_CONNECT_RECV_ACK != n->state) &&
3062          (S_CONNECT_SENT != n->state) ) ||
3063        (2 != n->send_connect_ack) )
3064   {
3065     GNUNET_STATISTICS_update (GST_stats,
3066                               gettext_noop ("# unexpected SESSION ACK messages"), 1,
3067                               GNUNET_NO);
3068     return;
3069   }
3070   n->state = S_CONNECTED;
3071   n->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
3072   GNUNET_STATISTICS_set (GST_stats,
3073                          gettext_noop ("# peers connected"),
3074                          ++neighbours_connected,
3075                          GNUNET_NO);
3076   connect_notify_cb (callback_cls, &n->id,
3077                      n->primary_address.bandwidth_in,
3078                      n->primary_address.bandwidth_out);
3079
3080   GST_ats_add_address (n->primary_address.address, n->primary_address.session);
3081   set_address (&n->primary_address,
3082                n->primary_address.address,
3083                n->primary_address.session,
3084                n->primary_address.bandwidth_in,
3085                n->primary_address.bandwidth_out,
3086                GNUNET_YES);
3087 }
3088
3089
3090 /**
3091  * Test if we're connected to the given peer.
3092  *
3093  * @param target peer to test
3094  * @return #GNUNET_YES if we are connected, #GNUNET_NO if not
3095  */
3096 int
3097 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
3098 {
3099   return test_connected (lookup_neighbour (target));
3100 }
3101
3102
3103 /**
3104  * Change the incoming quota for the given peer.
3105  *
3106  * @param neighbour identity of peer to change qutoa for
3107  * @param quota new quota
3108  */
3109 void
3110 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
3111                                    struct GNUNET_BANDWIDTH_Value32NBO quota)
3112 {
3113   struct NeighbourMapEntry *n;
3114
3115   if (NULL == (n = lookup_neighbour (neighbour)))
3116   {
3117     GNUNET_STATISTICS_update (GST_stats,
3118                               gettext_noop
3119                               ("# SET QUOTA messages ignored (no such peer)"),
3120                               1, GNUNET_NO);
3121     return;
3122   }
3123   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3124               "Setting inbound quota of %u Bps for peer `%s' to all clients\n",
3125               ntohl (quota.value__), GNUNET_i2s (&n->id));
3126   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
3127   if (0 != ntohl (quota.value__))
3128     return;
3129   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
3130               GNUNET_i2s (&n->id), "SET_QUOTA");
3131   if (GNUNET_YES == test_connected (n))
3132     GNUNET_STATISTICS_update (GST_stats,
3133                               gettext_noop ("# disconnects due to quota of 0"),
3134                               1, GNUNET_NO);
3135   disconnect_neighbour (n);
3136 }
3137
3138
3139 /**
3140  * We received a disconnect message from the given peer,
3141  * validate and process.
3142  *
3143  * @param peer sender of the message
3144  * @param msg the disconnect message
3145  */
3146 void
3147 GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity
3148                                           *peer,
3149                                           const struct GNUNET_MessageHeader
3150                                           *msg)
3151 {
3152   struct NeighbourMapEntry *n;
3153   const struct SessionDisconnectMessage *sdm;
3154
3155   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3156               "Received DISCONNECT message from peer `%s'\n",
3157               GNUNET_i2s (peer));
3158   if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
3159   {
3160     // GNUNET_break_op (0);
3161     GNUNET_STATISTICS_update (GST_stats,
3162                               gettext_noop
3163                               ("# disconnect messages ignored (old format)"), 1,
3164                               GNUNET_NO);
3165     return;
3166   }
3167   sdm = (const struct SessionDisconnectMessage *) msg;
3168   if (NULL == (n = lookup_neighbour (peer)))
3169     return;                     /* gone already */
3170   if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value_us <= n->connect_ack_timestamp.abs_value_us)
3171   {
3172     GNUNET_STATISTICS_update (GST_stats,
3173                               gettext_noop
3174                               ("# disconnect messages ignored (timestamp)"), 1,
3175                               GNUNET_NO);
3176     return;
3177   }
3178   if (0 != memcmp (peer, &sdm->public_key, sizeof (struct GNUNET_PeerIdentity)))
3179   {
3180     GNUNET_break_op (0);
3181     return;
3182   }
3183   if (ntohl (sdm->purpose.size) !=
3184       sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
3185       sizeof (struct GNUNET_CRYPTO_EddsaPublicKey) +
3186       sizeof (struct GNUNET_TIME_AbsoluteNBO))
3187   {
3188     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3189                 "%s message from peer `%s' has invalid size \n",
3190                 "DISCONNECT",
3191                 GNUNET_i2s (peer));
3192     GNUNET_break_op (0);
3193     return;
3194   }
3195   if (GNUNET_OK !=
3196       GNUNET_CRYPTO_eddsa_verify
3197       (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT, &sdm->purpose,
3198        &sdm->signature, &sdm->public_key))
3199   {
3200     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3201                 "%s message from peer `%s' cannot be verified \n",
3202                 "DISCONNECT",
3203                 GNUNET_i2s (peer));
3204     GNUNET_break_op (0);
3205     return;
3206   }
3207   if (GNUNET_YES == test_connected (n))
3208     GNUNET_STATISTICS_update (GST_stats,
3209                               gettext_noop
3210                               ("# other peer asked to disconnect from us"), 1,
3211                               GNUNET_NO);
3212   disconnect_neighbour (n);
3213 }
3214
3215
3216 /**
3217  * Closure for the neighbours_iterate function.
3218  */
3219 struct IteratorContext
3220 {
3221   /**
3222    * Function to call on each connected neighbour.
3223    */
3224   GST_NeighbourIterator cb;
3225
3226   /**
3227    * Closure for 'cb'.
3228    */
3229   void *cb_cls;
3230 };
3231
3232
3233 /**
3234  * Call the callback from the closure for each connected neighbour.
3235  *
3236  * @param cls the `struct IteratorContext`
3237  * @param key the hash of the public key of the neighbour
3238  * @param value the `struct NeighbourMapEntry`
3239  * @return #GNUNET_OK (continue to iterate)
3240  */
3241 static int
3242 neighbours_iterate (void *cls,
3243                     const struct GNUNET_PeerIdentity *key,
3244                     void *value)
3245 {
3246   struct IteratorContext *ic = cls;
3247   struct NeighbourMapEntry *n = value;
3248   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in;
3249   struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out;
3250
3251   if (GNUNET_YES != test_connected (n))
3252     return GNUNET_OK;
3253
3254   if (NULL != n->primary_address.address)
3255   {
3256     bandwidth_in = n->primary_address.bandwidth_in;
3257     bandwidth_out = n->primary_address.bandwidth_out;
3258   }
3259   else
3260   {
3261     bandwidth_in = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3262     bandwidth_out = GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT;
3263   }
3264   ic->cb (ic->cb_cls, &n->id,
3265           n->primary_address.address,
3266           bandwidth_in, bandwidth_out);
3267   return GNUNET_OK;
3268 }
3269
3270
3271 /**
3272  * Iterate over all connected neighbours.
3273  *
3274  * @param cb function to call
3275  * @param cb_cls closure for cb
3276  */
3277 void
3278 GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
3279 {
3280   struct IteratorContext ic;
3281
3282   if (NULL == neighbours)
3283     return; /* can happen during shutdown */
3284   ic.cb = cb;
3285   ic.cb_cls = cb_cls;
3286   GNUNET_CONTAINER_multipeermap_iterate (neighbours, &neighbours_iterate, &ic);
3287 }
3288
3289
3290 /**
3291  * If we have an active connection to the given target, it must be shutdown.
3292  *
3293  * @param target peer to disconnect from
3294  */
3295 void
3296 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
3297 {
3298   struct NeighbourMapEntry *n;
3299
3300   if (NULL == (n = lookup_neighbour (target)))
3301     return;  /* not active */
3302   if (GNUNET_YES == test_connected (n))
3303     GNUNET_STATISTICS_update (GST_stats,
3304                               gettext_noop
3305                               ("# disconnected from peer upon explicit request"), 1,
3306                               GNUNET_NO);
3307   disconnect_neighbour (n);
3308 }
3309
3310
3311 /**
3312  * Obtain current latency information for the given neighbour.
3313  *
3314  * @param peer to get the latency for
3315  * @return observed latency of the address, FOREVER if the
3316  *         the connection is not up
3317  */
3318 struct GNUNET_TIME_Relative
3319 GST_neighbour_get_latency (const struct GNUNET_PeerIdentity *peer)
3320 {
3321   struct NeighbourMapEntry *n;
3322
3323   n = lookup_neighbour (peer);
3324   if (NULL == n)
3325     return GNUNET_TIME_UNIT_FOREVER_REL;
3326   switch (n->state)
3327   {
3328   case S_CONNECTED:
3329   case S_CONNECTED_SWITCHING_CONNECT_SENT:
3330   case S_CONNECTED_SWITCHING_BLACKLIST:
3331   case S_RECONNECT_SENT:
3332   case S_RECONNECT_ATS:
3333   case S_RECONNECT_BLACKLIST:
3334     return n->latency;
3335   case S_NOT_CONNECTED:
3336   case S_INIT_BLACKLIST:
3337   case S_INIT_ATS:
3338   case S_CONNECT_RECV_BLACKLIST_INBOUND:
3339   case S_CONNECT_RECV_ATS:
3340   case S_CONNECT_RECV_BLACKLIST:
3341   case S_CONNECT_RECV_ACK:
3342   case S_CONNECT_SENT:
3343   case S_DISCONNECT:
3344   case S_DISCONNECT_FINISHED:
3345     return GNUNET_TIME_UNIT_FOREVER_REL;
3346   default:
3347     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3348                 "Unhandled state `%s'\n",
3349                 print_state (n->state));
3350     GNUNET_break (0);
3351     break;
3352   }
3353   return GNUNET_TIME_UNIT_FOREVER_REL;
3354 }
3355
3356
3357 /**
3358  * Obtain current address information for the given neighbour.
3359  *
3360  * @param peer
3361  * @return address currently used
3362  */
3363 struct GNUNET_HELLO_Address *
3364 GST_neighbour_get_current_address (const struct GNUNET_PeerIdentity *peer)
3365 {
3366   struct NeighbourMapEntry *n;
3367
3368   n = lookup_neighbour (peer);
3369   if (NULL == n)
3370     return NULL;
3371   return n->primary_address.address;
3372 }
3373
3374
3375 /**
3376  * Initialize the neighbours subsystem.
3377  *
3378  * @param cls closure for callbacks
3379  * @param connect_cb function to call if we connect to a peer
3380  * @param disconnect_cb function to call if we disconnect from a peer
3381  * @param peer_address_cb function to call if we change an active address
3382  *                   of a neighbour
3383  * @param max_fds maximum number of fds to use
3384  */
3385 void
3386 GST_neighbours_start (void *cls,
3387                       NotifyConnect connect_cb,
3388                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb,
3389                       GNUNET_TRANSPORT_PeerIterateCallback peer_address_cb,
3390                       unsigned int max_fds)
3391 {
3392   callback_cls = cls;
3393   connect_notify_cb = connect_cb;
3394   disconnect_notify_cb = disconnect_cb;
3395   address_change_cb = peer_address_cb;
3396   neighbours = GNUNET_CONTAINER_multipeermap_create (NEIGHBOUR_TABLE_SIZE, GNUNET_NO);
3397   util_transmission_tk = GNUNET_SCHEDULER_add_delayed (UTIL_TRANSMISSION_INTERVAL,
3398       utilization_transmission, NULL);
3399 }
3400
3401
3402 /**
3403  * Disconnect from the given neighbour.
3404  *
3405  * @param cls unused
3406  * @param key hash of neighbour's public key (not used)
3407  * @param value the 'struct NeighbourMapEntry' of the neighbour
3408  * @return #GNUNET_OK (continue to iterate)
3409  */
3410 static int
3411 disconnect_all_neighbours (void *cls,
3412                            const struct GNUNET_PeerIdentity *key,
3413                            void *value)
3414 {
3415   struct NeighbourMapEntry *n = value;
3416
3417   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3418               "Disconnecting peer `%4s', %s\n",
3419               GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
3420   n->state = S_DISCONNECT_FINISHED;
3421   free_neighbour (n, GNUNET_NO);
3422   return GNUNET_OK;
3423 }
3424
3425
3426 /**
3427  * Cleanup the neighbours subsystem.
3428  */
3429 void
3430 GST_neighbours_stop ()
3431 {
3432   if (NULL == neighbours)
3433     return;
3434   if (GNUNET_SCHEDULER_NO_TASK != util_transmission_tk)
3435   {
3436     GNUNET_SCHEDULER_cancel (util_transmission_tk);
3437     util_transmission_tk = GNUNET_SCHEDULER_NO_TASK;
3438   }
3439
3440   GNUNET_CONTAINER_multipeermap_iterate (neighbours,
3441                                          &disconnect_all_neighbours,
3442                                          NULL);
3443   GNUNET_CONTAINER_multipeermap_destroy (neighbours);
3444   neighbours = NULL;
3445   callback_cls = NULL;
3446   connect_notify_cb = NULL;
3447   disconnect_notify_cb = NULL;
3448   address_change_cb = NULL;
3449 }
3450
3451
3452 /* end of file gnunet-service-transport_neighbours.c */