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