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