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