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