finish logic to maintain paths
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet-new_paths.c
1
2 /*
3      This file is part of GNUnet.
4      Copyright (C) 2001-2017 GNUnet e.V.
5
6      GNUnet is free software; you can redistribute it and/or modify
7      it under the terms of the GNU General Public License as published
8      by the Free Software Foundation; either version 3, or (at your
9      option) any later version.
10
11      GNUnet is distributed in the hope that it will be useful, but
12      WITHOUT ANY WARRANTY; without even the implied warranty of
13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14      General Public License for more details.
15
16      You should have received a copy of the GNU General Public License
17      along with GNUnet; see the file COPYING.  If not, write to the
18      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19      Boston, MA 02110-1301, USA.
20 */
21
22 /**
23  * @file cadet/gnunet-service-cadet-new_paths.c
24  * @brief Information we track per path.
25  * @author Bartlomiej Polot
26  * @author Christian Grothoff
27  *
28  * TODO:
29  * - path desirability score calculations are not done
30  *   (and will be tricky to have during path changes)
31  */
32 #include "platform.h"
33 #include "gnunet-service-cadet-new_peer.h"
34 #include "gnunet-service-cadet-new_paths.h"
35
36
37 /**
38  * Information regarding a possible path to reach a peer.
39  */
40 struct CadetPeerPath
41 {
42
43   /**
44    * Array of all the peers on the path.  If @e hn is non-NULL, the
45    * last one is our owner.
46    */
47   struct CadetPeerPathEntry *entries;
48
49   /**
50    * Node of this path in the owner's heap.  Used to update our position
51    * in the heap whenever our @e desirability changes.
52    */
53   struct GNUNET_CONTAINER_HeapNode *hn;
54
55   /**
56    * Connections using this path, by destination peer
57    * (each hop of the path could correspond to an
58    * active connection).
59    */
60   struct GNUNET_CONTAINER_MultiPeerMap *connections;
61
62   /**
63    * Desirability of the path. How unique is it for the various peers
64    * on it?
65    */
66   GNUNET_CONTAINER_HeapCostType desirability;
67
68   /**
69    * Length of the @e entries array.
70    */
71   unsigned int entries_length;
72
73 };
74
75
76 /**
77  * Return how much we like keeping the path.  This is an aggregate
78  * score based on various factors, including the age of the path
79  * (older == better), and the value of this path to all of its ajacent
80  * peers.  For example, long paths that end at a peer that we have no
81  * shorter way to reach are very desirable, while long paths that end
82  * at a peer for which we have a shorter way as well are much less
83  * desirable.  Higher values indicate more valuable paths.  The
84  * returned value should be used to decide which paths to remember.
85  *
86  * @param path path to return the length for
87  * @return desirability of the path, larger is more desirable
88  */
89 GNUNET_CONTAINER_HeapCostType
90 GCPP_get_desirability (const struct CadetPeerPath *path)
91 {
92   return path->desirability;
93 }
94
95
96 /**
97  * Return connection to @a destination using @a path, or return
98  * NULL if no such connection exists.
99  *
100  * @param path path to traverse
101  * @param destination destination node to get to, must be on path
102  * @param off offset of @a destination on @a path
103  * @return NULL if @a create is NO and we have no existing connection
104  *         otherwise connection from us to @a destination via @a path
105  */
106 struct CadetConnection *
107 GCPP_get_connection (struct CadetPeerPath *path,
108                      struct CadetPeer *destination,
109                      unsigned int off)
110 {
111   struct CadetPeerPathEntry *entry;
112
113   GNUNET_assert (off < path->entries_length);
114   entry = &path->entries[off];
115   GNUNET_assert (entry->peer == destination);
116   return entry->cc;
117 }
118
119
120 /**
121  * Notify @a path that it is used for connection @a cc
122  * which ends at the path's offset @a off.
123  *
124  * @param path the path to remember the @a cc
125  * @param off the offset where the @a cc ends
126  * @param cc the connection to remember
127  */
128 void
129 GCPP_add_connection (struct CadetPeerPath *path,
130                      unsigned int off,
131                      struct CadetConnection *cc)
132 {
133   struct CadetPeerPathEntry *entry;
134
135   GNUNET_assert (off < path->entries_length);
136   entry = &path->entries[off];
137   GNUNET_assert (NULL == entry->cc);
138   entry->cc = cc;
139 }
140
141
142
143 /**
144  * Notify @a path that it is no longer used for connection @a cc which
145  * ended at the path's offset @a off.
146  *
147  * @param path the path to forget the @a cc
148  * @param off the offset where the @a cc ended
149  * @param cc the connection to forget
150  */
151 void
152 GCPP_del_connection (struct CadetPeerPath *path,
153                      unsigned int off,
154                      struct CadetConnection *cc)
155 {
156   struct CadetPeerPathEntry *entry;
157
158   GNUNET_assert (off < path->entries_length);
159   entry = &path->entries[off];
160   GNUNET_assert (cc == entry->cc);
161   entry->cc = NULL;
162 }
163
164
165 /**
166  * This path is no longer needed, free resources.
167  *
168  * @param path path resources to free
169  */
170 static void
171 path_destroy (struct CadetPeerPath *path)
172 {
173   GNUNET_assert (0 ==
174                  GNUNET_CONTAINER_multipeermap_size (path->connections));
175   GNUNET_CONTAINER_multipeermap_destroy (path->connections);
176   GNUNET_free (path->entries);
177   GNUNET_free (path);
178 }
179
180
181 /**
182  * The owning peer of this path is no longer interested in maintaining
183  * it, so the path should be discarded or shortened (in case a
184  * previous peer on the path finds the path desirable).
185  *
186  * @param path the path that is being released
187  */
188 void
189 GCPP_release (struct CadetPeerPath *path)
190 {
191   struct CadetPeerPathEntry *entry;
192
193   path->hn = NULL;
194   entry = &path->entries[path->entries_length - 1];
195   while (1)
196   {
197     /* cut 'off' end of path, verifying it is not in use */
198     GNUNET_assert (NULL ==
199                    GNUNET_CONTAINER_multipeermap_get (path->connections,
200                                                       GCP_get_id (entry->peer)));
201     GCP_path_entry_remove (entry->peer,
202                            entry,
203                            path->entries_length - 1);
204     path->entries_length--; /* We don't bother shrinking the 'entries' array,
205                                as it's probably not worth it. */
206     if (0 == path->entries_length)
207       break; /* the end */
208
209     /* see if new peer at the end likes this path any better */
210     entry = &path->entries[path->entries_length - 1];
211     path->hn = GCP_attach_path (entry->peer,
212                                 path,
213                                 path->entries_length);
214     if (NULL != path->hn)
215       return; /* yep, got attached, we are done. */
216   }
217
218   /* nobody wants us, discard the path */
219   path_destroy (path);
220 }
221
222
223 /**
224  * Updates the score for an entry on the path based
225  * on our experiences with using @a path.
226  *
227  * @param path the path to update
228  * @param off offset of the entry to update
229  * @param delta change in the score to apply
230  */
231 void
232 GCPP_update_score (struct CadetPeerPath *path,
233                    unsigned int off,
234                    int delta)
235 {
236   struct CadetPeerPathEntry *entry;
237
238   GNUNET_assert (off < path->entries_length);
239   entry = &path->entries[off];
240
241   /* Add delta, with checks for overflows */
242   if (delta >= 0)
243   {
244     if (delta + entry->score < entry->score)
245       entry->score = INT_MAX;
246     else
247       entry->score += delta;
248   }
249   else
250   {
251     if (delta + entry->score > entry->score)
252       entry->score = INT_MIN;
253     else
254       entry->score += delta;
255   }
256
257   /* FIXME: update path desirability! */
258 }
259
260
261 /**
262  * Closure for #find_peer_at() and #check_match().
263  */
264 struct CheckMatchContext
265 {
266
267   /**
268    * Set to a matching path, if any.
269    */
270   struct CadetPeerPath *match;
271
272   /**
273    * Array the combined paths.
274    */
275   struct CadetPeer **cpath;
276
277 };
278
279
280 /**
281  * Check if the given path is identical on all of the
282  * hops until @a off, and not longer than @a off.  If the
283  * @a path matches, store it in `match`.
284  *
285  * @param cls the `struct CheckMatchContext` to check against
286  * @param path the path to check
287  * @param off offset to check at
288  * @return #GNUNET_YES (continue to iterate), or if found #GNUNET_NO
289  */
290 static int
291 check_match (void *cls,
292              struct CadetPeerPath *path,
293              unsigned int off)
294 {
295   struct CheckMatchContext *cm_ctx = cls;
296
297   if (path->entries_length > off)
298     return GNUNET_YES; /* too long, cannot be useful */
299   for (unsigned int i=0;i<off;i++)
300     if (cm_ctx->cpath[i] !=
301         GCPP_get_peer_at_offset (path,
302                                  i))
303       return GNUNET_YES; /* missmatch, ignore */
304   cm_ctx->match = path;
305   return GNUNET_NO; /* match, we are done! */
306 }
307
308
309 /**
310  * Extend path @a path by the @a num_peers from the @a peers
311  * array, assuming the owners past the current owner want it.
312  *
313  * @param path path to extend
314  * @param peers list of peers beyond the end of @a path
315  * @param num_peers length of the @a peers array
316  */
317 static void
318 extend_path (struct CadetPeerPath *path,
319              struct CadetPeer **peers,
320              unsigned int num_peers)
321 {
322   unsigned int old_len = path->entries_length;
323   struct GNUNET_CONTAINER_HeapNode *hn;
324   int i;
325
326   /* If we extend an existing path, detach it from the
327      old owner and re-attach to the new one */
328   hn = NULL;
329   for (i=num_peers-1;i>=0;i--)
330   {
331     /* FIXME: note that path->desirability is used, but not yet updated here! */
332     hn = GCP_attach_path (peers[i],
333                           path,
334                           old_len + (unsigned int) i);
335     if (NULL != hn)
336       break;
337   }
338   if (NULL == hn)
339     return; /* none of the peers is interested in this path */
340   GCP_detach_path (path->entries[old_len-1].peer,
341                    path,
342                    path->hn);
343   path->hn = hn;
344   GNUNET_array_grow (path->entries,
345                      path->entries_length,
346                      old_len + i);
347   for (;i >= 0;i--)
348   {
349     struct CadetPeerPathEntry *entry = &path->entries[old_len + i];
350
351     entry->peer = peers[i];
352     entry->path = path;
353     GCP_path_entry_add (entry->peer,
354                         entry,
355                         old_len + i);
356   }
357 }
358
359
360 /**
361  * Create a peer path based on the result of a DHT lookup.  If we
362  * already know this path, or one that is longer, simply return NULL.
363  * Otherwise, we try to extend an existing path, or create a new one
364  * if applicable.
365  *
366  * @param get_path path of the get request
367  * @param get_path_length lenght of @a get_path
368  * @param put_path path of the put request
369  * @param put_path_length length of the @a put_path
370  * @return a path through the network
371  */
372 void
373 GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
374                         unsigned int get_path_length,
375                         const struct GNUNET_PeerIdentity *put_path,
376                         unsigned int put_path_length)
377 {
378   struct CheckMatchContext cm_ctx;
379   struct CadetPeer *cpath[get_path_length + put_path_length];
380   struct CadetPeerPath *path;
381   struct GNUNET_CONTAINER_HeapNode *hn;
382   int i;
383
384   /* precompute 'cpath' so we can avoid doing the lookups lots of times */
385   for (unsigned int off=0;off<get_path_length + put_path_length;off++)
386   {
387     const struct GNUNET_PeerIdentity *pid;
388
389     pid = (off < get_path_length)
390       ? &get_path[get_path_length - off]
391       : &put_path[get_path_length + put_path_length - off];
392     cpath[off] = GCP_get (pid,
393                           GNUNET_YES);
394   }
395
396   /* First figure out if this path is a subset of an existing path, an
397      extension of an existing path, or a new path. */
398   cm_ctx.cpath = cpath;
399   cm_ctx.match = NULL;
400   for (i=get_path_length + put_path_length-1;i>=0;i--)
401   {
402     GCP_iterate_paths_at (cpath[i],
403                           (unsigned int) i,
404                           &check_match,
405                           &cm_ctx);
406     if (NULL != cm_ctx.match)
407     {
408       if (i == get_path_length + put_path_length - 1)
409       {
410         /* Existing path includes this one, nothing to do! */
411         return;
412       }
413       if (cm_ctx.match->entries_length == i + 1)
414       {
415         /* Existing path ends in the middle of new path, extend it! */
416         extend_path (cm_ctx.match,
417                      &cpath[i],
418                      get_path_length + put_path_length - i);
419         return;
420       }
421     }
422   }
423
424   /* No match at all, create completely new path */
425   path = GNUNET_new (struct CadetPeerPath);
426
427   /* First, try to attach it */
428   hn = NULL;
429   for (i=get_path_length + put_path_length-1;i>=0;i--)
430   {
431     path->entries_length = i;
432     /* FIXME: note that path->desirability is used, but not yet initialized here! */
433     hn = GCP_attach_path (cpath[i],
434                           path,
435                           (unsigned int) i);
436     if (NULL != hn)
437       break;
438   }
439   if (NULL == hn)
440   {
441     /* None of the peers on the path care about it. */
442     GNUNET_free (path);
443     return;
444   }
445   path->hn = hn;
446   path->entries_length = i;
447   path->entries = GNUNET_new_array (path->entries_length,
448                                     struct CadetPeerPathEntry);
449   for (;i>=0;i--)
450   {
451     struct CadetPeerPathEntry *entry = &path->entries[i];
452
453     entry->peer = cpath[i];
454     entry->path = path;
455     GCP_path_entry_add (entry->peer,
456                         entry,
457                         i);
458   }
459 }
460
461
462 /**
463  * Return the length of the path.  Excludes one end of the
464  * path, so the loopback path has length 0.
465  *
466  * @param path path to return the length for
467  * @return number of peers on the path
468  */
469 unsigned int
470 GCPP_get_length (struct CadetPeerPath *path)
471 {
472   return path->entries_length;
473 }
474
475
476 /**
477  * Find peer's offset on path.
478  *
479  * @param path path to search
480  * @param cp peer to look for
481  * @return offset of @a cp on @a path, or UINT_MAX if not found
482  */
483 unsigned int
484 GCPP_find_peer (struct CadetPeerPath *path,
485                 struct CadetPeer *cp)
486 {
487   for (unsigned int off = 0;
488        off < path->entries_length;
489        off++)
490     if (cp == GCPP_get_peer_at_offset (path,
491                                        off))
492       return off;
493   return UINT_MAX;
494 }
495
496
497 /**
498  * Obtain the peer at offset @a off in @a path.
499  *
500  * @param path peer path to inspect
501  * @param off offset to return, must be smaller than path length
502  * @return the peer at offset @a off
503  */
504 struct CadetPeer *
505 GCPP_get_peer_at_offset (struct CadetPeerPath *path,
506                          unsigned int off)
507 {
508   return path->entries[off].peer;
509 }
510
511
512 /* end of gnunet-service-cadet-new_paths.c */