switch api
[oweals/gnunet.git] / src / transport / gnunet-service-transport_neighbours.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010,2011 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/gnunet-service-transport_neighbours.c
23  * @brief neighbour management
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_ats_service.h"
28 #include "gnunet-service-transport_neighbours.h"
29 #include "gnunet-service-transport_validation.h"
30 #include "gnunet-service-transport.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_constants.h"
33 #include "transport.h"
34
35
36 /**
37  * Size of the neighbour hash map.
38  */
39 #define NEIGHBOUR_TABLE_SIZE 256
40
41
42
43 // TODO:
44 // - have a way to access the currently 'connected' session
45 //   (for sending and to notice disconnect of it!)
46 // - have a way to access/update bandwidth/quota information per peer
47 //   (for CostReport/TrafficReport callbacks)
48
49
50 struct NeighbourMapEntry;
51
52 /**
53  * For each neighbour we keep a list of messages
54  * that we still want to transmit to the neighbour.
55  */
56 struct MessageQueue
57 {
58
59   /**
60    * This is a doubly linked list.
61    */
62   struct MessageQueue *next;
63
64   /**
65    * This is a doubly linked list.
66    */
67   struct MessageQueue *prev;
68
69   /**
70    * The message(s) we want to transmit, GNUNET_MessageHeader(s)
71    * stuck together in memory.  Allocated at the end of this struct.
72    */
73   const char *message_buf;
74
75   /**
76    * Size of the message buf
77    */
78   size_t message_buf_size;
79
80   /**
81    * Client responsible for queueing the message; used to check that a
82    * client has no two messages pending for the same target and to
83    * notify the client of a successful transmission; NULL if this is
84    * an internal message.
85    */
86   struct TransportClient *client;
87
88   /**
89    * At what time should we fail?
90    */
91   struct GNUNET_TIME_Absolute timeout;
92
93   /**
94    * Internal message of the transport system that should not be
95    * included in the usual SEND-SEND_OK transmission confirmation
96    * traffic management scheme.  Typically, "internal_msg" will
97    * be set whenever "client" is NULL (but it is not strictly
98    * required).
99    */
100   int internal_msg;
101
102   /**
103    * How important is the message?
104    */
105   unsigned int priority;
106
107 };
108
109
110 /**
111  * Entry in neighbours. 
112  */
113 struct NeighbourMapEntry
114 {
115
116   /**
117    * Head of list of messages we would like to send to this peer;
118    * must contain at most one message per client.
119    */
120   struct MessageQueue *messages_head;
121
122   /**
123    * Tail of list of messages we would like to send to this peer; must
124    * contain at most one message per client.
125    */
126   struct MessageQueue *messages_tail;
127
128   /**
129    * Context for address suggestion.
130    * NULL after we are connected.
131    */
132   struct GNUNET_ATS_SuggestionContext *asc;
133
134   /**
135    * Performance data for the peer.
136    */
137   struct GNUNET_TRANSPORT_ATS_Information *ats;
138
139   /**
140    * Active session for communicating with the peer.
141    */
142   struct Session *session;
143
144   /**
145    * Name of the plugin we currently use.
146    */
147   char *plugin_name;
148
149   /**
150    * Address used for communicating with the peer, NULL for inbound connections.
151    */
152   void *addr;
153   
154   /**
155    * Number of bytes in 'addr'.
156    */
157   size_t addrlen;
158
159   /**
160    * Public key for this peer.  Valid only if the respective flag is set below.
161    */
162   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
163
164   /**
165    * Identity of this neighbour.
166    */
167   struct GNUNET_PeerIdentity id;
168
169   /**
170    * ID of task scheduled to run when this peer is about to
171    * time out (will free resources associated with the peer).
172    */
173   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
174
175   /**
176    * ID of task scheduled to run when we should retry transmitting
177    * the head of the message queue.  Actually triggered when the
178    * transmission is timing out (we trigger instantly when we have
179    * a chance of success).
180    */
181   GNUNET_SCHEDULER_TaskIdentifier retry_task;
182
183   /**
184    * How long until we should consider this peer dead (if we don't
185    * receive another message in the meantime)?
186    */
187   struct GNUNET_TIME_Absolute peer_timeout;
188
189   /**
190    * Tracker for inbound bandwidth.
191    */
192   struct GNUNET_BANDWIDTH_Tracker in_tracker;
193
194   /**
195    * How often has the other peer (recently) violated the inbound
196    * traffic limit?  Incremented by 10 per violation, decremented by 1
197    * per non-violation (for each time interval).
198    */
199   unsigned int quota_violation_count;
200
201   /**
202    * Number of values in 'ats' array.
203    */
204   unsigned int ats_count;
205
206   /**
207    * Have we seen an PONG from this neighbour in the past (and
208    * not had a disconnect since)?
209    */
210   int received_pong;
211
212   /**
213    * Do we have a valid public key for this neighbour?
214    */
215   int public_key_valid;
216
217   /**
218    * Are we already in the process of disconnecting this neighbour?
219    */
220   int in_disconnect;
221
222   /**
223    * Do we currently consider this neighbour connected? (as far as
224    * the connect/disconnect callbacks are concerned)?
225    */
226   int is_connected;
227
228 };
229
230
231 /**
232  * All known neighbours and their HELLOs.
233  */
234 static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
235
236 /**
237  * Closure for connect_notify_cb and disconnect_notify_cb
238  */
239 static void *callback_cls;
240
241 /**
242  * Function to call when we connected to a neighbour.
243  */
244 static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
245
246 /**
247  * Function to call when we disconnected from a neighbour.
248  */
249 static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
250
251
252 /**
253  * Lookup a neighbour entry in the neighbours hash map.
254  *
255  * @param pid identity of the peer to look up
256  * @return the entry, NULL if there is no existing record
257  */
258 static struct NeighbourMapEntry *
259 lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
260 {
261   return GNUNET_CONTAINER_multihashmap_get (neighbours,
262                                             &pid->hashPubKey);
263 }
264
265
266 #if 0
267 /**
268  * Check the ready list for the given neighbour and if a plugin is
269  * ready for transmission (and if we have a message), do so!
270  *
271  * @param neighbour target peer for which to transmit
272  */
273 static void
274 try_transmission_to_peer (struct NeighbourMapEntry *n)
275 {
276   struct MessageQueue *mq;
277   struct GNUNET_TIME_Relative timeout;
278   ssize_t ret;
279
280   if (n->messages_head == NULL)
281     {
282 #if DEBUG_TRANSPORT
283       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
284                   "Transmission queue for `%4s' is empty\n",
285                   GNUNET_i2s (&n->id));
286 #endif
287       return;                     /* nothing to do */
288     }
289   mq = n->messages_head;
290   GNUNET_CONTAINER_DLL_remove (n->messages_head,
291                                n->messages_tail,
292                                mq);
293   ret = papi->send (papi->cls,
294                     &n->pid,
295                     mq->message_buf,
296                     mq->message_buf_size,
297                     mq->priority,
298                     GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
299                     n->session,
300                     n->addr,
301                     n->addrlen,
302                     GNUNET_YES /*?*/,
303                     &transmit_send_continuation, mq);
304   if (ret == -1)
305     {
306       /* failure, but 'send' would not call continuation in this case,
307          so we need to do it here! */
308       transmit_send_continuation (mq,
309                                   &mq->neighbour_id,
310                                   GNUNET_SYSERR);
311     }
312 }
313 #endif
314
315
316 /**
317  * Initialize the neighbours subsystem.
318  *
319  * @param cls closure for callbacks
320  * @param connect_cb function to call if we connect to a peer
321  * @param disconnect_cb function to call if we disconnect from a peer
322  */
323 void 
324 GST_neighbours_start (void *cls,
325                       GNUNET_TRANSPORT_NotifyConnect connect_cb,
326                       GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
327 {
328   callback_cls = cls;
329   connect_notify_cb = connect_cb;
330   disconnect_notify_cb = disconnect_cb;
331   neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
332 }
333
334
335 /**
336  * Disconnect from the given neighbour, clean up the record.
337  *
338  * @param n neighbour to disconnect from
339  */
340 static void
341 disconnect_neighbour (struct NeighbourMapEntry *n)
342 {
343   struct MessageQueue *mq;
344
345   if (n->is_connected)
346     {
347       disconnect_notify_cb (callback_cls,
348                             &n->id);
349       n->is_connected = GNUNET_NO;
350     }
351   GNUNET_assert (GNUNET_YES ==
352                  GNUNET_CONTAINER_multihashmap_remove (neighbours,
353                                                        &n->id.hashPubKey,
354                                                        n));
355   while (NULL != (mq = n->messages_head))
356     {
357       GNUNET_CONTAINER_DLL_remove (n->messages_head,
358                                    n->messages_tail,
359                                    mq);
360       GNUNET_free (mq);
361     }
362   if (NULL != n->asc)
363     {
364       GNUNET_ATS_suggest_address_cancel (n->asc);
365       n->asc = NULL;
366     }
367   GNUNET_array_grow (n->ats,
368                      n->ats_count,
369                      0);
370   if (NULL != n->plugin_name)
371     {
372       GNUNET_free (n->plugin_name);
373       n->plugin_name = NULL;
374     }
375   if (NULL != n->addr)
376     {
377       GNUNET_free (n->addr);
378       n->addr = NULL;
379       n->addrlen = 0;
380     }
381   n->session = NULL;
382   GNUNET_free (n);
383 }
384
385
386 /**
387  * Disconnect from the given neighbour.
388  *
389  * @param cls unused
390  * @param key hash of neighbour's public key (not used)
391  * @param value the 'struct NeighbourMapEntry' of the neighbour
392  */
393 static int
394 disconnect_all_neighbours (void *cls,
395                            const GNUNET_HashCode *key,
396                            void *value)
397 {
398   struct NeighbourMapEntry *n = value;
399
400 #if DEBUG_TRANSPORT
401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
402               "Disconnecting peer `%4s', %s\n",
403               GNUNET_i2s(&n->id),
404               "SHUTDOWN_TASK");
405 #endif
406   disconnect_neighbour (n);
407   return GNUNET_OK;
408 }
409
410
411 /**
412  * Cleanup the neighbours subsystem.
413  */
414 void
415 GST_neighbours_stop ()
416 {
417   GNUNET_CONTAINER_multihashmap_iterate (neighbours,
418                                          &disconnect_all_neighbours,
419                                          NULL);
420   GNUNET_CONTAINER_multihashmap_destroy (neighbours);
421   neighbours = NULL;
422   callback_cls = NULL;
423   connect_notify_cb = NULL;
424   disconnect_notify_cb = NULL;
425 }
426
427
428 /**
429  * For an existing neighbour record, set the active connection to
430  * the given address.
431  *
432  * @param plugin_name name of transport that delivered the PONG
433  * @param address address of the other peer, NULL if other peer
434  *                       connected to us
435  * @param address_len number of bytes in address
436  * @param ats performance data
437  * @param ats_count number of entries in ats (excluding 0-termination)
438  */
439 void
440 GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
441                                   const char *plugin_name,
442                                   const void *address,
443                                   size_t address_len,
444                                   struct Session *session,
445                                   const struct GNUNET_TRANSPORT_ATS_Information *ats,
446                                   uint32_t ats_count)
447 {
448   struct NeighbourMapEntry *n;
449
450   n = lookup_neighbour (peer);
451   if (NULL == n)
452     {
453       GNUNET_break (0);
454       return;
455     }
456   GNUNET_free_non_null (n->addr);
457   n->addr = GNUNET_malloc (address_len);
458   memcpy (n->addr, address, address_len);
459   n->addrlen = address_len;
460   n->session = session;
461   GNUNET_array_grow (n->ats,
462                      n->ats_count,
463                      ats_count);
464   memcpy (n->ats,
465           ats,
466           ats_count * sizeof (struct GNUNET_TRANSPORT_ATS_Information));
467   GNUNET_free_non_null (n->plugin_name);
468   n->plugin_name = GNUNET_strdup (plugin_name);
469 }
470
471
472 /**
473  * Try to connect to the target peer using the given address
474  * (if is valid).
475  *
476  * @param cls the 'struct NeighbourMapEntry' of the target
477  * @param public_key public key for the peer, never NULL
478  * @param target identity of the target peer
479  * @param plugin_name name of the plugin
480  * @param plugin_address binary address
481  * @param plugin_address_len length of address
482  * @param bandwidth available bandwidth
483  * @param ats performance data for the address (as far as known)
484  * @param ats_count number of performance records in 'ats'
485  */
486 static void
487 try_connect_using_address (void *cls,
488                            const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *public_key,
489                            const struct GNUNET_PeerIdentity *target,
490                            const char *plugin_name,
491                            const void *plugin_address,
492                            size_t plugin_address_len,
493                            struct GNUNET_BANDWIDTH_Value32NBO bandwidth,
494                            const struct GNUNET_TRANSPORT_ATS_Information *ats,
495                            uint32_t ats_count)
496 {
497   struct NeighbourMapEntry *n = cls;
498
499   n->asc = NULL;
500   if (n->public_key_valid == GNUNET_NO)
501     {
502       n->public_key = *public_key;
503       n->public_key_valid = GNUNET_YES;
504     }
505   GST_neighbours_switch_to_address (target,
506                                     plugin_name,
507                                     plugin_address,
508                                     plugin_address_len,
509                                     NULL,
510                                     ats, ats_count);
511   if (GNUNET_YES == n->is_connected)
512     return;
513   n->is_connected = GNUNET_YES;  
514   connect_notify_cb (callback_cls,
515                      target,
516                      n->ats,
517                      n->ats_count);
518 }
519
520
521 /**
522  * We've tried to connect but waited long enough and failed.  Clean up.
523  *
524  * @param cls the 'struct NeighbourMapEntry' of the neighbour that failed to connect
525  * @param tc scheduler context
526  */
527 static void
528 neighbour_connect_timeout_task (void *cls,
529                                 const struct GNUNET_SCHEDULER_TaskContext *tc)
530 {
531   struct NeighbourMapEntry *n = cls;
532
533   n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
534   GNUNET_assert (GNUNET_YES ==
535                  GNUNET_CONTAINER_multihashmap_remove (neighbours,
536                                                        &n->id.hashPubKey,
537                                                        n));
538   GNUNET_assert (NULL == n->messages_head);
539   GNUNET_assert (NULL == n->ats);
540   GNUNET_free (n);
541 }
542
543
544 /**
545  * Try to create a connection to the given target (eventually).
546  *
547  * @param target peer to try to connect to
548  */
549 void
550 GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
551 {
552   struct NeighbourMapEntry *n;
553
554   GNUNET_assert (0 != memcmp (target,
555                               &GST_my_identity,
556                               sizeof (struct GNUNET_PeerIdentity)));
557   n = lookup_neighbour (target);
558   if ( (NULL != n) ||
559        (GNUNET_YES == n->is_connected) )
560     return; /* already connected */
561   if (n == NULL)
562     {
563       n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
564       n->id = *target;
565       GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
566                                      GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
567                                      MAX_BANDWIDTH_CARRY_S);
568       n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
569                                                       &neighbour_connect_timeout_task, n);
570       GNUNET_assert (GNUNET_OK ==
571                      GNUNET_CONTAINER_multihashmap_put (neighbours,
572                                                         &n->id.hashPubKey,
573                                                         n,
574                                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
575     }
576   if (n->asc != NULL)
577     return; /* already trying */
578   n->asc = GNUNET_ATS_suggest_address (GST_ats,
579                                        target,
580                                        &try_connect_using_address,
581                                        n); 
582 }
583
584
585 /**
586  * Test if we're connected to the given peer.
587  * 
588  * @param target peer to test
589  * @return GNUNET_YES if we are connected, GNUNET_NO if not
590  */
591 int
592 GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
593 {
594   struct NeighbourMapEntry *n;
595
596   n = lookup_neighbour (target);
597   if ( (NULL == n) ||
598        (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) )
599        return GNUNET_NO; /* not connected */
600   return GNUNET_YES;
601 }
602
603
604 /**
605  * Transmit a message to the given target using the active connection.
606  *
607  * @param target destination
608  * @param msg message to send
609  * @param msg_size number of bytes in msg
610  * @param timeout when to fail with timeout
611  * @param cont function to call when done
612  * @param cont_cls closure for 'cont'
613  */
614 void
615 GST_neighbours_send (const struct GNUNET_PeerIdentity *target,
616                      const void *msg,
617                      size_t msg_size,
618                      struct GNUNET_TIME_Relative timeout,
619                      GST_NeighbourSendContinuation cont,
620                      void *cont_cls)
621 {
622   struct NeighbourMapEntry *n;
623   struct MessageQueue *mq;
624
625   n = lookup_neighbour (target);
626   if ( (n == NULL) ||
627        (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0) ) 
628     {
629       GNUNET_STATISTICS_update (GST_stats,
630                                 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
631                                 1,
632                                 GNUNET_NO);
633       if (NULL != cont)
634         cont (cont_cls,
635               GNUNET_SYSERR);
636       return;
637     }
638   GNUNET_assert (msg_size >= sizeof (struct GNUNET_MessageHeader));
639   GNUNET_STATISTICS_update (GST_stats,
640                             gettext_noop ("# bytes in message queue for other peers"),
641                             msg_size,
642                             GNUNET_NO);
643   mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
644   /* FIXME: this memcpy can be up to 7% of our total runtime! */
645   memcpy (&mq[1], msg, msg_size);
646   mq->message_buf = (const char*) &mq[1];
647   mq->message_buf_size = msg_size;
648   mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
649   GNUNET_CONTAINER_DLL_insert_tail (n->messages_head,
650                                     n->messages_tail,
651                                     mq);
652   // try_transmission_to_peer (n);
653 }
654
655
656 /**
657  * Change the incoming quota for the given peer.
658  *
659  * @param neighbour identity of peer to change qutoa for
660  * @param quota new quota 
661  */
662 void
663 GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
664                                    struct GNUNET_BANDWIDTH_Value32NBO quota)
665 {
666   struct NeighbourMapEntry *n;
667
668   n = lookup_neighbour (neighbour);
669   if (n == NULL)
670     {
671       GNUNET_STATISTICS_update (GST_stats,
672                                 gettext_noop ("# SET QUOTA messages ignored (no such peer)"),
673                                 1,
674                                 GNUNET_NO);
675       return;
676     }
677   GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker,
678                                          quota);
679   if (0 != ntohl (quota.value__))
680     return;
681 #if DEBUG_TRANSPORT
682   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
683               "Disconnecting peer `%4s' due to `%s'\n",
684               GNUNET_i2s(&n->id),
685               "SET_QUOTA");
686 #endif
687   GNUNET_STATISTICS_update (GST_stats,
688                             gettext_noop ("# disconnects due to quota of 0"),
689                             1,
690                             GNUNET_NO);
691   disconnect_neighbour (n);
692 }
693
694
695 /**
696  * Closure for the neighbours_iterate function.
697  */
698 struct IteratorContext
699 {
700   /**
701    * Function to call on each connected neighbour.
702    */
703   GST_NeighbourIterator cb;
704
705   /**
706    * Closure for 'cb'.
707    */
708   void *cb_cls;
709 };
710
711
712 /**
713  * Call the callback from the closure for each connected neighbour.
714  *
715  * @param cls the 'struct IteratorContext'
716  * @param key the hash of the public key of the neighbour
717  * @param value the 'struct NeighbourMapEntry'
718  * @return GNUNET_OK (continue to iterate)
719  */
720 static int
721 neighbours_iterate (void *cls,
722                     const GNUNET_HashCode *key,
723                     void *value)
724 {
725   struct IteratorContext *ic = cls;
726   struct NeighbourMapEntry *n = value;
727
728   if (GNUNET_TIME_absolute_get_remaining (n->peer_timeout).rel_value == 0)
729     return GNUNET_OK; /* not connected */
730   GNUNET_assert (n->ats_count > 0);
731   ic->cb (ic->cb_cls,
732           &n->id,
733           n->ats,
734           n->ats_count - 1);
735   return GNUNET_OK;
736 }
737
738
739 /**
740  * Iterate over all connected neighbours.
741  *
742  * @param cb function to call 
743  * @param cb_cls closure for cb
744  */
745 void
746 GST_neighbours_iterate (GST_NeighbourIterator cb,
747                         void *cb_cls)
748 {
749   struct IteratorContext ic;
750
751   ic.cb = cb;
752   ic.cb_cls = cb_cls;
753   GNUNET_CONTAINER_multihashmap_iterate (neighbours,
754                                          &neighbours_iterate,
755                                          &ic);
756 }
757
758
759 /**
760  * Peer has been idle for too long. Disconnect.
761  *
762  * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
763  * @param tc scheduler context
764  */
765 static void
766 neighbour_idle_timeout_task (void *cls,
767                              const struct GNUNET_SCHEDULER_TaskContext *tc)
768 {
769   struct NeighbourMapEntry *n = cls;
770
771   n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
772   disconnect_neighbour (n);
773 }
774
775
776 /**
777  * We have received a CONNECT.  Set the peer to connected.
778  *
779  * @param sender peer sending the PONG
780  * @param hdr the PONG message (presumably)
781  * @param plugin_name name of transport that delivered the PONG
782  * @param sender_address address of the other peer, NULL if other peer
783  *                       connected to us
784  * @param sender_address_len number of bytes in sender_address
785  * @param bandwidth bandwidth for the connection
786  * @param ats performance data
787  * @param ats_count number of entries in ats (excluding 0-termination)
788  * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
789  */
790 int
791 GST_neighbours_handle_connect (const struct GNUNET_PeerIdentity *sender,
792                                const struct GNUNET_MessageHeader *hdr,
793                                const char *plugin_name,
794                                const void *sender_address,
795                                size_t sender_address_len,
796                                struct Session *session,
797                                const struct GNUNET_TRANSPORT_ATS_Information *ats,
798                                uint32_t ats_count)
799 {  
800   struct NeighbourMapEntry *n;
801
802   if (0 == memcmp (sender,
803                    &GST_my_identity,
804                    sizeof (struct GNUNET_PeerIdentity)))
805     {
806       GNUNET_break (0);
807       return GNUNET_SYSERR;
808     } 
809   n = lookup_neighbour (sender);
810   if ( (NULL != n) &&
811        (n->is_connected == GNUNET_YES) )
812     return GNUNET_OK; /* ats will consider switching and tell us */
813   if (n == NULL)
814     {
815       n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
816       n->id = *sender;
817       GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
818                                      GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
819                                      MAX_BANDWIDTH_CARRY_S);
820       n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
821                                                       &neighbour_connect_timeout_task, n);
822       GNUNET_assert (GNUNET_OK ==
823                      GNUNET_CONTAINER_multihashmap_put (neighbours,
824                                                         &n->id.hashPubKey,
825                                                         n,
826                                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
827     }
828   GST_neighbours_switch_to_address (sender,
829                                     plugin_name,
830                                     sender_address,
831                                     sender_address_len,
832                                     session,
833                                     ats, ats_count);
834   if (NULL != session)
835     {
836       /* inbound bi-directional connection, just use it */
837       n->is_connected = GNUNET_YES;  
838       connect_notify_cb (callback_cls,
839                          sender,
840                          n->ats,
841                          n->ats_count);
842       n->peer_timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
843       if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
844         GNUNET_SCHEDULER_cancel (n->timeout_task);
845       n->timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
846                                                       &neighbour_idle_timeout_task,
847                                                       n);     
848     }
849   else
850     {
851       /* try to establish our connection back to the initiator */
852       GST_neighbours_try_connect (sender);
853     }
854   return GNUNET_OK;
855 }
856
857
858 /**
859  * If we have an active connection to the given target, it must be shutdown.
860  *
861  * @param target peer to disconnect from
862  */
863 void
864 GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
865 {
866   struct NeighbourMapEntry *n;
867
868   n = lookup_neighbour (target);
869   /* FIXME: send disconnect message to target... */
870   disconnect_neighbour (n);
871 }
872
873
874 /**
875  * We have received a DISCONNECT.  Set the peer to disconnected.
876  *
877  * @param sender peer sending the PONG
878  * @param hdr the PONG message (presumably)
879  * @param plugin_name name of transport that delivered the PONG
880  * @param sender_address address of the other peer, NULL if other peer
881  *                       connected to us
882  * @param sender_address_len number of bytes in sender_address
883  * @return GNUNET_OK if the message was well-formed, GNUNET_SYSERR if not
884  */
885 int
886 GST_neighbours_handle_disconnect (const struct GNUNET_PeerIdentity *sender,
887                                   const struct GNUNET_MessageHeader *hdr,
888                                   const char *plugin_name,
889                                   const void *sender_address,
890                                   size_t sender_address_len)
891 {
892   struct NeighbourMapEntry *n;
893
894   n = lookup_neighbour (sender);
895   /* FIXME: should disconnects have a signature that we should check here? */
896   disconnect_neighbour (n);
897   return GNUNET_OK;
898 }
899
900
901 /* end of file gnunet-service-transport_neighbours.c */