gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r8563 - gnunet/src/topology


From: gnunet
Subject: [GNUnet-SVN] r8563 - gnunet/src/topology
Date: Sat, 13 Jun 2009 17:15:58 -0600

Author: grothoff
Date: 2009-06-13 17:15:58 -0600 (Sat, 13 Jun 2009)
New Revision: 8563

Modified:
   gnunet/src/topology/gnunet-daemon-topology.c
Log:
first hack at topology -- incomplete

Modified: gnunet/src/topology/gnunet-daemon-topology.c
===================================================================
--- gnunet/src/topology/gnunet-daemon-topology.c        2009-06-13 18:09:19 UTC 
(rev 8562)
+++ gnunet/src/topology/gnunet-daemon-topology.c        2009-06-13 23:15:58 UTC 
(rev 8563)
@@ -22,19 +22,479 @@
  * @file topology/gnunet-daemon-topology.c
  * @brief code for bootstrapping via topology servers
  * @author Christian Grothoff
+ *
+ * TODO: 
+ * - blacklisting & respect for blacklist
+ * - calculate target_connection_count!
+ * - calculate peer_search retry delay 
  */
 
 #include <stdlib.h>
 #include "platform.h"
-#include "gnunet_getopt_lib.h"
+#include "gnunet_core_service.h"
 #include "gnunet_protocols.h"
-#include "gnunet_program_lib.h"
-#include "gnunet_statistics_service.h"
-#include "gnunet_strings_lib.h"
-#include "gnunet_time_lib.h"
+#include "gnunet_peerinfo_service.h"
+#include "gnunet_util_lib.h"
 
 
+#define DEBUG_TOPOLOGY GNUNET_NO
+
+
 /**
+ * List of neighbours and friends.
+ */
+struct FriendList
+{
+
+  /**
+   * This is a linked list.
+   */
+  struct FriendList *next;
+
+  /**
+   * Is this peer listed here because he is a friend?
+   */
+  int is_friend;
+
+  /**
+   * Are we connected to this peer right now?
+   */
+  int is_connected;
+
+  /**
+   * Until what time should we not try to connect again
+   * to this peer?
+   */
+  struct GNUNET_TIME_Absolute blacklisted_until;
+
+  /**
+   * ID of the peer.
+   */
+  struct GNUNET_PeerIdentity id;
+  
+};
+
+
+/**
+ * Our scheduler.
+ */
+static struct GNUNET_SCHEDULER_Handle * sched;
+
+/**
+ * Our configuration.
+ */
+static struct GNUNET_CONFIGURATION_Handle * cfg;
+   
+/**
+ * Handle to the core API.
+ */
+static struct GNUNET_CORE_Handle *handle;
+
+/**
+ * Identity of this peer.
+ */
+static struct GNUNET_PeerIdentity my_identity;
+        
+/**
+ * Linked list of all of our friends and all of our current
+ * neighbours.
+ */
+static struct FriendList *friends;
+
+/**
+ * Flag to disallow non-friend connections (pure F2F mode).
+ */
+static int friends_only;
+
+/**
+ * Minimum number of friends to have in the
+ * connection set before we allow non-friends.
+ */
+static unsigned int minimum_friend_count;
+
+/**
+ * Number of peers (friends and others) that we are currently connected to.
+ */
+static unsigned int connection_count;
+ 
+/**
+ * Target number of connections.
+ */
+static unsigned int target_connection_count;
+ 
+/**
+ * Number of friends that we are currently connected to.
+ */
+static unsigned int friend_count;
+ 
+/**
+ * Should the topology daemon try to establish connections?
+ */
+static int autoconnect;
+
+
+
+/**
+ * Force a disconnect from the specified peer.
+ */
+static void
+force_disconnect (const struct GNUNET_PeerIdentity *peer)
+{
+  GNUNET_CORE_peer_configure (handle,
+                             peer,
+                             GNUNET_TIME_UNIT_FOREVER_REL,
+                             0,
+                             0,
+                             0,
+                             NULL,
+                             NULL);
+}
+
+
+/**
+ * Function called by core when our attempt to connect
+ * succeeded.  Does nothing.
+ */
+static size_t 
+ready_callback (void *cls,
+               size_t size, void *buf)
+{
+  return 0;
+}
+
+
+/**
+ * Try to connect to the specified peer.
+ *
+ * @param pos NULL if not in friend list yet
+ */
+static void
+attempt_connect (const struct GNUNET_PeerIdentity *peer,
+                struct FriendList *pos)
+{
+  /* FIXME: do blacklist! */
+  GNUNET_CORE_notify_transmit_ready (handle,
+                                    0 /* priority */,
+                                    GNUNET_TIME_UNIT_MINUTES,
+                                    peer,
+                                    sizeof(struct GNUNET_MessageHeader),
+                                    &ready_callback,
+                                    NULL);
+}
+
+
+/**
+ * Is this peer one of our friends?
+ */
+static int
+is_friend (const struct GNUNET_PeerIdentity * peer)
+{
+  struct FriendList *pos;
+
+  pos = friends;
+  while (pos != NULL)
+    {
+      if ( (GNUNET_YES == pos->is_friend) &&
+          (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
+       return GNUNET_YES;
+      pos = pos->next;
+    }
+  return GNUNET_NO;
+}
+
+
+/**
+ * Check if an additional connection from the given peer is allowed.
+ */
+static int
+is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
+{
+  if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
+    return GNUNET_SYSERR;       /* disallow connections to self */
+  if (is_friend (peer))
+    return GNUNET_OK;
+  if (GNUNET_YES == friends_only)
+    return GNUNET_SYSERR;
+  if (friend_count >= minimum_friend_count)
+    return GNUNET_OK;
+  return GNUNET_SYSERR;
+}
+
+
+/**
+ * Method called whenever a peer connects.
+ *
+ * @param cls closure
+ * @param peer peer identity this notification is about
+ */
+static void connect_notify (void *cls,
+                           const struct
+                           GNUNET_PeerIdentity * peer)
+{
+  struct FriendList *pos;
+
+  connection_count++;
+  pos = friends;
+  while (pos != NULL)
+    {
+      if ( (GNUNET_YES == pos->is_friend) &&
+          (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
+       {
+         GNUNET_assert (GNUNET_NO == pos->is_connected);
+         pos->is_connected = GNUNET_YES;
+         friend_count++;         
+         return;
+       }
+      pos = pos->next;
+    }
+  pos = GNUNET_malloc (sizeof(struct FriendList));
+  pos->id = *peer;
+  pos->is_connected = GNUNET_YES;
+  pos->next = friends;
+  friends = pos;
+  if (GNUNET_OK != is_connection_allowed (peer))
+    force_disconnect (peer);
+}
+
+
+/**
+ * Disconnect from all non-friends (we're below quota).
+ */
+static void 
+drop_non_friends () 
+{
+  struct FriendList *pos;
+
+  pos = friends;
+  while (pos != NULL)
+    {
+      if (GNUNET_NO == pos->is_friend)
+       {
+         GNUNET_assert (GNUNET_YES == pos->is_connected);
+         force_disconnect (&pos->id);
+       }
+      pos = pos->next;
+    }
+}
+
+
+/**
+ * Method called whenever a peer disconnects.
+ *
+ * @param cls closure
+ * @param peer peer identity this notification is about
+ */
+static void disconnect_notify (void *cls,
+                              const struct
+                              GNUNET_PeerIdentity * peer)
+{
+  struct FriendList *pos;
+  struct FriendList *prev;
+
+  connection_count--;
+  pos = friends;
+  prev = NULL;
+  while (pos != NULL)
+    {
+      if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
+       {
+         GNUNET_assert (GNUNET_YES == pos->is_connected);
+         pos->is_connected = GNUNET_NO;
+         if (GNUNET_YES == pos->is_friend)
+           {
+             friend_count--;
+             if (friend_count < minimum_friend_count)
+               {
+                 /* disconnect from all non-friends */
+                 drop_non_friends ();
+                 attempt_connect (peer, pos);
+               }
+           }
+         else
+           {
+             /* free entry */
+             if (prev == NULL)
+               friends = pos->next;
+             else
+               prev->next = pos->next;
+             GNUNET_free (pos);
+           }
+         return;
+       }
+      prev = pos;
+      pos = pos->next;
+    } 
+  GNUNET_break (0);
+}
+
+/**
+ * Find more peers that we should connect to and ask the
+ * core to establish connections.
+ */
+static void
+find_more_peers (void *cls,
+                const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Determine when we should try again to find more peers and
+ * schedule the task.
+ */ 
+static void
+schedule_peer_search ()
+{
+  struct GNUNET_TIME_Relative delay;
+  
+  /* FIXME: calculate reasonable delay here */
+  delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
+                                        42);
+  GNUNET_SCHEDULER_add_delayed (sched,
+                               GNUNET_NO,
+                               GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                               GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
+                               delay,
+                               &find_more_peers,
+                               NULL);
+}
+
+
+/**
+ * Peerinfo calls this function to let us know about a
+ * possible peer that we might want to connect to.
+ */
+static void
+process_peer (void *cls,
+             const struct GNUNET_PeerIdentity * peer,
+             const struct GNUNET_HELLO_Message * hello,
+             uint32_t trust)
+{
+  struct FriendList *pos;
+
+  if (peer == NULL)
+    {
+      /* last call, schedule 'find_more_peers' again... */
+      schedule_peer_search ();
+      return;
+    }
+  if (hello == NULL)
+    {
+      /* no HELLO known; can not connect, ignore! */
+      return;
+    }
+  if (0 == memcmp (&my_identity,
+                   peer, sizeof (struct GNUNET_PeerIdentity)))
+    return;  /* that's me! */
+
+  pos = friends;
+  while (pos != NULL)
+    {
+      if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
+       {
+         if (GNUNET_YES == pos->is_connected)
+           return;
+         /* FIXME: check blacklisted... */
+         if (GNUNET_YES == pos->is_friend)
+           {
+             attempt_connect (peer, pos);
+             return;
+           }
+       }
+      pos = pos->next;
+    }
+  if (GNUNET_YES == friends_only)
+    return;
+  if (friend_count < minimum_friend_count)
+    return;
+  attempt_connect (peer, NULL);
+}
+
+
+/**
+ * Try to add more friends to our connection set.
+ */
+static void
+try_add_friends ()
+{
+  struct FriendList *pos;
+
+  pos = friends;
+  while (pos != NULL)
+    {
+      /* FIXME: check friends for blacklisting... */
+      if ( (GNUNET_YES == pos->is_friend) &&
+          (GNUNET_YES != pos->is_connected) )
+       attempt_connect (&pos->id, pos);
+      pos = pos->next;
+    }
+}
+
+
+/**
+ * Find more peers that we should connect to and ask the
+ * core to establish connections.
+ */
+static void
+find_more_peers (void *cls,
+                const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  if (target_connection_count <= connection_count)
+    {
+      schedule_peer_search ();
+      return;
+    }
+  if ( (GNUNET_YES == friends_only) ||
+       (friend_count < minimum_friend_count) )
+    {
+      try_add_friends ();
+      schedule_peer_search ();
+      return;
+    }
+  GNUNET_PEERINFO_for_all (cfg,
+                          sched,
+                          NULL,
+                          0, GNUNET_TIME_UNIT_FOREVER_REL,
+                          &process_peer, NULL);
+}
+
+
+/**
+ * Function called after GNUNET_CORE_connect has succeeded
+ * (or failed for good).
+ *
+ * @param cls closure
+ * @param server handle to the server, NULL if we failed
+ * @param my_id ID of this peer, NULL if we failed
+ * @param publicKey public key of this peer, NULL if we failed
+ */
+static void
+core_init (void *cls,
+          struct GNUNET_CORE_Handle * server,
+          const struct GNUNET_PeerIdentity *
+          my_id,
+          const struct
+          GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
+          publicKey)
+{
+  if (server == NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Failed to connect to core service, can not manage 
topology!\n"));
+      return; 
+    }
+  handle = server;
+  my_identity = *my_id;
+  if (autoconnect)
+    GNUNET_SCHEDULER_add_delayed (sched,
+                                 GNUNET_NO,
+                                 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+                                 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
+                                 GNUNET_TIME_UNIT_SECONDS /* give core time to 
tell us about existing connections */,
+                                 &find_more_peers,
+                                 NULL);
+}
+
+
+/**
  * gnunet-daemon-topology command line options.
  */
 static struct GNUNET_GETOPT_CommandLineOption options[] = {
@@ -42,23 +502,163 @@
 };
 
 
+/**
+ * Read the friends file.
+ */
+static void
+read_friends_file (struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+  char *fn;
+  char *data;
+  size_t pos;
+  GNUNET_HashCode hc;
+  struct stat frstat;
+  struct GNUNET_CRYPTO_HashAsciiEncoded enc;
+  unsigned int entries_found;
+  struct FriendList *fl;
 
+  fn = NULL;
+  GNUNET_CONFIGURATION_get_value_filename (cfg,
+                                          "TOPOLOGY",
+                                          "FRIENDS",
+                                          &fn);
+  if (GNUNET_OK != GNUNET_DISK_file_test (fn))
+    GNUNET_DISK_file_write (fn, NULL, 0, "600");
+  if (0 != STAT (fn, &frstat))
+    {
+      if ((friends_only) || (minimum_friend_count > 0))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                     _("Could not read friends list `%s'\n"), fn);
+         GNUNET_free (fn);
+          return;
+        }
+    }
+  if (frstat.st_size == 0)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Friends file `%s' is empty.\n"),
+                 fn);
+      GNUNET_free (fn);
+      return; 
+    }
+  data = GNUNET_malloc_large (frstat.st_size);
+  if (frstat.st_size !=
+      GNUNET_DISK_file_read (fn, frstat.st_size, data))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Failed to read friends list from `%s'\n"), fn);
+      GNUNET_free (fn);
+      GNUNET_free (data);
+      return;
+    }
+  entries_found = 0;
+  pos = 0;
+  while ((pos < frstat.st_size) && isspace (data[pos]))
+    pos++;
+  while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
+        (pos <= frstat.st_size - sizeof (struct 
GNUNET_CRYPTO_HashAsciiEncoded)))
+    {
+      memcpy (&enc, &data[pos], sizeof (struct 
GNUNET_CRYPTO_HashAsciiEncoded));
+      if (!isspace (enc.encoding[sizeof (struct 
GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                     _("Syntax error in topology specification at offset %llu, 
skipping bytes.\n"),
+                     (unsigned long long) pos);
+         pos++;
+         while ((pos < frstat.st_size) && (!isspace (data[pos])))
+           pos++;
+         continue;
+       }
+      enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
+      if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
+       {
+         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                     _("Syntax error in topology specification at offset %llu, 
skipping bytes `%s'.\n"),
+                     (unsigned long long) pos,
+                     &enc);
+       }
+      else
+       {
+         entries_found++;
+         fl = GNUNET_malloc (sizeof(struct FriendList));
+         fl->is_friend = GNUNET_YES;
+         fl->id.hashPubKey = hc;
+         fl->next = friends;
+         friends = fl;
+       }
+      pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
+      while ((pos < frstat.st_size) && isspace (data[pos]))
+       pos++;
+    }
+  GNUNET_free (data);
+  GNUNET_free (fn);
+  if ( (minimum_friend_count > entries_found) &&
+       (friends_only == GNUNET_NO) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("Fewer friends specified than required by minimum friend 
count. Will only connect to friends.\n"));
+    }
+  if ( (minimum_friend_count > target_connection_count) &&
+       (friends_only == GNUNET_NO) )
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                 _("More friendly connections required than target total 
number of connections.\n"));
+    }
+}
+
+
 /**
  * Main function that will be run.
  *
  * @param cls closure
- * @param sched the scheduler to use
+ * @param s the scheduler to use
  * @param args remaining command-line arguments
  * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
- * @param cfg configuration
+ * @param c configuration
  */
 static void 
 run (void *cls,
-     struct GNUNET_SCHEDULER_Handle * sched,
+     struct GNUNET_SCHEDULER_Handle * s,
      char *const *args,
      const char *cfgfile,
-     struct GNUNET_CONFIGURATION_Handle * cfg)
+     struct GNUNET_CONFIGURATION_Handle * c)
 {
+  struct GNUNET_CORE_MessageHandler handlers[] = 
+    {
+      { NULL, 0, 0 }
+    };
+  unsigned long long opt;
+
+  sched = s;
+  cfg = c;
+  autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                                     "TOPOLOGY",
+                                                     "AUTOCONNECT"); 
+  friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
+                                                      "TOPOLOGY",
+                                                      "FRIENDS-ONLY");
+  opt = 0;
+  GNUNET_CONFIGURATION_get_value_number (cfg,
+                                        "TOPOLOGY",
+                                        "MINIMUM-FRIENDS",
+                                        &opt);
+  minimum_friend_count = (unsigned int) opt;
+
+  if ( (friends_only == GNUNET_YES) ||
+       (minimum_friend_count > 0) )
+    read_friends_file (cfg);
+  GNUNET_CORE_connect (sched,
+                      cfg,
+                      GNUNET_TIME_UNIT_FOREVER_REL,
+                      NULL,
+                      &core_init,
+                      &connect_notify,
+                      &disconnect_notify,
+                      NULL,
+                      NULL, GNUNET_NO,
+                      NULL, GNUNET_NO,
+                      handlers);
 }
 
 





reply via email to

[Prev in Thread] Current Thread [Next in Thread]