aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-06-13 23:15:58 +0000
committerChristian Grothoff <christian@grothoff.org>2009-06-13 23:15:58 +0000
commit3da76c97d01ec6dc652183c1f457d1379f15f4f8 (patch)
tree8f860c76d7237e287eed299bea29095f027c28f8 /src
parenta14ead2e712910bb5e4d818d2757592c4f14031b (diff)
downloadgnunet-3da76c97d01ec6dc652183c1f457d1379f15f4f8.tar.gz
gnunet-3da76c97d01ec6dc652183c1f457d1379f15f4f8.zip
first hack at topology -- incomplete
Diffstat (limited to 'src')
-rw-r--r--src/topology/gnunet-daemon-topology.c618
1 files changed, 609 insertions, 9 deletions
diff --git a/src/topology/gnunet-daemon-topology.c b/src/topology/gnunet-daemon-topology.c
index 5d8c92523..4db35a17c 100644
--- a/src/topology/gnunet-daemon-topology.c
+++ b/src/topology/gnunet-daemon-topology.c
@@ -22,16 +22,476 @@
22 * @file topology/gnunet-daemon-topology.c 22 * @file topology/gnunet-daemon-topology.c
23 * @brief code for bootstrapping via topology servers 23 * @brief code for bootstrapping via topology servers
24 * @author Christian Grothoff 24 * @author Christian Grothoff
25 *
26 * TODO:
27 * - blacklisting & respect for blacklist
28 * - calculate target_connection_count!
29 * - calculate peer_search retry delay
25 */ 30 */
26 31
27#include <stdlib.h> 32#include <stdlib.h>
28#include "platform.h" 33#include "platform.h"
29#include "gnunet_getopt_lib.h" 34#include "gnunet_core_service.h"
30#include "gnunet_protocols.h" 35#include "gnunet_protocols.h"
31#include "gnunet_program_lib.h" 36#include "gnunet_peerinfo_service.h"
32#include "gnunet_statistics_service.h" 37#include "gnunet_util_lib.h"
33#include "gnunet_strings_lib.h" 38
34#include "gnunet_time_lib.h" 39
40#define DEBUG_TOPOLOGY GNUNET_NO
41
42
43/**
44 * List of neighbours and friends.
45 */
46struct FriendList
47{
48
49 /**
50 * This is a linked list.
51 */
52 struct FriendList *next;
53
54 /**
55 * Is this peer listed here because he is a friend?
56 */
57 int is_friend;
58
59 /**
60 * Are we connected to this peer right now?
61 */
62 int is_connected;
63
64 /**
65 * Until what time should we not try to connect again
66 * to this peer?
67 */
68 struct GNUNET_TIME_Absolute blacklisted_until;
69
70 /**
71 * ID of the peer.
72 */
73 struct GNUNET_PeerIdentity id;
74
75};
76
77
78/**
79 * Our scheduler.
80 */
81static struct GNUNET_SCHEDULER_Handle * sched;
82
83/**
84 * Our configuration.
85 */
86static struct GNUNET_CONFIGURATION_Handle * cfg;
87
88/**
89 * Handle to the core API.
90 */
91static struct GNUNET_CORE_Handle *handle;
92
93/**
94 * Identity of this peer.
95 */
96static struct GNUNET_PeerIdentity my_identity;
97
98/**
99 * Linked list of all of our friends and all of our current
100 * neighbours.
101 */
102static struct FriendList *friends;
103
104/**
105 * Flag to disallow non-friend connections (pure F2F mode).
106 */
107static int friends_only;
108
109/**
110 * Minimum number of friends to have in the
111 * connection set before we allow non-friends.
112 */
113static unsigned int minimum_friend_count;
114
115/**
116 * Number of peers (friends and others) that we are currently connected to.
117 */
118static unsigned int connection_count;
119
120/**
121 * Target number of connections.
122 */
123static unsigned int target_connection_count;
124
125/**
126 * Number of friends that we are currently connected to.
127 */
128static unsigned int friend_count;
129
130/**
131 * Should the topology daemon try to establish connections?
132 */
133static int autoconnect;
134
135
136
137/**
138 * Force a disconnect from the specified peer.
139 */
140static void
141force_disconnect (const struct GNUNET_PeerIdentity *peer)
142{
143 GNUNET_CORE_peer_configure (handle,
144 peer,
145 GNUNET_TIME_UNIT_FOREVER_REL,
146 0,
147 0,
148 0,
149 NULL,
150 NULL);
151}
152
153
154/**
155 * Function called by core when our attempt to connect
156 * succeeded. Does nothing.
157 */
158static size_t
159ready_callback (void *cls,
160 size_t size, void *buf)
161{
162 return 0;
163}
164
165
166/**
167 * Try to connect to the specified peer.
168 *
169 * @param pos NULL if not in friend list yet
170 */
171static void
172attempt_connect (const struct GNUNET_PeerIdentity *peer,
173 struct FriendList *pos)
174{
175 /* FIXME: do blacklist! */
176 GNUNET_CORE_notify_transmit_ready (handle,
177 0 /* priority */,
178 GNUNET_TIME_UNIT_MINUTES,
179 peer,
180 sizeof(struct GNUNET_MessageHeader),
181 &ready_callback,
182 NULL);
183}
184
185
186/**
187 * Is this peer one of our friends?
188 */
189static int
190is_friend (const struct GNUNET_PeerIdentity * peer)
191{
192 struct FriendList *pos;
193
194 pos = friends;
195 while (pos != NULL)
196 {
197 if ( (GNUNET_YES == pos->is_friend) &&
198 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
199 return GNUNET_YES;
200 pos = pos->next;
201 }
202 return GNUNET_NO;
203}
204
205
206/**
207 * Check if an additional connection from the given peer is allowed.
208 */
209static int
210is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
211{
212 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
213 return GNUNET_SYSERR; /* disallow connections to self */
214 if (is_friend (peer))
215 return GNUNET_OK;
216 if (GNUNET_YES == friends_only)
217 return GNUNET_SYSERR;
218 if (friend_count >= minimum_friend_count)
219 return GNUNET_OK;
220 return GNUNET_SYSERR;
221}
222
223
224/**
225 * Method called whenever a peer connects.
226 *
227 * @param cls closure
228 * @param peer peer identity this notification is about
229 */
230static void connect_notify (void *cls,
231 const struct
232 GNUNET_PeerIdentity * peer)
233{
234 struct FriendList *pos;
235
236 connection_count++;
237 pos = friends;
238 while (pos != NULL)
239 {
240 if ( (GNUNET_YES == pos->is_friend) &&
241 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
242 {
243 GNUNET_assert (GNUNET_NO == pos->is_connected);
244 pos->is_connected = GNUNET_YES;
245 friend_count++;
246 return;
247 }
248 pos = pos->next;
249 }
250 pos = GNUNET_malloc (sizeof(struct FriendList));
251 pos->id = *peer;
252 pos->is_connected = GNUNET_YES;
253 pos->next = friends;
254 friends = pos;
255 if (GNUNET_OK != is_connection_allowed (peer))
256 force_disconnect (peer);
257}
258
259
260/**
261 * Disconnect from all non-friends (we're below quota).
262 */
263static void
264drop_non_friends ()
265{
266 struct FriendList *pos;
267
268 pos = friends;
269 while (pos != NULL)
270 {
271 if (GNUNET_NO == pos->is_friend)
272 {
273 GNUNET_assert (GNUNET_YES == pos->is_connected);
274 force_disconnect (&pos->id);
275 }
276 pos = pos->next;
277 }
278}
279
280
281/**
282 * Method called whenever a peer disconnects.
283 *
284 * @param cls closure
285 * @param peer peer identity this notification is about
286 */
287static void disconnect_notify (void *cls,
288 const struct
289 GNUNET_PeerIdentity * peer)
290{
291 struct FriendList *pos;
292 struct FriendList *prev;
293
294 connection_count--;
295 pos = friends;
296 prev = NULL;
297 while (pos != NULL)
298 {
299 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
300 {
301 GNUNET_assert (GNUNET_YES == pos->is_connected);
302 pos->is_connected = GNUNET_NO;
303 if (GNUNET_YES == pos->is_friend)
304 {
305 friend_count--;
306 if (friend_count < minimum_friend_count)
307 {
308 /* disconnect from all non-friends */
309 drop_non_friends ();
310 attempt_connect (peer, pos);
311 }
312 }
313 else
314 {
315 /* free entry */
316 if (prev == NULL)
317 friends = pos->next;
318 else
319 prev->next = pos->next;
320 GNUNET_free (pos);
321 }
322 return;
323 }
324 prev = pos;
325 pos = pos->next;
326 }
327 GNUNET_break (0);
328}
329
330/**
331 * Find more peers that we should connect to and ask the
332 * core to establish connections.
333 */
334static void
335find_more_peers (void *cls,
336 const struct GNUNET_SCHEDULER_TaskContext *tc);
337
338
339/**
340 * Determine when we should try again to find more peers and
341 * schedule the task.
342 */
343static void
344schedule_peer_search ()
345{
346 struct GNUNET_TIME_Relative delay;
347
348 /* FIXME: calculate reasonable delay here */
349 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
350 42);
351 GNUNET_SCHEDULER_add_delayed (sched,
352 GNUNET_NO,
353 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
354 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
355 delay,
356 &find_more_peers,
357 NULL);
358}
359
360
361/**
362 * Peerinfo calls this function to let us know about a
363 * possible peer that we might want to connect to.
364 */
365static void
366process_peer (void *cls,
367 const struct GNUNET_PeerIdentity * peer,
368 const struct GNUNET_HELLO_Message * hello,
369 uint32_t trust)
370{
371 struct FriendList *pos;
372
373 if (peer == NULL)
374 {
375 /* last call, schedule 'find_more_peers' again... */
376 schedule_peer_search ();
377 return;
378 }
379 if (hello == NULL)
380 {
381 /* no HELLO known; can not connect, ignore! */
382 return;
383 }
384 if (0 == memcmp (&my_identity,
385 peer, sizeof (struct GNUNET_PeerIdentity)))
386 return; /* that's me! */
387
388 pos = friends;
389 while (pos != NULL)
390 {
391 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
392 {
393 if (GNUNET_YES == pos->is_connected)
394 return;
395 /* FIXME: check blacklisted... */
396 if (GNUNET_YES == pos->is_friend)
397 {
398 attempt_connect (peer, pos);
399 return;
400 }
401 }
402 pos = pos->next;
403 }
404 if (GNUNET_YES == friends_only)
405 return;
406 if (friend_count < minimum_friend_count)
407 return;
408 attempt_connect (peer, NULL);
409}
410
411
412/**
413 * Try to add more friends to our connection set.
414 */
415static void
416try_add_friends ()
417{
418 struct FriendList *pos;
419
420 pos = friends;
421 while (pos != NULL)
422 {
423 /* FIXME: check friends for blacklisting... */
424 if ( (GNUNET_YES == pos->is_friend) &&
425 (GNUNET_YES != pos->is_connected) )
426 attempt_connect (&pos->id, pos);
427 pos = pos->next;
428 }
429}
430
431
432/**
433 * Find more peers that we should connect to and ask the
434 * core to establish connections.
435 */
436static void
437find_more_peers (void *cls,
438 const struct GNUNET_SCHEDULER_TaskContext *tc)
439{
440 if (target_connection_count <= connection_count)
441 {
442 schedule_peer_search ();
443 return;
444 }
445 if ( (GNUNET_YES == friends_only) ||
446 (friend_count < minimum_friend_count) )
447 {
448 try_add_friends ();
449 schedule_peer_search ();
450 return;
451 }
452 GNUNET_PEERINFO_for_all (cfg,
453 sched,
454 NULL,
455 0, GNUNET_TIME_UNIT_FOREVER_REL,
456 &process_peer, NULL);
457}
458
459
460/**
461 * Function called after GNUNET_CORE_connect has succeeded
462 * (or failed for good).
463 *
464 * @param cls closure
465 * @param server handle to the server, NULL if we failed
466 * @param my_id ID of this peer, NULL if we failed
467 * @param publicKey public key of this peer, NULL if we failed
468 */
469static void
470core_init (void *cls,
471 struct GNUNET_CORE_Handle * server,
472 const struct GNUNET_PeerIdentity *
473 my_id,
474 const struct
475 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
476 publicKey)
477{
478 if (server == NULL)
479 {
480 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
481 _("Failed to connect to core service, can not manage topology!\n"));
482 return;
483 }
484 handle = server;
485 my_identity = *my_id;
486 if (autoconnect)
487 GNUNET_SCHEDULER_add_delayed (sched,
488 GNUNET_NO,
489 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
490 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
491 GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
492 &find_more_peers,
493 NULL);
494}
35 495
36 496
37/** 497/**
@@ -42,23 +502,163 @@ static struct GNUNET_GETOPT_CommandLineOption options[] = {
42}; 502};
43 503
44 504
505/**
506 * Read the friends file.
507 */
508static void
509read_friends_file (struct GNUNET_CONFIGURATION_Handle *cfg)
510{
511 char *fn;
512 char *data;
513 size_t pos;
514 GNUNET_HashCode hc;
515 struct stat frstat;
516 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
517 unsigned int entries_found;
518 struct FriendList *fl;
519
520 fn = NULL;
521 GNUNET_CONFIGURATION_get_value_filename (cfg,
522 "TOPOLOGY",
523 "FRIENDS",
524 &fn);
525 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
526 GNUNET_DISK_file_write (fn, NULL, 0, "600");
527 if (0 != STAT (fn, &frstat))
528 {
529 if ((friends_only) || (minimum_friend_count > 0))
530 {
531 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
532 _("Could not read friends list `%s'\n"), fn);
533 GNUNET_free (fn);
534 return;
535 }
536 }
537 if (frstat.st_size == 0)
538 {
539 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
540 _("Friends file `%s' is empty.\n"),
541 fn);
542 GNUNET_free (fn);
543 return;
544 }
545 data = GNUNET_malloc_large (frstat.st_size);
546 if (frstat.st_size !=
547 GNUNET_DISK_file_read (fn, frstat.st_size, data))
548 {
549 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
550 _("Failed to read friends list from `%s'\n"), fn);
551 GNUNET_free (fn);
552 GNUNET_free (data);
553 return;
554 }
555 entries_found = 0;
556 pos = 0;
557 while ((pos < frstat.st_size) && isspace (data[pos]))
558 pos++;
559 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
560 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
561 {
562 memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
563 if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
564 {
565 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
566 _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
567 (unsigned long long) pos);
568 pos++;
569 while ((pos < frstat.st_size) && (!isspace (data[pos])))
570 pos++;
571 continue;
572 }
573 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
574 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
575 {
576 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
577 _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
578 (unsigned long long) pos,
579 &enc);
580 }
581 else
582 {
583 entries_found++;
584 fl = GNUNET_malloc (sizeof(struct FriendList));
585 fl->is_friend = GNUNET_YES;
586 fl->id.hashPubKey = hc;
587 fl->next = friends;
588 friends = fl;
589 }
590 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
591 while ((pos < frstat.st_size) && isspace (data[pos]))
592 pos++;
593 }
594 GNUNET_free (data);
595 GNUNET_free (fn);
596 if ( (minimum_friend_count > entries_found) &&
597 (friends_only == GNUNET_NO) )
598 {
599 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
600 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
601 }
602 if ( (minimum_friend_count > target_connection_count) &&
603 (friends_only == GNUNET_NO) )
604 {
605 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
606 _("More friendly connections required than target total number of connections.\n"));
607 }
608}
609
45 610
46/** 611/**
47 * Main function that will be run. 612 * Main function that will be run.
48 * 613 *
49 * @param cls closure 614 * @param cls closure
50 * @param sched the scheduler to use 615 * @param s the scheduler to use
51 * @param args remaining command-line arguments 616 * @param args remaining command-line arguments
52 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 617 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
53 * @param cfg configuration 618 * @param c configuration
54 */ 619 */
55static void 620static void
56run (void *cls, 621run (void *cls,
57 struct GNUNET_SCHEDULER_Handle * sched, 622 struct GNUNET_SCHEDULER_Handle * s,
58 char *const *args, 623 char *const *args,
59 const char *cfgfile, 624 const char *cfgfile,
60 struct GNUNET_CONFIGURATION_Handle * cfg) 625 struct GNUNET_CONFIGURATION_Handle * c)
61{ 626{
627 struct GNUNET_CORE_MessageHandler handlers[] =
628 {
629 { NULL, 0, 0 }
630 };
631 unsigned long long opt;
632
633 sched = s;
634 cfg = c;
635 autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
636 "TOPOLOGY",
637 "AUTOCONNECT");
638 friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
639 "TOPOLOGY",
640 "FRIENDS-ONLY");
641 opt = 0;
642 GNUNET_CONFIGURATION_get_value_number (cfg,
643 "TOPOLOGY",
644 "MINIMUM-FRIENDS",
645 &opt);
646 minimum_friend_count = (unsigned int) opt;
647
648 if ( (friends_only == GNUNET_YES) ||
649 (minimum_friend_count > 0) )
650 read_friends_file (cfg);
651 GNUNET_CORE_connect (sched,
652 cfg,
653 GNUNET_TIME_UNIT_FOREVER_REL,
654 NULL,
655 &core_init,
656 &connect_notify,
657 &disconnect_notify,
658 NULL,
659 NULL, GNUNET_NO,
660 NULL, GNUNET_NO,
661 handlers);
62} 662}
63 663
64 664