aboutsummaryrefslogtreecommitdiff
path: root/src/transport
diff options
context:
space:
mode:
authorMatthias Wachs <wachs@net.in.tum.de>2011-10-25 16:36:01 +0000
committerMatthias Wachs <wachs@net.in.tum.de>2011-10-25 16:36:01 +0000
commitb9c87498868c88745df077108be224bf3771a6e3 (patch)
treebee397fa9cca4a167f108aedc0fd696d54639ddf /src/transport
parent0307ab82093f876665e4ac77fffa0a52e0d6d89c (diff)
downloadgnunet-b9c87498868c88745df077108be224bf3771a6e3.tar.gz
gnunet-b9c87498868c88745df077108be224bf3771a6e3.zip
work in progress
- neighbour management based on a FSM - 3-way connect handshake
Diffstat (limited to 'src/transport')
-rw-r--r--src/transport/gnunet-service-transport_neighbours_fsm.c1588
1 files changed, 1588 insertions, 0 deletions
diff --git a/src/transport/gnunet-service-transport_neighbours_fsm.c b/src/transport/gnunet-service-transport_neighbours_fsm.c
new file mode 100644
index 000000000..f9794967a
--- /dev/null
+++ b/src/transport/gnunet-service-transport_neighbours_fsm.c
@@ -0,0 +1,1588 @@
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_plugins.h"
30#include "gnunet-service-transport_validation.h"
31#include "gnunet-service-transport_clients.h"
32#include "gnunet-service-transport.h"
33#include "gnunet_peerinfo_service.h"
34#include "gnunet_constants.h"
35#include "transport.h"
36
37
38/**
39 * Size of the neighbour hash map.
40 */
41#define NEIGHBOUR_TABLE_SIZE 256
42
43/**
44 * How often must a peer violate bandwidth quotas before we start
45 * to simply drop its messages?
46 */
47#define QUOTA_VIOLATION_DROP_THRESHOLD 10
48
49/**
50 * How often do we send KEEPALIVE messages to each of our neighbours?
51 * (idle timeout is 5 minutes or 300 seconds, so with 90s interval we
52 * send 3 keepalives in each interval, so 3 messages would need to be
53 * lost in a row for a disconnect).
54 */
55#define KEEPALIVE_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 90)
56
57
58/**
59 * Entry in neighbours.
60 */
61struct NeighbourMapEntry;
62
63/**
64 * Message a peer sends to another to indicate its
65 * preference for communicating via a particular
66 * session (and the desire to establish a real
67 * connection).
68 */
69struct SessionConnectMessage
70{
71 /**
72 * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT'
73 */
74 struct GNUNET_MessageHeader header;
75
76 /**
77 * Always zero.
78 */
79 uint32_t reserved GNUNET_PACKED;
80
81 /**
82 * Absolute time at the sender. Only the most recent connect
83 * message implies which session is preferred by the sender.
84 */
85 struct GNUNET_TIME_AbsoluteNBO timestamp;
86
87};
88
89
90struct SessionDisconnectMessage
91{
92 /**
93 * Header of type 'GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT'
94 */
95 struct GNUNET_MessageHeader header;
96
97 /**
98 * Always zero.
99 */
100 uint32_t reserved GNUNET_PACKED;
101
102 /**
103 * Purpose of the signature. Extends over the timestamp.
104 * Purpose should be GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DISCONNECT.
105 */
106 struct GNUNET_CRYPTO_RsaSignaturePurpose purpose;
107
108 /**
109 * Absolute time at the sender. Only the most recent connect
110 * message implies which session is preferred by the sender.
111 */
112 struct GNUNET_TIME_AbsoluteNBO timestamp;
113
114 /**
115 * Public key of the sender.
116 */
117 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key;
118
119 /**
120 * Signature of the peer that sends us the disconnect. Only
121 * valid if the timestamp is AFTER the timestamp from the
122 * corresponding 'CONNECT' message.
123 */
124 struct GNUNET_CRYPTO_RsaSignature signature;
125
126};
127
128
129/**
130 * For each neighbour we keep a list of messages
131 * that we still want to transmit to the neighbour.
132 */
133struct MessageQueue
134{
135
136 /**
137 * This is a doubly linked list.
138 */
139 struct MessageQueue *next;
140
141 /**
142 * This is a doubly linked list.
143 */
144 struct MessageQueue *prev;
145
146 /**
147 * Once this message is actively being transmitted, which
148 * neighbour is it associated with?
149 */
150 struct NeighbourMapEntry *n;
151
152 /**
153 * Function to call once we're done.
154 */
155 GST_NeighbourSendContinuation cont;
156
157 /**
158 * Closure for 'cont'
159 */
160 void *cont_cls;
161
162 /**
163 * The message(s) we want to transmit, GNUNET_MessageHeader(s)
164 * stuck together in memory. Allocated at the end of this struct.
165 */
166 const char *message_buf;
167
168 /**
169 * Size of the message buf
170 */
171 size_t message_buf_size;
172
173 /**
174 * At what time should we fail?
175 */
176 struct GNUNET_TIME_Absolute timeout;
177
178};
179
180enum State
181{
182 /* fresh peer or completely disconnected */
183 S_NOT_CONNECTED = 0,
184 /* sent CONNECT message to other peer, waiting for CONNECT_ACK */
185 S_CONNECT_SENT = 1,
186 /* received CONNECT message to other peer, sending CONNECT_ACK */
187 S_CONNECT_RECV = 4,
188 /* sent CONNECT_ACK message to other peer, wait for ACK or payload */
189 S_CONNECT_RECV_ACK_SENT = 8,
190 /* received ACK or payload */
191 S_CONNECTED = 16,
192 S_DISCONNECTED = 32
193};
194
195/**
196 * Entry in neighbours.
197 */
198struct NeighbourMapEntry
199{
200
201 /**
202 * Head of list of messages we would like to send to this peer;
203 * must contain at most one message per client.
204 */
205 struct MessageQueue *messages_head;
206
207 /**
208 * Tail of list of messages we would like to send to this peer; must
209 * contain at most one message per client.
210 */
211 struct MessageQueue *messages_tail;
212
213 /**
214 * Performance data for the peer.
215 */
216 //struct GNUNET_ATS_Information *ats;
217
218 /**
219 * Are we currently trying to send a message? If so, which one?
220 */
221 struct MessageQueue *is_active;
222
223 /**
224 * Active session for communicating with the peer.
225 */
226 struct Session *session;
227
228 /**
229 * Name of the plugin we currently use.
230 */
231 char *plugin_name;
232
233 /**
234 * Address used for communicating with the peer, NULL for inbound connections.
235 */
236 void *addr;
237
238 /**
239 * Number of bytes in 'addr'.
240 */
241 size_t addrlen;
242
243 /**
244 * Identity of this neighbour.
245 */
246 struct GNUNET_PeerIdentity id;
247
248 /**
249 * ID of task scheduled to run when this peer is about to
250 * time out (will free resources associated with the peer).
251 */
252 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
253
254 /**
255 * ID of task scheduled to send keepalives.
256 */
257 GNUNET_SCHEDULER_TaskIdentifier keepalive_task;
258
259 /**
260 * ID of task scheduled to run when we should try transmitting
261 * the head of the message queue.
262 */
263 GNUNET_SCHEDULER_TaskIdentifier transmission_task;
264
265 /**
266 * Tracker for inbound bandwidth.
267 */
268 struct GNUNET_BANDWIDTH_Tracker in_tracker;
269
270 /**
271 * Timestamp of the 'SESSION_CONNECT' message we got from the other peer
272 */
273 struct GNUNET_TIME_Absolute connect_ts;
274
275 /**
276 * How often has the other peer (recently) violated the inbound
277 * traffic limit? Incremented by 10 per violation, decremented by 1
278 * per non-violation (for each time interval).
279 */
280 unsigned int quota_violation_count;
281
282 /**
283 * Number of values in 'ats' array.
284 */
285 //unsigned int ats_count;
286
287 /**
288 * Are we already in the process of disconnecting this neighbour?
289 */
290 int in_disconnect;
291
292 /**
293 * Do we currently consider this neighbour connected? (as far as
294 * the connect/disconnect callbacks are concerned)?
295 */
296 int is_connected;
297
298 int state;
299
300};
301
302
303/**
304 * All known neighbours and their HELLOs.
305 */
306static struct GNUNET_CONTAINER_MultiHashMap *neighbours;
307
308/**
309 * Closure for connect_notify_cb and disconnect_notify_cb
310 */
311static void *callback_cls;
312
313/**
314 * Function to call when we connected to a neighbour.
315 */
316static GNUNET_TRANSPORT_NotifyConnect connect_notify_cb;
317
318/**
319 * Function to call when we disconnected from a neighbour.
320 */
321static GNUNET_TRANSPORT_NotifyDisconnect disconnect_notify_cb;
322
323/**
324 * counter for connected neighbours
325 */
326static int neighbours_connected;
327
328/**
329 * Lookup a neighbour entry in the neighbours hash map.
330 *
331 * @param pid identity of the peer to look up
332 * @return the entry, NULL if there is no existing record
333 */
334static struct NeighbourMapEntry *
335lookup_neighbour (const struct GNUNET_PeerIdentity *pid)
336{
337 return GNUNET_CONTAINER_multihashmap_get (neighbours, &pid->hashPubKey);
338}
339
340#define change_state(n, state, ...) change (n, state, __LINE__)
341
342static int
343change (struct NeighbourMapEntry * n, int state, int line)
344{
345 char * old;
346 char * new;
347
348 switch (n->state) {
349 case S_CONNECTED:
350 old = "S_CONNECTED";
351 break;
352 case S_CONNECT_RECV:
353 old = "S_CONNECT_RECV";
354 break;
355 case S_CONNECT_RECV_ACK_SENT:
356 old = "S_CONNECT_RECV_ACK_SENT";
357 break;
358 case S_CONNECT_SENT:
359 old = "S_CONNECT_SENT";
360 break;
361 case S_DISCONNECTED:
362 old = "S_DISCONNECTED";
363 break;
364 case S_NOT_CONNECTED:
365 old = "S_NOT_CONNECTED";
366 break;
367 default:
368 GNUNET_break (0);
369 break;
370 }
371
372 switch (state) {
373 case S_CONNECTED:
374 new = "S_CONNECTED";
375 break;
376 case S_CONNECT_RECV:
377 new = "S_CONNECT_RECV";
378 break;
379 case S_CONNECT_RECV_ACK_SENT:
380 new = "S_CONNECT_RECV_ACK_SENT";
381 break;
382 case S_CONNECT_SENT:
383 new = "S_CONNECT_SENT";
384 break;
385 case S_DISCONNECTED:
386 new = "S_DISCONNECTED";
387 break;
388 case S_NOT_CONNECTED:
389 new = "S_NOT_CONNECTED";
390 break;
391 default:
392 GNUNET_break (0);
393 break;
394 }
395
396 /* allowed transitions */
397 switch (n->state) {
398 case S_NOT_CONNECTED:
399 if (state == S_CONNECT_RECV)
400 break;
401 if (state == S_CONNECT_SENT)
402 break;
403 case S_CONNECT_RECV:
404 if (state == S_CONNECT_RECV_ACK_SENT)
405 break;
406 case S_CONNECT_SENT:
407 if (state == S_NOT_CONNECTED)
408 break;
409 if (state == S_CONNECTED)
410 break;
411 case S_CONNECTED:
412 case S_DISCONNECTED:
413 default:
414 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
415 "Illegal state transition from `%s' to `%s' in line %u \n",
416 old, new, line);
417 GNUNET_break (0);
418 return GNUNET_SYSERR;
419 }
420
421 n->state = state;
422 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "State for neighbour `%s' %X changed from `%s' to `%s' in line %u\n",
423 GNUNET_i2s (&n->id), n, old, new, line);
424 return GNUNET_OK;
425}
426
427/**
428 * Task invoked to start a transmission to another peer.
429 *
430 * @param cls the 'struct NeighbourMapEntry'
431 * @param tc scheduler context
432 */
433static void
434transmission_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
435
436
437/**
438 * We're done with our transmission attempt, continue processing.
439 *
440 * @param cls the 'struct MessageQueue' of the message
441 * @param receiver intended receiver
442 * @param success whether it worked or not
443 */
444static void
445transmit_send_continuation (void *cls,
446 const struct GNUNET_PeerIdentity *receiver,
447 int success)
448{
449 struct MessageQueue *mq;
450 struct NeighbourMapEntry *n;
451
452 mq = cls;
453 n = mq->n;
454 if (NULL != n)
455 {
456 GNUNET_assert (n->is_active == mq);
457 n->is_active = NULL;
458 if (success == GNUNET_YES)
459 {
460 GNUNET_assert (n->transmission_task == GNUNET_SCHEDULER_NO_TASK);
461 n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
462 }
463 }
464
465 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Sending message of type %u had result: %u\n",
466 ntohs (((struct GNUNET_MessageHeader *) mq->message_buf)->type), success);
467
468 if (NULL != mq->cont)
469 mq->cont (mq->cont_cls, success);
470 GNUNET_free (mq);
471}
472
473
474/**
475 * Check the ready list for the given neighbour and if a plugin is
476 * ready for transmission (and if we have a message), do so!
477 *
478 * @param n target peer for which to transmit
479 */
480static void
481try_transmission_to_peer (struct NeighbourMapEntry *n)
482{
483 struct MessageQueue *mq;
484 struct GNUNET_TIME_Relative timeout;
485 ssize_t ret;
486 struct GNUNET_TRANSPORT_PluginFunctions *papi;
487
488 if (n->is_active != NULL)
489 {
490 GNUNET_break (0);
491 return; /* transmission already pending */
492 }
493 if (n->transmission_task != GNUNET_SCHEDULER_NO_TASK)
494 {
495 GNUNET_break (0);
496 return; /* currently waiting for bandwidth */
497 }
498 while (NULL != (mq = n->messages_head))
499 {
500 timeout = GNUNET_TIME_absolute_get_remaining (mq->timeout);
501 if (timeout.rel_value > 0)
502 break;
503 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
504 n->is_active = mq;
505 mq->n = n;
506 transmit_send_continuation (mq, &n->id, GNUNET_SYSERR); /* timeout */
507 }
508 if (NULL == mq)
509 return; /* no more messages */
510
511 papi = GST_plugins_find (n->plugin_name);
512 if (papi == NULL)
513 {
514 GNUNET_break (0);
515 return;
516 }
517 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
518 n->is_active = mq;
519 mq->n = n;
520
521 if ((n->session == NULL) && (n->addr == NULL) && (n->addrlen == 0))
522 {
523 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No address for peer `%s'\n",
524 GNUNET_i2s (&n->id));
525 transmit_send_continuation (mq, &n->id, GNUNET_SYSERR);
526 GNUNET_assert (n->transmission_task == GNUNET_SCHEDULER_NO_TASK);
527 n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
528 return;
529 }
530 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "try_transmission_to_peer\n");
531 papi = GST_plugins_find (n->plugin_name);
532 ret =
533 papi->send (papi->cls, &n->id, mq->message_buf, mq->message_buf_size,
534 0 /* priority -- remove from plugin API? */ ,
535 timeout, n->session, n->addr, n->addrlen, GNUNET_YES,
536 &transmit_send_continuation, mq);
537 if (ret == -1)
538 {
539 /* failure, but 'send' would not call continuation in this case,
540 * so we need to do it here! */
541 transmit_send_continuation (mq, &n->id, GNUNET_SYSERR);
542 }
543
544}
545
546
547/**
548 * Task invoked to start a transmission to another peer.
549 *
550 * @param cls the 'struct NeighbourMapEntry'
551 * @param tc scheduler context
552 */
553static void
554transmission_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
555{
556 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "transmission_task\n");
557 struct NeighbourMapEntry *n = cls;
558 GNUNET_assert (NULL != lookup_neighbour(&n->id));
559 n->transmission_task = GNUNET_SCHEDULER_NO_TASK;
560 try_transmission_to_peer (n);
561}
562
563
564/**
565 * Initialize the neighbours subsystem.
566 *
567 * @param cls closure for callbacks
568 * @param connect_cb function to call if we connect to a peer
569 * @param disconnect_cb function to call if we disconnect from a peer
570 */
571void
572GST_neighbours_start (void *cls, GNUNET_TRANSPORT_NotifyConnect connect_cb,
573 GNUNET_TRANSPORT_NotifyDisconnect disconnect_cb)
574{
575 callback_cls = cls;
576 connect_notify_cb = connect_cb;
577 disconnect_notify_cb = disconnect_cb;
578 neighbours = GNUNET_CONTAINER_multihashmap_create (NEIGHBOUR_TABLE_SIZE);
579}
580
581
582/**
583 * Disconnect from the given neighbour, clean up the record.
584 *
585 * @param n neighbour to disconnect from
586 */
587static void
588disconnect_neighbour (struct NeighbourMapEntry *n)
589{
590 struct MessageQueue *mq;
591
592 if (GNUNET_YES == n->in_disconnect)
593 return;
594 n->in_disconnect = GNUNET_YES;
595 while (NULL != (mq = n->messages_head))
596 {
597 GNUNET_CONTAINER_DLL_remove (n->messages_head, n->messages_tail, mq);
598 if (NULL != mq->cont)
599 mq->cont (mq->cont_cls, GNUNET_SYSERR);
600 GNUNET_free (mq);
601 }
602 if (NULL != n->is_active)
603 {
604 n->is_active->n = NULL;
605 n->is_active = NULL;
606 }
607 if (GNUNET_YES == n->is_connected)
608 {
609 n->is_connected = GNUNET_NO;
610 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != n->keepalive_task);
611 GNUNET_SCHEDULER_cancel (n->keepalive_task);
612 n->keepalive_task = GNUNET_SCHEDULER_NO_TASK;
613 GNUNET_assert (neighbours_connected > 0);
614 neighbours_connected--;
615 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), -1,
616 GNUNET_NO);
617 disconnect_notify_cb (callback_cls, &n->id);
618 }
619 GNUNET_assert (GNUNET_YES ==
620 GNUNET_CONTAINER_multihashmap_remove (neighbours,
621 &n->id.hashPubKey, n));
622 if (GNUNET_SCHEDULER_NO_TASK != n->timeout_task)
623 {
624 GNUNET_SCHEDULER_cancel (n->timeout_task);
625 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
626 }
627 if (GNUNET_SCHEDULER_NO_TASK != n->transmission_task)
628 {
629 GNUNET_SCHEDULER_cancel (n->transmission_task);
630 n->transmission_task = GNUNET_SCHEDULER_NO_TASK;
631 }
632 if (NULL != n->plugin_name)
633 {
634 GNUNET_free (n->plugin_name);
635 n->plugin_name = NULL;
636 }
637 if (NULL != n->addr)
638 {
639 GNUNET_free (n->addr);
640 n->addr = NULL;
641 n->addrlen = 0;
642 }
643 n->session = NULL;
644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Deleting peer `%4s', %X\n",
645 GNUNET_i2s (&n->id), n);
646 GNUNET_free (n);
647}
648
649
650/**
651 * Peer has been idle for too long. Disconnect.
652 *
653 * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
654 * @param tc scheduler context
655 */
656static void
657neighbour_timeout_task (void *cls,
658 const struct GNUNET_SCHEDULER_TaskContext *tc)
659{
660 struct NeighbourMapEntry *n = cls;
661
662 n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
663 if (GNUNET_YES == n->is_connected)
664 GNUNET_STATISTICS_update (GST_stats,
665 gettext_noop ("# peers disconnected due to timeout"), 1,
666 GNUNET_NO);
667 disconnect_neighbour (n);
668}
669
670
671/**
672 * Send another keepalive message.
673 *
674 * @param cls the 'struct NeighbourMapEntry' of the neighbour that went idle
675 * @param tc scheduler context
676 */
677static void
678neighbour_keepalive_task (void *cls,
679 const struct GNUNET_SCHEDULER_TaskContext *tc)
680{
681 struct NeighbourMapEntry *n = cls;
682 struct GNUNET_MessageHeader m;
683 struct GNUNET_TRANSPORT_PluginFunctions *papi;
684
685 n->keepalive_task = GNUNET_SCHEDULER_add_delayed (KEEPALIVE_FREQUENCY,
686 &neighbour_keepalive_task,
687 n);
688 GNUNET_assert (GNUNET_YES == n->is_connected);
689 GNUNET_STATISTICS_update (GST_stats,
690 gettext_noop ("# keepalives sent"), 1,
691 GNUNET_NO);
692 m.size = htons (sizeof (struct GNUNET_MessageHeader));
693 m.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE);
694 papi = GST_plugins_find (n->plugin_name);
695 if (papi != NULL)
696 papi->send (papi->cls,
697 &n->id, (const void *) &m,
698 sizeof (m),
699 UINT32_MAX /* priority */ ,
700 GNUNET_TIME_UNIT_FOREVER_REL, n->session, n->addr, n->addrlen,
701 GNUNET_YES, NULL, NULL);
702}
703
704
705/**
706 * Disconnect from the given neighbour.
707 *
708 * @param cls unused
709 * @param key hash of neighbour's public key (not used)
710 * @param value the 'struct NeighbourMapEntry' of the neighbour
711 */
712static int
713disconnect_all_neighbours (void *cls, const GNUNET_HashCode * key, void *value)
714{
715 struct NeighbourMapEntry *n = value;
716
717#if DEBUG_TRANSPORT
718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s', %s\n",
719 GNUNET_i2s (&n->id), "SHUTDOWN_TASK");
720#endif
721 if (GNUNET_YES == n->is_connected)
722 GNUNET_STATISTICS_update (GST_stats,
723 gettext_noop ("# peers disconnected due to global disconnect"), 1,
724 GNUNET_NO);
725 disconnect_neighbour (n);
726 return GNUNET_OK;
727}
728
729
730/**
731 * Cleanup the neighbours subsystem.
732 */
733void
734GST_neighbours_stop ()
735{
736 GNUNET_assert (neighbours != NULL);
737
738 GNUNET_CONTAINER_multihashmap_iterate (neighbours, &disconnect_all_neighbours,
739 NULL);
740 GNUNET_CONTAINER_multihashmap_destroy (neighbours);
741 GNUNET_assert (neighbours_connected == 0);
742 neighbours = NULL;
743 callback_cls = NULL;
744 connect_notify_cb = NULL;
745 disconnect_notify_cb = NULL;
746}
747
748
749/**
750 * We tried to send a SESSION_CONNECT message to another peer. If this
751 * succeeded, we should mark the peer up. If it failed, we should tell
752 * ATS to not use this address anymore (until it is re-validated).
753 *
754 * @param cls the 'struct NeighbourMapEntry'
755 * @param success GNUNET_OK on success
756 */
757static void
758send_connect_continuation (void *cls,
759 int success)
760{
761 struct NeighbourMapEntry *n = cls;
762
763 GNUNET_assert (n != NULL);
764 if (GNUNET_YES == n->in_disconnect)
765 return; /* neighbour is going away */
766 if (GNUNET_YES != success)
767 {
768 change_state(n, S_NOT_CONNECTED);
769 GNUNET_ATS_address_destroyed (GST_ats,
770 &n->id,
771 n->plugin_name,
772 n->addr,
773 n->addrlen,
774 NULL);
775 disconnect_neighbour (n);
776 return;
777 }
778}
779
780
781/**
782 * For an existing neighbour record, set the active connection to
783 * the given address.
784 *
785 * @param peer identity of the peer to switch the address for
786 * @param plugin_name name of transport that delivered the PONG
787 * @param address address of the other peer, NULL if other peer
788 * connected to us
789 * @param address_len number of bytes in address
790 * @param session session to use (or NULL)
791 * @param ats performance data
792 * @param ats_count number of entries in ats (excluding 0-termination)
793 * @return GNUNET_YES if we are currently connected, GNUNET_NO if the
794 * connection is not up (yet)
795 */
796int
797GST_neighbours_switch_to_address (const struct GNUNET_PeerIdentity *peer,
798 const char *plugin_name, const void *address,
799 size_t address_len, struct Session *session,
800 const struct GNUNET_ATS_Information
801 *ats, uint32_t ats_count)
802{
803 struct NeighbourMapEntry *n;
804 struct SessionConnectMessage connect_msg;
805 //int was_connected;
806
807 GNUNET_assert (neighbours != NULL);
808 n = lookup_neighbour (peer);
809 if (NULL == n)
810 {
811 if (NULL == session)
812 GNUNET_ATS_address_destroyed (GST_ats,
813 peer,
814 plugin_name, address,
815 address_len, NULL);
816 return GNUNET_NO;
817 }
818 /*
819 was_connected = n->is_connected;
820 n->is_connected = GNUNET_YES;
821 if (GNUNET_YES != was_connected)
822 n->keepalive_task = GNUNET_SCHEDULER_add_delayed (KEEPALIVE_FREQUENCY,
823 &neighbour_keepalive_task,
824 n);*/
825
826 change_state (n, S_CONNECT_SENT);
827
828#if DEBUG_TRANSPORT
829 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
830 "SWITCH! Peer `%4s' switches to plugin `%s' address '%s' session %X\n",
831 GNUNET_i2s (peer), plugin_name,
832 (address_len == 0) ? "<inbound>" : GST_plugins_a2s (plugin_name,
833 address,
834 address_len),
835 session);
836#endif
837 GNUNET_free_non_null (n->addr);
838 n->addr = GNUNET_malloc (address_len);
839 memcpy (n->addr, address, address_len);
840 n->addrlen = address_len;
841 n->session = session;
842 GNUNET_free_non_null (n->plugin_name);
843 n->plugin_name = GNUNET_strdup (plugin_name);
844 GNUNET_SCHEDULER_cancel (n->timeout_task);
845 n->timeout_task =
846 GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
847 &neighbour_timeout_task, n);
848 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
849 connect_msg.header.type =
850 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT);
851 connect_msg.reserved = htonl (0);
852 connect_msg.timestamp =
853 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
854 GST_neighbours_send (peer, &connect_msg, sizeof (connect_msg),
855 GNUNET_TIME_UNIT_FOREVER_REL,
856 &send_connect_continuation,
857 n);
858 /*
859 if (GNUNET_YES == was_connected)
860 return GNUNET_YES;
861 // First tell clients about connected neighbours...
862 neighbours_connected++;
863 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), 1,
864 GNUNET_NO);
865 connect_notify_cb (callback_cls, peer, ats, ats_count);
866 return GNUNET_YES;*/
867 /* connection not yet up */
868 return GNUNET_NO;
869}
870
871
872/**
873 * Create an entry in the neighbour map for the given peer
874 *
875 * @param peer peer to create an entry for
876 * @return new neighbour map entry
877 */
878static struct NeighbourMapEntry *
879setup_neighbour (const struct GNUNET_PeerIdentity *peer)
880{
881 struct NeighbourMapEntry *n;
882
883#if DEBUG_TRANSPORT
884#endif
885 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
886 "Unknown peer `%s', creating new neighbour\n",
887 GNUNET_i2s (peer));
888
889 n = GNUNET_malloc (sizeof (struct NeighbourMapEntry));
890 n->id = *peer;
891 n->state = S_NOT_CONNECTED;
892 GNUNET_BANDWIDTH_tracker_init (&n->in_tracker,
893 GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT,
894 MAX_BANDWIDTH_CARRY_S);
895 n->timeout_task =
896 GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
897 &neighbour_timeout_task, n);
898 GNUNET_assert (GNUNET_OK ==
899 GNUNET_CONTAINER_multihashmap_put (neighbours,
900 &n->id.hashPubKey, n,
901 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
902 return n;
903}
904
905
906/**
907 * Try to create a connection to the given target (eventually).
908 *
909 * @param target peer to try to connect to
910 */
911void
912GST_neighbours_try_connect (const struct GNUNET_PeerIdentity *target)
913{
914 struct NeighbourMapEntry *n;
915
916 GNUNET_assert (neighbours != NULL);
917#if DEBUG_TRANSPORT
918#endif
919 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Trying to connect to peer `%s'\n",
920 GNUNET_i2s (target));
921
922 GNUNET_assert (0 !=
923 memcmp (target, &GST_my_identity,
924 sizeof (struct GNUNET_PeerIdentity)));
925 n = lookup_neighbour (target);
926 if ((NULL != n) && (GNUNET_YES == n->is_connected))
927 return; /* already connected */
928
929 if ((NULL != n) && (n->state > S_NOT_CONNECTED))
930 return; /* already connecting */
931
932 if (n == NULL)
933 n = setup_neighbour (target);
934#if DEBUG_TRANSPORT
935 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
936 "Asking ATS for suggested address to connect to peer `%s'\n",
937 GNUNET_i2s (&n->id));
938#endif
939 GNUNET_ATS_suggest_address (GST_ats, &n->id);
940}
941
942
943/**
944 * Test if we're connected to the given peer.
945 *
946 * @param target peer to test
947 * @return GNUNET_YES if we are connected, GNUNET_NO if not
948 */
949int
950GST_neighbours_test_connected (const struct GNUNET_PeerIdentity *target)
951{
952 struct NeighbourMapEntry *n;
953
954 GNUNET_assert (neighbours != NULL);
955
956 n = lookup_neighbour (target);
957 if ((NULL == n) || (n->is_connected != GNUNET_YES))
958 return GNUNET_NO; /* not connected */
959 return GNUNET_YES;
960}
961
962
963/**
964 * A session was terminated. Take note.
965 *
966 * @param peer identity of the peer where the session died
967 * @param session session that is gone
968 */
969void
970GST_neighbours_session_terminated (const struct GNUNET_PeerIdentity *peer,
971 struct Session *session)
972{
973 struct NeighbourMapEntry *n;
974
975 GNUNET_assert (neighbours != NULL);
976
977#if DEBUG_TRANSPORT
978 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
979 "Session %X to peer `%s' ended \n",
980 session, GNUNET_i2s (peer));
981#endif
982 n = lookup_neighbour (peer);
983 if (NULL == n)
984 return;
985 if (session != n->session)
986 return; /* doesn't affect us */
987
988 n->session = NULL;
989 GNUNET_free (n->addr);
990 n->addr = NULL;
991 n->addrlen = 0;
992
993
994 if (GNUNET_YES != n->is_connected)
995 return; /* not connected anymore anyway, shouldn't matter */
996 /* fast disconnect unless ATS suggests a new address */
997 GNUNET_SCHEDULER_cancel (n->timeout_task);
998 n->timeout_task =
999 GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT,
1000 &neighbour_timeout_task, n);
1001 /* try QUICKLY to re-establish a connection, reduce timeout! */
1002 GNUNET_ATS_suggest_address (GST_ats, peer);
1003}
1004
1005
1006/**
1007 * Transmit a message to the given target using the active connection.
1008 *
1009 * @param target destination
1010 * @param msg message to send
1011 * @param msg_size number of bytes in msg
1012 * @param timeout when to fail with timeout
1013 * @param cont function to call when done
1014 * @param cont_cls closure for 'cont'
1015 */
1016void
1017GST_neighbours_send (const struct GNUNET_PeerIdentity *target, const void *msg,
1018 size_t msg_size, struct GNUNET_TIME_Relative timeout,
1019 GST_NeighbourSendContinuation cont, void *cont_cls)
1020{
1021 struct NeighbourMapEntry *n;
1022 struct MessageQueue *mq;
1023
1024 GNUNET_assert (neighbours != NULL);
1025
1026 n = lookup_neighbour (target);
1027 /* FIXME */
1028 if ((n == NULL) /* || (GNUNET_YES != n->is_connected)*/)
1029 {
1030 GNUNET_STATISTICS_update (GST_stats,
1031 gettext_noop
1032 ("# messages not sent (no such peer or not connected)"),
1033 1, GNUNET_NO);
1034#if DEBUG_TRANSPORT
1035 if (n == NULL)
1036 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1037 "Could not send message to peer `%s': unknown neighbor",
1038 GNUNET_i2s (target));
1039 else if (GNUNET_YES != n->is_connected)
1040 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1041 "Could not send message to peer `%s': not connected\n",
1042 GNUNET_i2s (target));
1043#endif
1044 if (NULL != cont)
1045 cont (cont_cls, GNUNET_SYSERR);
1046 return;
1047 }
1048 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "GST_neighbours_send %u\n", __LINE__);
1049 if ((n->session == NULL) && (n->addr == NULL) && (n->addrlen ==0))
1050 {
1051 GNUNET_STATISTICS_update (GST_stats,
1052 gettext_noop
1053 ("# messages not sent (no such peer or not connected)"),
1054 1, GNUNET_NO);
1055#if DEBUG_TRANSPORT
1056 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1057 "Could not send message to peer `%s': no address available\n",
1058 GNUNET_i2s (target));
1059#endif
1060
1061 if (NULL != cont)
1062 cont (cont_cls, GNUNET_SYSERR);
1063 return;
1064 }
1065 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "GST_neighbours_send %u\n", __LINE__);
1066 GNUNET_assert (msg_size >= sizeof (struct GNUNET_MessageHeader));
1067 GNUNET_STATISTICS_update (GST_stats,
1068 gettext_noop
1069 ("# bytes in message queue for other peers"),
1070 msg_size, GNUNET_NO);
1071 mq = GNUNET_malloc (sizeof (struct MessageQueue) + msg_size);
1072 mq->cont = cont;
1073 mq->cont_cls = cont_cls;
1074 /* FIXME: this memcpy can be up to 7% of our total runtime! */
1075 memcpy (&mq[1], msg, msg_size);
1076 mq->message_buf = (const char *) &mq[1];
1077 mq->message_buf_size = msg_size;
1078 mq->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1079 GNUNET_CONTAINER_DLL_insert_tail (n->messages_head, n->messages_tail, mq);
1080
1081 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "GST_neighbours_send %u\n", __LINE__);
1082
1083 if ((GNUNET_SCHEDULER_NO_TASK == n->transmission_task) &&
1084 (NULL == n->is_active))
1085 n->transmission_task = GNUNET_SCHEDULER_add_now (&transmission_task, n);
1086}
1087
1088
1089/**
1090 * We have received a message from the given sender. How long should
1091 * we delay before receiving more? (Also used to keep the peer marked
1092 * as live).
1093 *
1094 * @param sender sender of the message
1095 * @param size size of the message
1096 * @param do_forward set to GNUNET_YES if the message should be forwarded to clients
1097 * GNUNET_NO if the neighbour is not connected or violates the quota,
1098 * GNUNET_SYSERR if the connection is not fully up yet
1099 * @return how long to wait before reading more from this sender
1100 */
1101struct GNUNET_TIME_Relative
1102GST_neighbours_calculate_receive_delay (const struct GNUNET_PeerIdentity
1103 *sender, ssize_t size, int *do_forward)
1104{
1105 struct NeighbourMapEntry *n;
1106 struct GNUNET_TIME_Relative ret;
1107
1108 GNUNET_assert (neighbours != NULL);
1109
1110 n = lookup_neighbour (sender);
1111 if (n == NULL)
1112 {
1113 GST_neighbours_try_connect (sender);
1114 n = lookup_neighbour (sender);
1115 if (NULL == n)
1116 {
1117 GNUNET_STATISTICS_update (GST_stats,
1118 gettext_noop
1119 ("# messages discarded due to lack of neighbour record"),
1120 1, GNUNET_NO);
1121 *do_forward = GNUNET_NO;
1122 return GNUNET_TIME_UNIT_ZERO;
1123 }
1124 }
1125 if (GNUNET_YES != n->is_connected)
1126 {
1127 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1128 _("Plugin gave us %d bytes of data but somehow the session is not marked as UP yet!\n"),
1129 (int) size);
1130 *do_forward = GNUNET_SYSERR;
1131 return GNUNET_TIME_UNIT_ZERO;
1132 }
1133 if (GNUNET_YES == GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, size))
1134 {
1135 n->quota_violation_count++;
1136#if DEBUG_TRANSPORT
1137 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138 "Bandwidth quota (%u b/s) violation detected (total of %u).\n",
1139 n->in_tracker.available_bytes_per_s__,
1140 n->quota_violation_count);
1141#endif
1142 /* Discount 32k per violation */
1143 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, -32 * 1024);
1144 }
1145 else
1146 {
1147 if (n->quota_violation_count > 0)
1148 {
1149 /* try to add 32k back */
1150 GNUNET_BANDWIDTH_tracker_consume (&n->in_tracker, 32 * 1024);
1151 n->quota_violation_count--;
1152 }
1153 }
1154 if (n->quota_violation_count > QUOTA_VIOLATION_DROP_THRESHOLD)
1155 {
1156 GNUNET_STATISTICS_update (GST_stats,
1157 gettext_noop
1158 ("# bandwidth quota violations by other peers"),
1159 1, GNUNET_NO);
1160 *do_forward = GNUNET_NO;
1161 return GNUNET_CONSTANTS_QUOTA_VIOLATION_TIMEOUT;
1162 }
1163 *do_forward = GNUNET_YES;
1164 ret = GNUNET_BANDWIDTH_tracker_get_delay (&n->in_tracker, 32 * 1024);
1165 if (ret.rel_value > 0)
1166 {
1167#if DEBUG_TRANSPORT
1168 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1169 "Throttling read (%llu bytes excess at %u b/s), waiting %llu ms before reading more.\n",
1170 (unsigned long long) n->in_tracker.
1171 consumption_since_last_update__,
1172 (unsigned int) n->in_tracker.available_bytes_per_s__,
1173 (unsigned long long) ret.rel_value);
1174#endif
1175 GNUNET_STATISTICS_update (GST_stats,
1176 gettext_noop ("# ms throttling suggested"),
1177 (int64_t) ret.rel_value, GNUNET_NO);
1178 }
1179 return ret;
1180}
1181
1182
1183/**
1184 * Keep the connection to the given neighbour alive longer,
1185 * we received a KEEPALIVE (or equivalent).
1186 *
1187 * @param neighbour neighbour to keep alive
1188 */
1189void
1190GST_neighbours_keepalive (const struct GNUNET_PeerIdentity *neighbour)
1191{
1192 struct NeighbourMapEntry *n;
1193
1194 GNUNET_assert (neighbours != NULL);
1195
1196 n = lookup_neighbour (neighbour);
1197 if (NULL == n)
1198 {
1199 GNUNET_STATISTICS_update (GST_stats,
1200 gettext_noop
1201 ("# KEEPALIVE messages discarded (not connected)"),
1202 1, GNUNET_NO);
1203 return;
1204 }
1205 GNUNET_SCHEDULER_cancel (n->timeout_task);
1206 n->timeout_task =
1207 GNUNET_SCHEDULER_add_delayed (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
1208 &neighbour_timeout_task, n);
1209}
1210
1211
1212/**
1213 * Change the incoming quota for the given peer.
1214 *
1215 * @param neighbour identity of peer to change qutoa for
1216 * @param quota new quota
1217 */
1218void
1219GST_neighbours_set_incoming_quota (const struct GNUNET_PeerIdentity *neighbour,
1220 struct GNUNET_BANDWIDTH_Value32NBO quota)
1221{
1222 struct NeighbourMapEntry *n;
1223
1224 GNUNET_assert (neighbours != NULL);
1225
1226 n = lookup_neighbour (neighbour);
1227 if (n == NULL)
1228 {
1229 GNUNET_STATISTICS_update (GST_stats,
1230 gettext_noop
1231 ("# SET QUOTA messages ignored (no such peer)"),
1232 1, GNUNET_NO);
1233 return;
1234 }
1235 GNUNET_BANDWIDTH_tracker_update_quota (&n->in_tracker, quota);
1236 if (0 != ntohl (quota.value__))
1237 return;
1238#if DEBUG_TRANSPORT
1239 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting peer `%4s' due to `%s'\n",
1240 GNUNET_i2s (&n->id), "SET_QUOTA");
1241#endif
1242 if (GNUNET_YES == n->is_connected)
1243 GNUNET_STATISTICS_update (GST_stats,
1244 gettext_noop ("# disconnects due to quota of 0"), 1,
1245 GNUNET_NO);
1246 disconnect_neighbour (n);
1247}
1248
1249
1250/**
1251 * Closure for the neighbours_iterate function.
1252 */
1253struct IteratorContext
1254{
1255 /**
1256 * Function to call on each connected neighbour.
1257 */
1258 GST_NeighbourIterator cb;
1259
1260 /**
1261 * Closure for 'cb'.
1262 */
1263 void *cb_cls;
1264};
1265
1266
1267/**
1268 * Call the callback from the closure for each connected neighbour.
1269 *
1270 * @param cls the 'struct IteratorContext'
1271 * @param key the hash of the public key of the neighbour
1272 * @param value the 'struct NeighbourMapEntry'
1273 * @return GNUNET_OK (continue to iterate)
1274 */
1275static int
1276neighbours_iterate (void *cls, const GNUNET_HashCode * key, void *value)
1277{
1278 struct IteratorContext *ic = cls;
1279 struct NeighbourMapEntry *n = value;
1280
1281 if (GNUNET_YES != n->is_connected)
1282 return GNUNET_OK;
1283
1284 ic->cb (ic->cb_cls, &n->id, NULL, 0, n->plugin_name, n->addr, n->addrlen);
1285 return GNUNET_OK;
1286}
1287
1288
1289/**
1290 * Iterate over all connected neighbours.
1291 *
1292 * @param cb function to call
1293 * @param cb_cls closure for cb
1294 */
1295void
1296GST_neighbours_iterate (GST_NeighbourIterator cb, void *cb_cls)
1297{
1298 struct IteratorContext ic;
1299
1300 GNUNET_assert (neighbours != NULL);
1301
1302 ic.cb = cb;
1303 ic.cb_cls = cb_cls;
1304 GNUNET_CONTAINER_multihashmap_iterate (neighbours, &neighbours_iterate, &ic);
1305}
1306
1307
1308/**
1309 * If we have an active connection to the given target, it must be shutdown.
1310 *
1311 * @param target peer to disconnect from
1312 */
1313void
1314GST_neighbours_force_disconnect (const struct GNUNET_PeerIdentity *target)
1315{
1316 struct NeighbourMapEntry *n;
1317 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1318 struct SessionDisconnectMessage disconnect_msg;
1319
1320 GNUNET_assert (neighbours != NULL);
1321
1322 n = lookup_neighbour (target);
1323 if (NULL == n)
1324 return; /* not active */
1325 if (GNUNET_YES == n->is_connected)
1326 {
1327 /* we're actually connected, send DISCONNECT message */
1328 disconnect_msg.header.size = htons (sizeof (struct SessionDisconnectMessage));
1329 disconnect_msg.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT);
1330 disconnect_msg.reserved = htonl (0);
1331 disconnect_msg.purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
1332 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1333 sizeof (struct GNUNET_TIME_AbsoluteNBO) );
1334 disconnect_msg.purpose.purpose = htonl (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT);
1335 disconnect_msg.timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1336 disconnect_msg.public_key = GST_my_public_key;
1337 GNUNET_assert (GNUNET_OK ==
1338 GNUNET_CRYPTO_rsa_sign (GST_my_private_key,
1339 &disconnect_msg.purpose,
1340 &disconnect_msg.signature));
1341
1342 papi = GST_plugins_find (n->plugin_name);
1343 if (papi != NULL)
1344 papi->send (papi->cls, target, (const void *) &disconnect_msg,
1345 sizeof (disconnect_msg),
1346 UINT32_MAX /* priority */ ,
1347 GNUNET_TIME_UNIT_FOREVER_REL, n->session, n->addr, n->addrlen,
1348 GNUNET_YES, NULL, NULL);
1349 GNUNET_STATISTICS_update (GST_stats,
1350 gettext_noop ("# peers disconnected due to external request"), 1,
1351 GNUNET_NO);
1352 n = lookup_neighbour (target);
1353 if (NULL == n)
1354 return; /* gone already */
1355 }
1356 disconnect_neighbour (n);
1357}
1358
1359
1360/**
1361 * We received a disconnect message from the given peer,
1362 * validate and process.
1363 *
1364 * @param peer sender of the message
1365 * @param msg the disconnect message
1366 */
1367void
1368GST_neighbours_handle_disconnect_message (const struct GNUNET_PeerIdentity *peer,
1369 const struct GNUNET_MessageHeader *msg)
1370{
1371 struct NeighbourMapEntry *n;
1372 const struct SessionDisconnectMessage *sdm;
1373 GNUNET_HashCode hc;
1374
1375 if (ntohs (msg->size) != sizeof (struct SessionDisconnectMessage))
1376 {
1377 // GNUNET_break_op (0);
1378 GNUNET_STATISTICS_update (GST_stats,
1379 gettext_noop ("# disconnect messages ignored (old format)"), 1,
1380 GNUNET_NO);
1381 return;
1382 }
1383 sdm = (const struct SessionDisconnectMessage* ) msg;
1384 n = lookup_neighbour (peer);
1385 if (NULL == n)
1386 return; /* gone already */
1387 if (GNUNET_TIME_absolute_ntoh (sdm->timestamp).abs_value <=
1388 n->connect_ts.abs_value)
1389 {
1390 GNUNET_STATISTICS_update (GST_stats,
1391 gettext_noop ("# disconnect messages ignored (timestamp)"), 1,
1392 GNUNET_NO);
1393 return;
1394 }
1395 GNUNET_CRYPTO_hash (&sdm->public_key,
1396 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
1397 &hc);
1398 if (0 != memcmp (peer,
1399 &hc,
1400 sizeof (struct GNUNET_PeerIdentity)))
1401 {
1402 GNUNET_break_op (0);
1403 return;
1404 }
1405 if (ntohl (sdm->purpose.size) !=
1406 sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) +
1407 sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) +
1408 sizeof (struct GNUNET_TIME_AbsoluteNBO))
1409 {
1410 GNUNET_break_op (0);
1411 return;
1412 }
1413 if (GNUNET_OK !=
1414 GNUNET_CRYPTO_rsa_verify (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT,
1415 &sdm->purpose,
1416 &sdm->signature,
1417 &sdm->public_key))
1418 {
1419 GNUNET_break_op (0);
1420 return;
1421 }
1422 GST_neighbours_force_disconnect (peer);
1423}
1424
1425static void neighbour_connected (struct NeighbourMapEntry *n,
1426 const struct GNUNET_ATS_Information *ats,
1427 uint32_t ats_count)
1428{
1429 /* LEGACY */
1430 int was_connected;
1431 was_connected = n->is_connected;
1432 n->is_connected = GNUNET_YES;
1433 /* END LEGACY */
1434
1435 if (GNUNET_YES != was_connected)
1436 n->keepalive_task = GNUNET_SCHEDULER_add_delayed (KEEPALIVE_FREQUENCY,
1437 &neighbour_keepalive_task,
1438 n);
1439 if (n->state == S_CONNECTED)
1440 return;
1441 // First tell clients about connected neighbours...
1442 //change_state (n, S_CONNECTED);
1443
1444 neighbours_connected++;
1445 GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# peers connected"), 1,
1446 GNUNET_NO);
1447 connect_notify_cb (callback_cls, &n->id, ats, ats_count);
1448}
1449
1450
1451/**
1452 * We received a 'SESSION_CONNECT_ACK' message from the other peer.
1453 * Consider switching to it.
1454 *
1455 * @param message possibly a 'struct SessionConnectMessage' (check format)
1456 * @param peer identity of the peer to switch the address for
1457 * @param plugin_name name of transport that delivered the PONG
1458 * @param address address of the other peer, NULL if other peer
1459 * connected to us
1460 * @param address_len number of bytes in address
1461 * @param session session to use (or NULL)
1462 * @param ats performance data
1463 * @param ats_count number of entries in ats
1464 */
1465void
1466GST_neighbours_handle_connect_ack (const struct GNUNET_MessageHeader *message,
1467 const struct GNUNET_PeerIdentity *peer,
1468 const char *plugin_name,
1469 const char *sender_address, uint16_t sender_address_len,
1470 struct Session *session,
1471 const struct GNUNET_ATS_Information *ats,
1472 uint32_t ats_count)
1473{
1474 const struct SessionConnectMessage *scm;
1475 struct GNUNET_TIME_Absolute ts;
1476 struct NeighbourMapEntry *n;
1477 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1478 "GST_neighbours_handle_connect_ack\n");
1479 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
1480 {
1481 GNUNET_break_op (0);
1482 return;
1483 }
1484
1485 scm = (const struct SessionConnectMessage *) message;
1486 GNUNET_break_op (ntohl (scm->reserved) == 0);
1487 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
1488 n = lookup_neighbour (peer);
1489 if (NULL == n)
1490 n = setup_neighbour (peer);
1491
1492 if (n->state != S_CONNECT_SENT)
1493 {
1494 GNUNET_break (0);
1495 return;
1496 }
1497
1498 change_state (n, S_CONNECTED);
1499 neighbour_connected (n, ats, ats_count);
1500}
1501
1502
1503/**
1504 * We received a 'SESSION_CONNECT' message from the other peer.
1505 * Consider switching to it.
1506 *
1507 * @param message possibly a 'struct SessionConnectMessage' (check format)
1508 * @param peer identity of the peer to switch the address for
1509 * @param plugin_name name of transport that delivered the PONG
1510 * @param address address of the other peer, NULL if other peer
1511 * connected to us
1512 * @param address_len number of bytes in address
1513 * @param session session to use (or NULL)
1514 * @param ats performance data
1515 * @param ats_count number of entries in ats (excluding 0-termination)
1516 */
1517void
1518GST_neighbours_handle_connect (const struct GNUNET_MessageHeader *message,
1519 const struct GNUNET_PeerIdentity *peer,
1520 const char *plugin_name,
1521 const char *sender_address, uint16_t sender_address_len,
1522 struct Session *session,
1523 const struct GNUNET_ATS_Information *ats,
1524 uint32_t ats_count)
1525{
1526 const struct SessionConnectMessage *scm;
1527 struct GNUNET_TIME_Absolute ts;
1528 struct NeighbourMapEntry *n;
1529 struct SessionConnectMessage connect_msg;
1530 struct GNUNET_TRANSPORT_PluginFunctions *papi;
1531 int ret;
1532
1533 if (ntohs (message->size) != sizeof (struct SessionConnectMessage))
1534 {
1535 GNUNET_break_op (0);
1536 return;
1537 }
1538 scm = (const struct SessionConnectMessage *) message;
1539 GNUNET_break_op (ntohl (scm->reserved) == 0);
1540 ts = GNUNET_TIME_absolute_ntoh (scm->timestamp);
1541 n = lookup_neighbour (peer);
1542 if (NULL == n)
1543 n = setup_neighbour (peer);
1544
1545 if (n->state > S_NOT_CONNECTED)
1546 return;
1547
1548 change_state (n, S_CONNECT_RECV);
1549 /* send CONNECT_ACK */
1550 connect_msg.header.size = htons (sizeof (struct SessionConnectMessage));
1551 connect_msg.header.type =
1552 htons (GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK);
1553 connect_msg.reserved = htonl (0);
1554 connect_msg.timestamp =
1555 GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
1556
1557 papi = GST_plugins_find (plugin_name);
1558 ret = papi->send (papi->cls, &n->id, (const char *) &connect_msg, sizeof (struct SessionConnectMessage),
1559 0,
1560 GNUNET_TIME_UNIT_FOREVER_REL, NULL, sender_address, sender_address_len, GNUNET_YES,
1561 NULL, NULL);
1562
1563 if (ret == GNUNET_SYSERR)
1564 {
1565 change_state (n, S_NOT_CONNECTED);
1566 GNUNET_break (0);
1567 return;
1568 }
1569
1570 if (ts.abs_value > n->connect_ts.abs_value)
1571 {
1572 if (NULL != session)
1573 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
1574 "transport-ats",
1575 "Giving ATS session %p of plugin %s for peer %s\n",
1576 session,
1577 plugin_name,
1578 GNUNET_i2s (peer));
1579 GNUNET_ATS_address_update (GST_ats,
1580 peer,
1581 plugin_name, sender_address, sender_address_len,
1582 session, ats, ats_count);
1583 n->connect_ts = ts;
1584 }
1585}
1586
1587
1588/* end of file gnunet-service-transport_neighbours.c */