clean up for configs
[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  * FIXME refactor and use build_path_from_peer_ids
115  */
116 static struct MeshPeerPath *
117 path_build_from_dht (const struct GNUNET_PeerIdentity *get_path,
118                      unsigned int get_path_length,
119                      const struct GNUNET_PeerIdentity *put_path,
120                      unsigned int put_path_length)
121 {
122   struct MeshPeerPath *p;
123   GNUNET_PEER_Id id;
124   int i;
125
126   p = path_new (1);
127   p->peers[0] = myid;
128   GNUNET_PEER_change_rc (myid, 1);
129   i = get_path_length;
130   LOG (GNUNET_ERROR_TYPE_DEBUG, "   GET has %d hops.\n", i);
131   for (i--; i >= 0; i--)
132   {
133     id = GNUNET_PEER_intern (&get_path[i]);
134     if (p->length > 0 && id == p->peers[p->length - 1])
135     {
136       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
137       GNUNET_PEER_change_rc (id, -1);
138     }
139     else
140     {
141       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from GET: %s.\n",
142                   GNUNET_i2s (&get_path[i]));
143       p->length++;
144       p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
145       p->peers[p->length - 1] = id;
146     }
147   }
148   i = put_path_length;
149   LOG (GNUNET_ERROR_TYPE_DEBUG, "   PUT has %d hops.\n", i);
150   for (i--; i >= 0; i--)
151   {
152     id = GNUNET_PEER_intern (&put_path[i]);
153     if (id == myid)
154     {
155       /* PUT path went through us, so discard the path up until now and start
156        * from here to get a much shorter (and loop-free) path.
157        */
158       path_destroy (p);
159       p = path_new (0);
160     }
161     if (p->length > 0 && id == p->peers[p->length - 1])
162     {
163       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Optimizing 1 hop out.\n");
164       GNUNET_PEER_change_rc (id, -1);
165     }
166     else
167     {
168       LOG (GNUNET_ERROR_TYPE_DEBUG, "   Adding from PUT: %s.\n",
169                   GNUNET_i2s (&put_path[i]));
170       p->length++;
171       p->peers = GNUNET_realloc (p->peers, sizeof (GNUNET_PEER_Id) * p->length);
172       p->peers[p->length - 1] = id;
173     }
174   }
175 #if MESH_DEBUG
176   if (get_path_length > 0)
177     LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of GET: %s)\n",
178                 GNUNET_i2s (&get_path[0]));
179   if (put_path_length > 0)
180     LOG (GNUNET_ERROR_TYPE_DEBUG, "   (first of PUT: %s)\n",
181                 GNUNET_i2s (&put_path[0]));
182   LOG (GNUNET_ERROR_TYPE_DEBUG, "   In total: %d hops\n",
183               p->length);
184   for (i = 0; i < p->length; i++)
185   {
186     struct GNUNET_PeerIdentity peer_id;
187
188     GNUNET_PEER_resolve (p->peers[i], &peer_id);
189     LOG (GNUNET_ERROR_TYPE_DEBUG, "       %u: %s\n", p->peers[i],
190                 GNUNET_i2s (&peer_id));
191   }
192 #endif
193   return p;
194 }
195
196
197 /**
198  * Function to process paths received for a new peer addition. The recorded
199  * paths form the initial tunnel, which can be optimized later.
200  * Called on each result obtained for the DHT search.
201  *
202  * @param cls closure
203  * @param exp when will this value expire
204  * @param key key of the result
205  * @param get_path path of the get request
206  * @param get_path_length lenght of get_path
207  * @param put_path path of the put request
208  * @param put_path_length length of the put_path
209  * @param type type of the result
210  * @param size number of bytes in data
211  * @param data pointer to the result data
212  */
213 static void
214 dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp,
215                     const struct GNUNET_HashCode * key,
216                     const struct GNUNET_PeerIdentity *get_path,
217                     unsigned int get_path_length,
218                     const struct GNUNET_PeerIdentity *put_path,
219                     unsigned int put_path_length, enum GNUNET_BLOCK_Type type,
220                     size_t size, const void *data)
221 {
222   struct GMD_search_handle *h = cls;
223   struct MeshPeerPath *p;
224
225   LOG (GNUNET_ERROR_TYPE_DEBUG, "Got results!\n");
226   p = path_build_from_dht (get_path, get_path_length,
227                            put_path, put_path_length);
228   h->callback (h->cls, p);
229   path_destroy (p);
230   return;
231 }
232
233
234 /**
235  * Periodically announce self id in the DHT
236  *
237  * @param cls closure
238  * @param tc task context
239  */
240 static void
241 announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
242 {
243   struct PBlock block;
244   struct GNUNET_HashCode phash;
245
246   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
247   {
248     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
249     return;
250   }
251
252   /* TODO
253    * - Set data expiration in function of X
254    * - Adapt X to churn
255    */
256   block.id = my_full_id;
257   GNUNET_CRYPTO_hash (&my_full_id, sizeof (struct GNUNET_PeerIdentity), &phash);
258   GNUNET_DHT_put (dht_handle,   /* DHT handle */
259                   &phash,       /* Key to use */
260                   dht_replication_level,     /* Replication level */
261                   GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,    /* DHT options */
262                   GNUNET_BLOCK_TYPE_MESH_PEER,       /* Block type */
263                   sizeof (block),  /* Size of the data */
264                   (const char *) &block, /* Data itself */
265                   GNUNET_TIME_UNIT_FOREVER_ABS,  /* Data expiration */
266                   GNUNET_TIME_UNIT_FOREVER_REL, /* Retry time */
267                   NULL,         /* Continuation */
268                   NULL);        /* Continuation closure */
269   announce_id_task =
270       GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls);
271 }
272
273 /**
274  * Iterator over hash map entries and stop GET requests before disconnecting
275  * from the DHT.
276  *
277  * @param cls Closure (unused)
278  * @param key Current peer ID.
279  * @param value Value in the hash map (GMD_search_handle).
280  *
281  * @return #GNUNET_YES, we should continue to iterate,
282  */
283 int
284 stop_get (void *cls,
285           uint32_t key,
286           void *value)
287 {
288   struct GMD_search_handle *h = value;
289
290   GMD_search_stop (h);
291   return GNUNET_YES;
292 }
293
294
295 /******************************************************************************/
296 /********************************    API    ***********************************/
297 /******************************************************************************/
298
299 /**
300  * Initialize the DHT subsystem.
301  *
302  * @param c Configuration.
303  */
304 void
305 GMD_init (const struct GNUNET_CONFIGURATION_Handle *c)
306 {
307   LOG (GNUNET_ERROR_TYPE_DEBUG, "init\n");
308   if (GNUNET_OK !=
309       GNUNET_CONFIGURATION_get_value_number (c, "MESH", "DHT_REPLICATION_LEVEL",
310                                              &dht_replication_level))
311   {
312     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
313                                "MESH", "DHT_REPLICATION_LEVEL", "USING DEFAULT");
314     dht_replication_level = 3;
315   }
316
317   if (GNUNET_OK !=
318       GNUNET_CONFIGURATION_get_value_time (c, "MESH", "ID_ANNOUNCE_TIME",
319                                            &id_announce_time))
320   {
321     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
322                                "MESH", "ID_ANNOUNCE_TIME", "MISSING");
323     GNUNET_SCHEDULER_shutdown ();
324     return;
325   }
326
327   dht_handle = GNUNET_DHT_connect (c, 64);
328   if (NULL == dht_handle)
329   {
330     GNUNET_break (0);
331   }
332
333   announce_id_task = GNUNET_SCHEDULER_add_now (&announce_id, NULL);
334   get_requests = GNUNET_CONTAINER_multihashmap32_create (32);
335 }
336
337
338 /**
339  * Shut down the DHT subsystem.
340  */
341 void
342 GMD_shutdown (void)
343 {
344   GNUNET_CONTAINER_multihashmap32_iterate (get_requests, &stop_get, NULL);
345   GNUNET_CONTAINER_multihashmap32_destroy (get_requests);
346   if (dht_handle != NULL)
347   {
348     GNUNET_DHT_disconnect (dht_handle);
349     dht_handle = NULL;
350   }
351   if (GNUNET_SCHEDULER_NO_TASK != announce_id_task)
352   {
353     GNUNET_SCHEDULER_cancel (announce_id_task);
354     announce_id_task = GNUNET_SCHEDULER_NO_TASK;
355   }
356 }
357
358 struct GMD_search_handle *
359 GMD_search (const struct GNUNET_PeerIdentity *peer_id,
360             GMD_search_callback callback, void *cls)
361 {
362   struct GNUNET_HashCode phash;
363   struct GMD_search_handle *h;
364
365   LOG (GNUNET_ERROR_TYPE_DEBUG,
366        "  Starting DHT GET for peer %s\n", GNUNET_i2s (peer_id));
367   GNUNET_CRYPTO_hash (peer_id, sizeof (struct GNUNET_PeerIdentity), &phash);
368   h = GNUNET_new (struct GMD_search_handle);
369   h->peer_id = GNUNET_PEER_intern (peer_id);
370   h->callback = callback;
371   h->cls = cls;
372   h->dhtget = GNUNET_DHT_get_start (dht_handle,    /* handle */
373                                     GNUNET_BLOCK_TYPE_MESH_PEER, /* type */
374                                     &phash,     /* key to search */
375                                     dht_replication_level, /* replication level */
376                                     GNUNET_DHT_RO_RECORD_ROUTE |
377                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
378                                     NULL,       /* xquery */
379                                     0,     /* xquery bits */
380                                     &dht_get_id_handler, h);
381   GNUNET_CONTAINER_multihashmap32_put (get_requests, h->peer_id, h,
382                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
383   return h;
384 }
385
386 void
387 GMD_search_stop (struct GMD_search_handle *h)
388 {
389   GNUNET_break (GNUNET_OK ==
390                 GNUNET_CONTAINER_multihashmap32_remove (get_requests,
391                                                         h->peer_id, h));
392   GNUNET_DHT_get_stop (h->dhtget);
393   GNUNET_free (h);
394 }