- add peer's tunnel setter
[oweals/gnunet.git] / src / mesh / gnunet-service-mesh_dht.c
1 /*
2      This file is part of GNUnet.
3      (C) 2013 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 #include "platform.h"
23 #include "gnunet_util_lib.h"
24
25 #include "gnunet_dht_service.h"
26
27 #include "mesh_path.h"
28 #include "gnunet-service-mesh_dht.h"
29 #include "gnunet-service-mesh_peer.h"
30
31 #define LOG(level, ...) GNUNET_log_from (level,"mesh-dht",__VA_ARGS__)
32
33
34 /******************************************************************************/
35 /********************************   STRUCTS  **********************************/
36 /******************************************************************************/
37
38 /**
39  * Handle for DHT searches.
40  */
41 struct GMD_search_handle
42 {
43   /** DHT_GET handle. */
44   struct GNUNET_DHT_GetHandle *dhtget;
45
46   /** Provided callback to call when a path is found. */
47   GMD_search_callback callback;
48
49   /** Provided closure. */
50   void *cls;
51 };
52
53
54 /******************************************************************************/
55 /*******************************   GLOBALS  ***********************************/
56 /******************************************************************************/
57
58 /**
59  * Handle to use DHT.
60  */
61 static struct GNUNET_DHT_Handle *dht_handle;
62
63 /**
64  * How often to PUT own ID in the DHT.
65  */
66 static struct GNUNET_TIME_Relative id_announce_time;
67
68 /**
69  * DHT replication level, see DHT API: GNUNET_DHT_get_start, GNUNET_DHT_put.
70  */
71 static unsigned long long dht_replication_level;
72
73 /**
74  * Task to periodically announce itself in the network.
75  */
76 static GNUNET_SCHEDULER_TaskIdentifier announce_id_task;
77
78 /**
79  * Own ID (short value).
80  */
81 static GNUNET_PEER_Id short_id;
82
83 /**
84  * Own ID (full value).
85  */
86 static struct GNUNET_PeerIdentity *full_id;
87
88 /**
89  * Own private key.
90  */
91 static struct GNUNET_CRYPTO_EccPrivateKey *private_key;
92
93
94 /******************************************************************************/
95 /********************************   STATIC  ***********************************/
96 /******************************************************************************/
97
98
99 /**
100  * Build a PeerPath from the paths returned from the DHT, reversing the paths
101  * to obtain a local peer -> destination path and interning the peer ids.
102  *
103  * @return Newly allocated and created path
104  */
105 static struct MeshPeerPath *
106 path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
107                      unsigned int get_path_length,
108                      const struct GNUNET_PeerIdentity *put_path,
109                      unsigned int put_path_length)
110 {
111   struct MeshPeerPath *p;
112   GNUNET_PEER_Id id;
113   int i;
114
115   p = path_new (1);
116   p->peers[0] = myid;
117   GNUNET_PEER_change_rc (myid, 1);
118   i = get_path_length;
119   LOG (GNUNET_ERROR_TYPE_DEBUG, "   GET has %d hops.\n", i);
120   for (i--; i >= 0; i--)
121   {
122     id = GNUNET_PEER_intern (&get_path[i]);
123     if (p->length > 0 && id == p->peers[p->length - 1])
124     {
125       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
126       GNUNET_PEER_change_rc (id, -1);
127     }
128     else
129     {
130       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from GET: %s.\n",
131                   GNUNET_i2s (&get_path[i]));
132       p->length++;
133       p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
134       p->peers[p->length - 1] = id;
135     }
136   }
137   i = put_path_length;
138   LOG (GNUNET_ERROR_TYPE_DEBUG, "   PUT has %d hops.\n", i);
139   for (i--; i >= 0; i--)
140   {
141     id = GNUNET_PEER_intern (&put_path[i]);
142     if (id == myid)
143     {
144       /* PUT path went through us, so discard the path up until now and start
145        * from here to get a much shorter (and loop-free) path.
146        */
147       path_destroy (p);
148       p = path_new (0);
149     }
150     if (p->length > 0 && id == p->peers[p->length - 1])
151     {
152       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
153       GNUNET_PEER_change_rc (id, -1);
154     }
155     else
156     {
157       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from PUT: %s.\n",
158                   GNUNET_i2s (&put_path[i]));
159       p->length++;
160       p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
161       p->peers[p->length - 1] = id;
162     }
163   }
164 #if MESH_DEBUG
165   if (get_path_length > 0)
166     LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of GET: %s)\n",
167                 GNUNET_i2s (&get_path[0]));
168   if (put_path_length > 0)
169     LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of PUT: %s)\n",
170                 GNUNET_i2s (&put_path[0]));
171   LOG (GNUNET_ERROR_TYPE_DEBUG, "   In total: %d hops\n",
172               p->length);
173   for (i = 0; i < p->length; i++)
174   {
175     struct GNUNET_PeerIdentity peer_id;
176
177     GNUNET_PEER_resolve (p->peers[i], &peer_id);
178     LOG (GNUNET_ERROR_TYPE_DEBUG, "       %u: %s\n", p->peers[i],
179                 GNUNET_i2s (&peer_id));
180   }
181 #endif
182   return p;
183 }
184
185
186 /**
187  * Function to process paths received for a new peer addition. The recorded
188  * paths form the initial tunnel, which can be optimized later.
189  * Called on each result obtained for the DHT search.
190  *
191  * @param cls closure
192  * @param exp when will this value expire
193  * @param key key of the result
194  * @param get_path path of the get request
195  * @param get_path_length lenght of get_path
196  * @param put_path path of the put request
197  * @param put_path_length length of the put_path
198  * @param type type of the result
199  * @param size number of bytes in data
200  * @param data pointer to the result data
201  */
202 static void
203 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
204                     const struct GNUNET_HashCode * key,
205                     const struct GNUNET_PeerIdentity *get_path,
206                     unsigned int get_path_length,
207                     const struct GNUNET_PeerIdentity *put_path,
208                     unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
209                     size_t size, const void *data)
210 {
211   struct GMD_search_handle *h = cls;
212   struct MeshPeerPath *p;
213
214   LOG (GNUNET_ERROR_TYPE_DEBUG, "Got results!\n");
215   p = path_build_from_dht (get_path, get_path_length,
216                            put_path, put_path_length);
217   h->callback (h->cls, p);
218   path_destroy (p);
219   return;
220 }
221
222
223 /**
224  * Periodically announce self id in the DHT
225  *
226  * @param cls closure
227  * @param tc task context
228  */
229 static void
230 announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
231 {
232   struct PBlock block;
233   struct GNUNET_HashCode phash;
234
235   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
236   {
237     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
238     return;
239   }
240
241   /* TODO
242    * - Set data expiration in function of X
243    * - Adapt X to churn
244    */
245   block.id = *full_id;
246   GNUNET_CRYPTO_hash (full_id, sizeof (struct GNUNET_PeerIdentity), &phash);
247   GNUNET_DHT_put (dht_handle,   /* DHT handle */
248                   &phash,       /* Key to use */
249                   dht_replication_level,     /* Replication level */
250                   GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,    /* DHT options */
251                   GNUNET_BLOCK_TYPE_MESH_PEER,       /* Block type */
252                   sizeof (block),  /* Size of the data */
253                   (const char *) &block, /* Data itself */
254                   GNUNET_TIME_UNIT_FOREVER_ABS,  /* Data expiration */
255                   GNUNET_TIME_UNIT_FOREVER_REL, /* Retry time */
256                   NULL,         /* Continuation */
257                   NULL);        /* Continuation closure */
258   announce_id_task =
259       GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls);
260 }
261
262
263 /******************************************************************************/
264 /********************************    API    ***********************************/
265 /******************************************************************************/
266
267 /**
268  * Initialize the DHT subsystem.
269  *
270  * @param c Configuration.
271  * @param peer_id Local peer ID (must remain valid during all execution time).
272  */
273 void
274 GMD_init (const struct GNUNET_CONFIGURATION_Handle *c,
275           struct GNUNET_PeerIdentity *peer_id)
276 {
277   full_id = peer_id;
278   if (GNUNET_OK !=
279       GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DHT_REPLICATION_LEVEL",
280                                              &dht_replication_level))
281   {
282     LOG_config_invalid (GNUNET_ERROR_TYPE_WARNING,
283                                "MESH", "DHT_REPLICATION_LEVEL", "USING DEFAULT");
284     dht_replication_level = 3;
285   }
286
287   if (GNUNET_OK !=
288       GNUNET_CONFIGURATION_get_value_time (c, "MESH", "ID_ANNOUNCE_TIME",
289                                            &id_announce_time))
290   {
291     LOG_config_invalid (GNUNET_ERROR_TYPE_ERROR,
292                                "MESH", "ID_ANNOUNCE_TIME", "MISSING");
293     GNUNET_SCHEDULER_shutdown ();
294     return;
295   }
296
297   dht_handle = GNUNET_DHT_connect (c, 64);
298   if (NULL == dht_handle)
299   {
300     GNUNET_break (0);
301   }
302
303   announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL);
304 }
305
306
307 /**
308  * Shut down the DHT subsystem.
309  */
310 void
311 GMD_shutdown(void )
312 {
313   if (dht_handle != NULL)
314   {
315     GNUNET_DHT_disconnect (dht_handle);
316     dht_handle = NULL;
317   }
318   if (GNUNET_SCHEDULER_NO_TASK != announce_id_task)
319   {
320     GNUNET_SCHEDULER_cancel (announce_id_task);
321     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
322   }
323 }
324
325 struct GMD_search_handle *
326 GMD_search (const struct GNUNET_PeerIdentity *peer_id,
327             GMD_search_callback callback, void *cls)
328 {
329   struct GNUNET_HashCode phash;
330   struct GMD_search_handle *h;
331
332   LOG (GNUNET_ERROR_TYPE_DEBUG,
333        "  Starting DHT GET for peer %s\n", GNUNET_i2s (peer_id));
334   GNUNET_CRYPTO_hash (peer_id, sizeof (struct GNUNET_PeerIdentity), &phash);
335   h = GNUNET_new (struct GMD_search_handle);
336   h->cls = cls;
337   h->dhtget = GNUNET_DHT_get_start (dht_handle,    /* handle */
338                                     GNUNET_BLOCK_TYPE_MESH_PEER, /* type */
339                                     &phash,     /* key to search */
340                                     dht_replication_level, /* replication level */
341                                     GNUNET_DHT_RO_RECORD_ROUTE |
342                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
343                                     NULL,       /* xquery */
344                                     0,     /* xquery bits */
345                                     &dht_get_id_handler, h);
346   return h;
347 }
348
349 void
350 GMD_search_stop (struct GMD_search_handle *h)
351 {
352   GNUNET_DHT_get_stop (h->dhtget);
353   GNUNET_free (h);
354 }