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