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