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