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