respect path tracking and demultiplex everywhere options
[oweals/gnunet.git] / src / dht / gnunet-service-dht_routing.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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  * @file dht/gnunet-service-dht_routing.c
23  * @brief GNUnet DHT tracking of requests for routing replies
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet-service-dht_neighbours.h"
28 #include "gnunet-service-dht_routing.h"
29 #include "gnunet-service-dht.h"
30
31
32 /**
33  * Number of requests we track at most (for routing replies).
34  */
35 #define DHT_MAX_RECENT (1024 * 16)
36
37
38 /**
39  * Information we keep about all recent GET requests
40  * so that we can route replies.
41  */
42 struct RecentRequest
43 {
44
45   /**
46    * The peer this request was received from.
47    */
48   struct GNUNET_PeerIdentity peer;
49
50   /**
51    * Key of this request.
52    */
53   GNUNET_HashCode key;
54
55   /**
56    * Position of this node in the min heap.
57    */
58   struct GNUNET_CONTAINER_HeapNode *heap_node;
59
60   /**
61    * Bloomfilter for replies to drop.
62    */
63   struct GNUNET_CONTAINER_BloomFilter *reply_bf;
64
65   /**
66    * Type of the requested block.
67    */
68   enum GNUNET_BLOCK_Type type;
69   
70   /**
71    * extended query (see gnunet_block_lib.h).  Allocated at the
72    * end of this struct.
73    */
74   const void *xquery;
75
76   /**
77    * Number of bytes in xquery.
78    */
79   size_t xquery_size;
80
81   /**
82    * Mutator value for the reply_bf, see gnunet_block_lib.h
83    */
84   uint32_t reply_bf_mutator;
85
86   /**
87    * Request options.
88    */
89   enum GNUNET_DHT_RouteOption options;
90
91 };
92
93
94 /**
95  * Recent requests by time inserted.
96  */
97 static struct GNUNET_CONTAINER_Heap *recent_heap;
98
99 /**
100  * Recently seen requests by key.
101  */
102 static struct GNUNET_CONTAINER_MultiHashMap *recent_map;
103
104
105 /**
106  * Closure for the 'process' function.
107  */
108 struct ProcessContext
109 {
110   /**
111    * Path of the original PUT
112    */
113   const struct GNUNET_PeerIdentity *put_path;
114
115   /**
116    * Path of the reply.
117    */ 
118   const struct GNUNET_PeerIdentity *get_path;
119
120   /**
121    * Payload of the reply.
122    */
123   const void *data;
124
125   /**
126    * Expiration time of the result.
127    */
128   struct GNUNET_TIME_Absolute expiration_time;
129
130   /**
131    * Number of entries in 'put_path'.
132    */
133   unsigned int put_path_length;
134
135   /**
136    * Number of entries in 'get_path'.
137    */
138   unsigned int get_path_length;
139
140   /**
141    * Number of bytes in 'data'.
142    */
143   size_t data_size;
144
145   /**
146    * Type of the reply.
147    */
148   enum GNUNET_BLOCK_Type type;
149
150 };
151
152
153 /**
154  * Forward the result to the given peer if it matches the request.
155  *
156  * @param cls the 'struct ProcessContext' with the result
157  * @param key the query
158  * @param value the 'struct RecentRequest' with the request
159  * @return GNUNET_OK (continue to iterate), 
160  *         GNUNET_SYSERR if the result is malformed or type unsupported
161  */
162 static int
163 process (void *cls,
164          const GNUNET_HashCode *key,
165          void *value)
166 {
167   struct ProcessContext *pc = cls;
168   struct RecentRequest *rr = value;
169   enum GNUNET_BLOCK_EvaluationResult eval;
170   unsigned int gpl;
171   unsigned int ppl;
172
173   if ( (rr->type != GNUNET_BLOCK_TYPE_ANY) &&
174        (rr->type != pc->type) )
175     return GNUNET_OK; /* type missmatch */
176
177   if (0 != (rr->options & GNUNET_DHT_RO_RECORD_ROUTE))
178   {
179     gpl = pc->get_path_length;
180     ppl = pc->put_path_length;
181   }
182   else
183   {
184     gpl = 0;
185     ppl = 0;
186   }
187   eval = GNUNET_BLOCK_evaluate (GDS_block_context,
188                                 pc->type,
189                                 key,
190                                 &rr->reply_bf,
191                                 rr->reply_bf_mutator,
192                                 rr->xquery,
193                                 rr->xquery_size,
194                                 pc->data,
195                                 pc->data_size);
196   switch (eval)
197   {
198   case GNUNET_BLOCK_EVALUATION_OK_MORE:
199   case GNUNET_BLOCK_EVALUATION_OK_LAST:
200     GDS_NEIGHBOURS_handle_reply (&rr->peer,
201                                  pc->type,
202                                  pc->expiration_time,
203                                  key,
204                                  ppl,
205                                  pc->put_path,
206                                  gpl,
207                                  pc->get_path,
208                                  pc->data,
209                                  pc->data_size);
210     break;
211   case GNUNET_BLOCK_EVALUATION_OK_DUPLICATE:
212     return GNUNET_OK;
213   case GNUNET_BLOCK_EVALUATION_RESULT_INVALID:
214     GNUNET_break_op (0);
215     return GNUNET_SYSERR;
216   case GNUNET_BLOCK_EVALUATION_REQUEST_VALID:
217   case GNUNET_BLOCK_EVALUATION_REQUEST_INVALID:
218     GNUNET_break (0);
219     return GNUNET_OK;
220   case GNUNET_BLOCK_EVALUATION_TYPE_NOT_SUPPORTED:
221     return GNUNET_SYSERR;
222   default:
223     GNUNET_break (0);
224     return GNUNET_SYSERR;  
225   }
226   return GNUNET_OK;
227 }
228
229
230 /**
231  * Handle a reply (route to origin).  Only forwards the reply back to
232  * other peers waiting for it.  Does not do local caching or
233  * forwarding to local clients.  Essentially calls
234  * GDS_NEIGHBOURS_handle_reply for all peers that sent us a matching
235  * request recently.
236  *
237  * @param type type of the block
238  * @param expiration_time when does the content expire
239  * @param key key for the content
240  * @param put_path_length number of entries in put_path
241  * @param put_path peers the original PUT traversed (if tracked)
242  * @param get_path_length number of entries in put_path
243  * @param get_path peers this reply has traversed so far (if tracked)
244  * @param data payload of the reply
245  * @param data_size number of bytes in data
246  */
247 void
248 GDS_ROUTING_process (enum GNUNET_BLOCK_Type type,
249                      struct GNUNET_TIME_Absolute expiration_time,
250                      const GNUNET_HashCode *key,
251                      unsigned int put_path_length,
252                      const struct GNUNET_PeerIdentity *put_path,
253                      unsigned int get_path_length,
254                      const struct GNUNET_PeerIdentity *get_path,
255                      const void *data,
256                      size_t data_size)
257 {
258   struct ProcessContext pc;
259
260   pc.type = type;
261   pc.expiration_time = expiration_time;
262   pc.put_path_length = put_path_length;
263   pc.put_path = put_path;
264   pc.get_path_length = get_path_length;
265   pc.get_path = get_path;
266   pc.data = data;
267   pc.data_size = data_size;
268   GNUNET_CONTAINER_multihashmap_get_multiple (recent_map,
269                                               key,
270                                               &process,
271                                               &pc);
272 }
273
274
275 /**
276  * Add a new entry to our routing table.
277  *
278  * @param sender peer that originated the request
279  * @param type type of the block
280  * @param options options for processing
281  * @param key key for the content
282  * @param xquery extended query
283  * @param xquery_size number of bytes in xquery
284  * @param reply_bf bloomfilter to filter duplicates
285  * @param reply_bf_mutator mutator for reply_bf
286 */
287 void
288 GDS_ROUTING_add (const struct GNUNET_PeerIdentity *sender,
289                  enum GNUNET_BLOCK_Type type,
290                  enum GNUNET_DHT_RouteOption options,
291                  const GNUNET_HashCode *key,
292                  const void *xquery,
293                  size_t xquery_size,
294                  const struct GNUNET_CONTAINER_BloomFilter *reply_bf,
295                  uint32_t reply_bf_mutator)
296 {
297   struct RecentRequest *recent_req;
298
299   while (GNUNET_CONTAINER_heap_get_size (recent_heap) >= DHT_MAX_RECENT)
300   {
301     recent_req = GNUNET_CONTAINER_heap_peek (recent_heap);
302     GNUNET_assert (recent_req != NULL);
303     GNUNET_CONTAINER_heap_remove_node (recent_req->heap_node);
304     GNUNET_CONTAINER_bloomfilter_free (recent_req->reply_bf);
305     GNUNET_free (recent_req);
306   }
307
308   recent_req = GNUNET_malloc (sizeof (struct RecentRequest) + xquery_size);
309   recent_req->peer = *sender;
310   recent_req->key = *key;
311   recent_req->heap_node =
312     GNUNET_CONTAINER_heap_insert (recent_heap, recent_req,
313                                   GNUNET_TIME_absolute_get ().abs_value);
314   recent_req->reply_bf =
315     GNUNET_CONTAINER_bloomfilter_copy (reply_bf);
316   recent_req->type = type;
317   recent_req->options = options;
318   recent_req->xquery = &recent_req[1];
319   recent_req->xquery_size = xquery_size;
320   recent_req->reply_bf_mutator = reply_bf_mutator;
321   GNUNET_CONTAINER_multihashmap_put (recent_map,
322                                      key,
323                                      recent_req,
324                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
325
326
327 }
328
329
330 /**
331  * Initialize routing subsystem.
332  */
333 void
334 GDS_ROUTING_init ()
335 {
336   recent_heap =
337     GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
338   recent_map =
339       GNUNET_CONTAINER_multihashmap_create (DHT_MAX_RECENT * 4 / 3);
340 }
341
342
343 /**
344  * Shutdown routing subsystem.
345  */
346 void
347 GDS_ROUTING_done ()
348 {
349   struct RecentRequest *recent_req;
350
351   while (GNUNET_CONTAINER_heap_get_size (recent_heap) > 0)
352   {
353     recent_req = GNUNET_CONTAINER_heap_peek (recent_heap);
354     GNUNET_assert (recent_req != NULL);
355     GNUNET_CONTAINER_heap_remove_node (recent_req->heap_node);
356     GNUNET_CONTAINER_bloomfilter_free (recent_req->reply_bf);
357     GNUNET_free (recent_req);
358   }
359   GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (recent_heap));
360   GNUNET_CONTAINER_heap_destroy (recent_heap);
361   recent_heap = NULL;
362   GNUNET_CONTAINER_multihashmap_destroy (recent_map);
363   recent_map = NULL;
364 }
365
366 /* end of gnunet-service-dht_routing.c */