WiP (add path to peer algorithm added)
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh.c
index f27291cb6386ef63a4734578a2082aa80fa7e324..df4f39eedb11e99d0eefb58212093ed010a06136 100644 (file)
@@ -371,7 +371,7 @@ get_peer_info (const struct GNUNET_PeerIdentity *peer)
 }
 
 /**
- * find the first peer whom to send a packet to go down this path
+ * Find the first peer whom to send a packet to go down this path
  * @param path The path to use
  * @return short id of the next peer, myid in case of local delivery,
  * or 0 in case of error
@@ -396,10 +396,66 @@ get_first_hop (struct MeshPath *path)
     return 0;
 }
 
+
+/**
+ * Get the cost of the path.
+ * @param path The path to analyze
+ * @return Number of hops to reach destination, UINT_MAX in case the peer is not
+ * in the path
+ */
+static unsigned int
+get_path_cost(struct MeshPath *path)
+{
+    unsigned int        i;
+
+    if (NULL == path) return UINT_MAX;
+    for (i = 0; i < path->length; i++) {
+        if (path->peers[i] == myid) {
+            return path->length - i;
+        }
+    }
+    return UINT_MAX;
+}
+
+
+/**
+ * Add the path to the peer and update the path used to reach it in case this
+ * is the shortest.
+ * @param peer_info Destination peer to add the path to.
+ * @param path New path to add. Last peer must be the peer in arg 1.
+ */
 static void
 add_path_to_peer(struct MeshPeerInfo *peer_info, struct MeshPath *path)
 {
-    
+    unsigned int        i;
+    unsigned int        new_cost;
+    unsigned int        best_cost;
+    struct MeshPath     *aux;
+    struct MeshPath     *best;
+
+    if (NULL == peer_info || NULL == path) return;
+
+    new_cost = get_path_cost(path);
+    best_cost = UINT_MAX;
+    best = NULL;
+    for (aux = peer_info->path; aux != NULL; aux = aux->next) {
+        if ((i = get_path_cost(aux)) < best_cost) {
+            best = aux;
+            best_cost = i;
+        }
+    }
+    if (best_cost < new_cost) {
+        path->in_use = 0;
+        GNUNET_CONTAINER_DLL_insert_tail(peer_info->path,
+                                         peer_info->path_tail,
+                                         path);
+    } else {
+        if (NULL != best) best->in_use = 0;
+        path->in_use = 1;
+        GNUNET_CONTAINER_DLL_insert(peer_info->path,
+                                    peer_info->path_tail,
+                                    path);
+    }
     return;
 }
 
@@ -458,7 +514,7 @@ retrieve_tunnel_by_pi (GNUNET_PEER_Id pi, MESH_TunnelNumber tid)
 }
 
 
-#if LATER
+
 /**
  * Search for a tunnel by global ID using full PeerIdentities
  * @param oid owner of the tunnel
@@ -483,7 +539,7 @@ retrieve_tunnel (struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid)
  * @return GNUNET_OK on success
  */
 static int
-destroy_path(struct MeshTunnel  *t, struct MeshPath *p)
+destroy_path(struct MeshPath *p)
 {
     GNUNET_PEER_decrement_rcs(p->peers, p->length);
     GNUNET_free(p->peers);
@@ -491,7 +547,7 @@ destroy_path(struct MeshTunnel  *t, struct MeshPath *p)
     return GNUNET_OK;
 }
 
-
+#if LATER
 /**
  * Destroy the peer_info and free any allocated resources linked to it
  * @param t tunnel the path belongs to
@@ -522,14 +578,17 @@ destroy_peer_info(struct MeshPeerInfo *pi)
  * @return GNUNET_OK on success
  */
 static int
-destroy_tunnel(struct MeshClient *c, struct MeshTunnel  *t)
+destroy_tunnel(struct MeshTunnel  *t)
 {
 //     struct MeshPath         *path;
-    GNUNET_HashCode         hash;
-    int                     r;
+    struct MeshClient           *c;
+    GNUNET_HashCode             hash;
+    int                         r;
 
     if (NULL == t) return GNUNET_OK;
 
+    c = t->client;
+
     GNUNET_CRYPTO_hash(&t->id, sizeof(struct MESH_TunnelID), &hash);
     if(GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove(tunnels, &hash, t)) {
         r = GNUNET_SYSERR;
@@ -667,11 +726,98 @@ handle_mesh_path_create (void *cls,
                               const struct GNUNET_TRANSPORT_ATS_Information
                               *atsi)
 {
-    /* Extract path */
-    /* Find origin & self */
-    /* Search for origin in local tunnels */
-    /* Create tunnel / add path */
-    /* Retransmit to next link in chain, if any (core_notify + callback) */
+    unsigned int                        own_pos;
+    uint16_t                            size;
+    uint16_t                            i;
+    MESH_TunnelNumber                   tid;
+    struct GNUNET_MESH_ManipulatePath   *msg;
+    struct GNUNET_PeerIdentity          *pi;
+    GNUNET_HashCode                     hash;
+    struct MeshPath                     *path;
+    struct MeshPeerInfo                 *peer_info;
+    struct MeshTunnel                   *t;
+
+
+    size = ntohs(message->size);
+    if (size < sizeof(struct GNUNET_MESH_ManipulatePath)) {
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
+                   "received create path message too short\n");
+        return GNUNET_OK;
+    }
+
+    size -= sizeof(struct GNUNET_MESH_ManipulatePath);
+    if (size < 2 * sizeof(struct GNUNET_PeerIdentity)) {
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
+                   "create path message lacks enough peers\n");
+        return GNUNET_OK;
+    }
+    if (size % sizeof(struct GNUNET_PeerIdentity)) {
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
+                   "create path message of wrong size\n");
+        return GNUNET_OK;
+    }
+    msg = (struct GNUNET_MESH_ManipulatePath *) message;
+    size /= sizeof(struct GNUNET_PeerIdentity);
+
+    tid = ntohl(msg->tid);
+    pi = (struct GNUNET_PeerIdentity *) &msg[1];
+    t = retrieve_tunnel(pi, tid);
+
+    if (NULL == t) {
+        t = GNUNET_malloc(sizeof(struct MeshTunnel));
+        t->id.oid = GNUNET_PEER_intern(pi);
+        t->id.tid = tid;
+        t->local_tid = 0;
+        t->client = NULL;
+        t->peers = GNUNET_CONTAINER_multihashmap_create(32);
+
+        GNUNET_CRYPTO_hash(&t->id, sizeof(struct MESH_TunnelID), &hash);
+        if (GNUNET_OK !=
+            GNUNET_CONTAINER_multihashmap_put(tunnels,
+                            &hash,
+                            t,
+                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+        {
+            GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
+                   "create path: could not store tunnel in hashmap\n");
+            return GNUNET_OK;
+        }
+
+    }
+    peer_info = GNUNET_CONTAINER_multihashmap_get(peers,
+                                                  &pi[size - 1].hashPubKey);
+    if (NULL == peer_info) {
+        peer_info = GNUNET_malloc(sizeof(struct MeshPeerInfo));
+        peer_info->id = GNUNET_PEER_intern(&pi[size - 1]);
+        peer_info->state = MESH_PEER_WAITING;
+        GNUNET_CONTAINER_multihashmap_put(peers,
+                            &pi[size - 1].hashPubKey,
+                            peer_info,
+                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+    }
+
+    path = GNUNET_malloc(sizeof(struct MeshPath));
+    path->length = size;
+    path->peers = GNUNET_malloc(size * sizeof(GNUNET_PEER_Id));
+    own_pos = 0;
+    for (i = 0; i < size; i++) {
+        path->peers[i] = GNUNET_PEER_intern(&pi[i]);
+        if (path->peers[i] == myid) own_pos = i;
+    }
+    if (own_pos == 0) { /* cannot be self, must be 'not found' */
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
+                   "create path: self not found in path through self\n");
+        destroy_path(path);
+        /* FIXME destroy tunnel? leave for timeout? */
+        return 0;
+    }
+    if (own_pos == size - 1) { /* it is for us! */
+        destroy_path(path); /* not needed anymore */
+        /* TODO: send ack? new meesage type? */
+    } else {
+        add_path_to_peer(peer_info, path);
+        /* TODO: Retransmit to next link in chain, if any (core_notify + callback) */
+    }
     return GNUNET_OK;
 }
 
@@ -730,7 +876,7 @@ static struct GNUNET_CORE_MessageHandler core_handlers[] = {
 static int
 delete_tunnel_entry (void *cls, const GNUNET_HashCode * key, void *value) {
     int r;
-    r = destroy_tunnel((struct MeshClient *) cls, (struct MeshTunnel *) value);
+    r = destroy_tunnel((struct MeshTunnel *) value);
     return r;
 }