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