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