make GCPP_2s also return static string
[oweals/gnunet.git] / src / cadet / gnunet-service-cadet-new_paths.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2017 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file cadet/gnunet-service-cadet-new_paths.c
22  * @brief Information we track per path.
23  * @author Bartlomiej Polot
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - path desirability score calculations are not done
28  *   (and will be tricky to have during path changes)
29  */
30 #include "platform.h"
31 #include "gnunet-service-cadet-new_connection.h"
32 #include "gnunet-service-cadet-new_peer.h"
33 #include "gnunet-service-cadet-new_paths.h"
34
35
36 #define LOG(level, ...) GNUNET_log_from(level,"cadet-pat",__VA_ARGS__)
37
38
39 /**
40  * Information regarding a possible path to reach a peer.
41  */
42 struct CadetPeerPath
43 {
44
45   /**
46    * Array of all the peers on the path.  If @e hn is non-NULL, the
47    * last one is our owner.
48    */
49   struct CadetPeerPathEntry *entries;
50
51   /**
52    * Node of this path in the owner's heap.  Used to update our position
53    * in the heap whenever our @e desirability changes.
54    */
55   struct GNUNET_CONTAINER_HeapNode *hn;
56
57   /**
58    * Connections using this path, by destination peer
59    * (each hop of the path could correspond to an
60    * active connection).
61    */
62   struct GNUNET_CONTAINER_MultiPeerMap *connections;
63
64   /**
65    * Desirability of the path. How unique is it for the various peers
66    * on it?
67    */
68   GNUNET_CONTAINER_HeapCostType desirability;
69
70   /**
71    * Length of the @e entries array.
72    */
73   unsigned int entries_length;
74
75 };
76
77
78 /**
79  * Return how much we like keeping the path.  This is an aggregate
80  * score based on various factors, including the age of the path
81  * (older == better), and the value of this path to all of its ajacent
82  * peers.  For example, long paths that end at a peer that we have no
83  * shorter way to reach are very desirable, while long paths that end
84  * at a peer for which we have a shorter way as well are much less
85  * desirable.  Higher values indicate more valuable paths.  The
86  * returned value should be used to decide which paths to remember.
87  *
88  * @param path path to return the length for
89  * @return desirability of the path, larger is more desirable
90  */
91 GNUNET_CONTAINER_HeapCostType
92 GCPP_get_desirability (const struct CadetPeerPath *path)
93 {
94   return path->desirability;
95 }
96
97
98 /**
99  * Return connection to @a destination using @a path, or return
100  * NULL if no such connection exists.
101  *
102  * @param path path to traverse
103  * @param destination destination node to get to, must be on path
104  * @param off offset of @a destination on @a path
105  * @return NULL if we have no existing connection
106  *         otherwise connection from us to @a destination via @a path
107  */
108 struct CadetConnection *
109 GCPP_get_connection (struct CadetPeerPath *path,
110                      struct CadetPeer *destination,
111                      unsigned int off)
112 {
113   struct CadetPeerPathEntry *entry;
114
115   GNUNET_assert (off < path->entries_length);
116   entry = &path->entries[off];
117   GNUNET_assert (entry->peer == destination);
118   return entry->cc;
119 }
120
121
122 /**
123  * Notify @a path that it is used for connection @a cc
124  * which ends at the path's offset @a off.
125  *
126  * @param path the path to remember the @a cc
127  * @param off the offset where the @a cc ends
128  * @param cc the connection to remember
129  */
130 void
131 GCPP_add_connection (struct CadetPeerPath *path,
132                      unsigned int off,
133                      struct CadetConnection *cc)
134 {
135   struct CadetPeerPathEntry *entry;
136
137   LOG (GNUNET_ERROR_TYPE_DEBUG,
138        "Adding connection %s to path %s at offset %u\n",
139        GCC_2s (cc),
140        GCPP_2s (path),
141        off);
142   GNUNET_assert (off < path->entries_length);
143   entry = &path->entries[off];
144   GNUNET_assert (NULL == entry->cc);
145   entry->cc = cc;
146 }
147
148
149
150 /**
151  * Notify @a path that it is no longer used for connection @a cc which
152  * ended at the path's offset @a off.
153  *
154  * @param path the path to forget the @a cc
155  * @param off the offset where the @a cc ended
156  * @param cc the connection to forget
157  */
158 void
159 GCPP_del_connection (struct CadetPeerPath *path,
160                      unsigned int off,
161                      struct CadetConnection *cc)
162 {
163   struct CadetPeerPathEntry *entry;
164
165   LOG (GNUNET_ERROR_TYPE_DEBUG,
166        "Removing connection %s to path %s at offset %u\n",
167        GCC_2s (cc),
168        GCPP_2s (path),
169        off);
170   GNUNET_assert (off < path->entries_length);
171   entry = &path->entries[off];
172   GNUNET_assert (cc == entry->cc);
173   entry->cc = NULL;
174 }
175
176
177 /**
178  * This path is no longer needed, free resources.
179  *
180  * @param path path resources to free
181  */
182 static void
183 path_destroy (struct CadetPeerPath *path)
184 {
185   LOG (GNUNET_ERROR_TYPE_DEBUG,
186        "Destroying path %s\n",
187        GCPP_2s (path));
188   GNUNET_assert (0 ==
189                  GNUNET_CONTAINER_multipeermap_size (path->connections));
190   GNUNET_CONTAINER_multipeermap_destroy (path->connections);
191   GNUNET_free (path->entries);
192   GNUNET_free (path);
193 }
194
195
196 /**
197  * The owning peer of this path is no longer interested in maintaining
198  * it, so the path should be discarded or shortened (in case a
199  * previous peer on the path finds the path desirable).
200  *
201  * @param path the path that is being released
202  */
203 void
204 GCPP_release (struct CadetPeerPath *path)
205 {
206   struct CadetPeerPathEntry *entry;
207
208   LOG (GNUNET_ERROR_TYPE_DEBUG,
209        "Owner releases path %s\n",
210        GCPP_2s (path));
211   path->hn = NULL;
212   entry = &path->entries[path->entries_length - 1];
213   while (1)
214   {
215     /* cut 'off' end of path, verifying it is not in use */
216     GNUNET_assert (NULL ==
217                    GNUNET_CONTAINER_multipeermap_get (path->connections,
218                                                       GCP_get_id (entry->peer)));
219     GCP_path_entry_remove (entry->peer,
220                            entry,
221                            path->entries_length - 1);
222     path->entries_length--; /* We don't bother shrinking the 'entries' array,
223                                as it's probably not worth it. */
224     if (0 == path->entries_length)
225       break; /* the end */
226
227     /* see if new peer at the end likes this path any better */
228     entry = &path->entries[path->entries_length - 1];
229     path->hn = GCP_attach_path (entry->peer,
230                                 path,
231                                 path->entries_length,
232                                 GNUNET_NO);
233     if (NULL != path->hn)
234       return; /* yep, got attached, we are done. */
235   }
236
237   /* nobody wants us, discard the path */
238   path_destroy (path);
239 }
240
241
242 /**
243  * Updates the score for an entry on the path based
244  * on our experiences with using @a path.
245  *
246  * @param path the path to update
247  * @param off offset of the entry to update
248  * @param delta change in the score to apply
249  */
250 void
251 GCPP_update_score (struct CadetPeerPath *path,
252                    unsigned int off,
253                    int delta)
254 {
255   struct CadetPeerPathEntry *entry;
256
257   GNUNET_assert (off < path->entries_length);
258   entry = &path->entries[off];
259
260   /* Add delta, with checks for overflows */
261   if (delta >= 0)
262   {
263     if (delta + entry->score < entry->score)
264       entry->score = INT_MAX;
265     else
266       entry->score += delta;
267   }
268   else
269   {
270     if (delta + entry->score > entry->score)
271       entry->score = INT_MIN;
272     else
273       entry->score += delta;
274   }
275
276   /* FIXME: update path desirability! */
277 }
278
279
280 /**
281  * Closure for #find_peer_at() and #check_match().
282  */
283 struct CheckMatchContext
284 {
285
286   /**
287    * Set to a matching path, if any.
288    */
289   struct CadetPeerPath *match;
290
291   /**
292    * Array the combined paths.
293    */
294   struct CadetPeer **cpath;
295
296 };
297
298
299 /**
300  * Check if the given path is identical on all of the
301  * hops until @a off, and not longer than @a off.  If the
302  * @a path matches, store it in `match`.
303  *
304  * @param cls the `struct CheckMatchContext` to check against
305  * @param path the path to check
306  * @param off offset to check at
307  * @return #GNUNET_YES (continue to iterate), or if found #GNUNET_NO
308  */
309 static int
310 check_match (void *cls,
311              struct CadetPeerPath *path,
312              unsigned int off)
313 {
314   struct CheckMatchContext *cm_ctx = cls;
315
316   if (path->entries_length > off)
317     return GNUNET_YES; /* too long, cannot be useful */
318   for (unsigned int i=0;i<off;i++)
319     if (cm_ctx->cpath[i] !=
320         GCPP_get_peer_at_offset (path,
321                                  i))
322       return GNUNET_YES; /* missmatch, ignore */
323   cm_ctx->match = path;
324   return GNUNET_NO; /* match, we are done! */
325 }
326
327
328 /**
329  * Extend path @a path by the @a num_peers from the @a peers
330  * array, assuming the owners past the current owner want it.
331  *
332  * @param path path to extend
333  * @param peers list of peers beyond the end of @a path
334  * @param num_peers length of the @a peers array
335  * @param force force attachment, even if we have other
336  *        paths already
337  */
338 static void
339 extend_path (struct CadetPeerPath *path,
340              struct CadetPeer **peers,
341              unsigned int num_peers,
342              int force)
343 {
344   unsigned int old_len = path->entries_length;
345   struct GNUNET_CONTAINER_HeapNode *hn;
346   int i;
347
348   /* If we extend an existing path, detach it from the
349      old owner and re-attach to the new one */
350   hn = NULL;
351   for (i=num_peers-1;i>=0;i--)
352   {
353     /* FIXME: note that path->desirability is used, but not yet updated here! */
354     hn = GCP_attach_path (peers[i],
355                           path,
356                           old_len + (unsigned int) i,
357                           GNUNET_YES);
358     if (NULL != hn)
359       break;
360   }
361   if (NULL == hn)
362     return; /* none of the peers is interested in this path */
363   GCP_detach_path (path->entries[old_len-1].peer,
364                    path,
365                    path->hn);
366   path->hn = hn;
367   GNUNET_array_grow (path->entries,
368                      path->entries_length,
369                      old_len + i);
370   for (;i >= 0;i--)
371   {
372     struct CadetPeerPathEntry *entry = &path->entries[old_len + i];
373
374     entry->peer = peers[i];
375     entry->path = path;
376     GCP_path_entry_add (entry->peer,
377                         entry,
378                         old_len + i);
379   }
380   LOG (GNUNET_ERROR_TYPE_DEBUG,
381        "Extended path %s\n",
382        GCPP_2s (path));
383 }
384
385
386 /**
387  * Create a peer path based on the result of a DHT lookup.  If we
388  * already know this path, or one that is longer, simply return NULL.
389  * Otherwise, we try to extend an existing path, or create a new one
390  * if applicable.
391  *
392  * @param get_path path of the get request
393  * @param get_path_length lenght of @a get_path
394  * @param put_path path of the put request
395  * @param put_path_length length of the @a put_path
396  * @return a path through the network
397  */
398 void
399 GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
400                         unsigned int get_path_length,
401                         const struct GNUNET_PeerIdentity *put_path,
402                         unsigned int put_path_length)
403 {
404   struct CadetPeer *cpath[get_path_length + put_path_length];
405   struct CheckMatchContext cm_ctx;
406   struct CadetPeerPath *path;
407   struct GNUNET_CONTAINER_HeapNode *hn;
408   int i;
409
410   /* precompute 'cpath' so we can avoid doing the lookups lots of times */
411   for (unsigned int off=0;off<get_path_length + put_path_length;off++)
412   {
413     const struct GNUNET_PeerIdentity *pid;
414
415     pid = (off < get_path_length)
416       ? &get_path[get_path_length - off]
417       : &put_path[get_path_length + put_path_length - off];
418     cpath[off] = GCP_get (pid,
419                           GNUNET_YES);
420   }
421
422   /* First figure out if this path is a subset of an existing path, an
423      extension of an existing path, or a new path. */
424   cm_ctx.cpath = cpath;
425   cm_ctx.match = NULL;
426   for (i=get_path_length + put_path_length-1;i>=0;i--)
427   {
428     GCP_iterate_paths_at (cpath[i],
429                           (unsigned int) i,
430                           &check_match,
431                           &cm_ctx);
432     if (NULL != cm_ctx.match)
433     {
434       if (i == get_path_length + put_path_length - 1)
435       {
436         /* Existing path includes this one, nothing to do! */
437         LOG (GNUNET_ERROR_TYPE_DEBUG,
438              "Path discovered from DHT is already known\n");
439         return;
440       }
441       if (cm_ctx.match->entries_length == i + 1)
442       {
443         /* Existing path ends in the middle of new path, extend it! */
444         LOG (GNUNET_ERROR_TYPE_DEBUG,
445              "Trying to extend existing path %s by additional links discovered from DHT\n",
446              GCPP_2s (cm_ctx.match));
447         extend_path (cm_ctx.match,
448                      &cpath[i],
449                      get_path_length + put_path_length - i,
450                      GNUNET_NO);
451         return;
452       }
453     }
454   }
455
456   /* No match at all, create completely new path */
457   path = GNUNET_new (struct CadetPeerPath);
458
459   /* First, try to attach it */
460   hn = NULL;
461   for (i=get_path_length + put_path_length-1;i>=0;i--)
462   {
463     path->entries_length = i;
464     /* FIXME: note that path->desirability is used, but not yet initialized here! */
465     hn = GCP_attach_path (cpath[i],
466                           path,
467                           (unsigned int) i,
468                           GNUNET_NO);
469     if (NULL != hn)
470       break;
471   }
472   if (NULL == hn)
473   {
474     /* None of the peers on the path care about it. */
475     LOG (GNUNET_ERROR_TYPE_DEBUG,
476          "Path discovered from DHT is not interesting to us\n");
477     GNUNET_free (path);
478     return;
479   }
480   path->hn = hn;
481   path->entries_length = i;
482   path->entries = GNUNET_new_array (path->entries_length,
483                                     struct CadetPeerPathEntry);
484   for (;i>=0;i--)
485   {
486     struct CadetPeerPathEntry *entry = &path->entries[i];
487
488     entry->peer = cpath[i];
489     entry->path = path;
490     GCP_path_entry_add (entry->peer,
491                         entry,
492                         i);
493   }
494   LOG (GNUNET_ERROR_TYPE_DEBUG,
495        "Created new path %s based on information from DHT\n",
496        GCPP_2s (path));
497 }
498
499
500 /**
501  * We got an incoming connection, obtain the corresponding path.
502  *
503  * @param path_length number of segments on the @a path
504  * @param pids path through the network, in reverse order (we are at the end at index @a path_length)
505  * @return corresponding path object
506  */
507 struct CadetPeerPath *
508 GCPP_get_path_from_route (unsigned int path_length,
509                           const struct GNUNET_PeerIdentity *pids)
510 {
511   struct CheckMatchContext cm_ctx;
512   struct CadetPeer *cpath[path_length];
513   struct CadetPeerPath *path;
514
515   /* precompute inverted 'cpath' so we can avoid doing the lookups and
516      have the correct order */
517   for (unsigned int off=0;off<path_length;off++)
518     cpath[off] = GCP_get (&pids[path_length - 1 - off],
519                           GNUNET_YES);
520
521   /* First figure out if this path is a subset of an existing path, an
522      extension of an existing path, or a new path. */
523   cm_ctx.cpath = cpath;
524   cm_ctx.match = NULL;
525   for (int i=path_length-1;i>=0;i--)
526   {
527     GCP_iterate_paths_at (cpath[i],
528                           (unsigned int) i,
529                           &check_match,
530                           &cm_ctx);
531     if (NULL != cm_ctx.match)
532     {
533       if (i == path_length - 1)
534       {
535         /* Existing path includes this one, return the match! */
536         LOG (GNUNET_ERROR_TYPE_DEBUG,
537              "Returning existing path %s as inverse for incoming connection\n",
538              GCPP_2s (cm_ctx.match));
539         return cm_ctx.match;
540       }
541       if (cm_ctx.match->entries_length == i + 1)
542       {
543         /* Existing path ends in the middle of new path, extend it! */
544         LOG (GNUNET_ERROR_TYPE_DEBUG,
545              "Extending existing path %s to create inverse for incoming connection\n",
546              GCPP_2s (cm_ctx.match));
547         extend_path (cm_ctx.match,
548                      &cpath[i],
549                      path_length - i,
550                      GNUNET_YES);
551         /* Check that extension was successful */
552         GNUNET_assert (cm_ctx.match->entries_length == path_length);
553         return cm_ctx.match;
554       }
555     }
556   }
557
558   /* No match at all, create completely new path */
559   path = GNUNET_new (struct CadetPeerPath);
560   path->entries_length = path_length;
561   path->entries = GNUNET_new_array (path->entries_length,
562                                     struct CadetPeerPathEntry);
563   for (int i=path_length-1;i>=0;i--)
564   {
565     struct CadetPeerPathEntry *entry = &path->entries[i];
566
567     entry->peer = cpath[i];
568     entry->path = path;
569     GCP_path_entry_add (entry->peer,
570                         entry,
571                         i);
572   }
573   path->hn = GCP_attach_path (cpath[path_length - 1],
574                               path,
575                               path_length - 1,
576                               GNUNET_YES);
577   LOG (GNUNET_ERROR_TYPE_DEBUG,
578        "Created new path %s to create inverse for incoming connection\n",
579        GCPP_2s (path));
580   return path;
581 }
582
583
584 /**
585  * Return the length of the path.  Excludes one end of the
586  * path, so the loopback path has length 0.
587  *
588  * @param path path to return the length for
589  * @return number of peers on the path
590  */
591 unsigned int
592 GCPP_get_length (struct CadetPeerPath *path)
593 {
594   return path->entries_length;
595 }
596
597
598 /**
599  * Find peer's offset on path.
600  *
601  * @param path path to search
602  * @param cp peer to look for
603  * @return offset of @a cp on @a path, or UINT_MAX if not found
604  */
605 unsigned int
606 GCPP_find_peer (struct CadetPeerPath *path,
607                 struct CadetPeer *cp)
608 {
609   for (unsigned int off = 0;
610        off < path->entries_length;
611        off++)
612     if (cp == GCPP_get_peer_at_offset (path,
613                                        off))
614       return off;
615   return UINT_MAX;
616 }
617
618
619 /**
620  * Obtain the peer at offset @a off in @a path.
621  *
622  * @param path peer path to inspect
623  * @param off offset to return, must be smaller than path length
624  * @return the peer at offset @a off
625  */
626 struct CadetPeer *
627 GCPP_get_peer_at_offset (struct CadetPeerPath *path,
628                          unsigned int off)
629 {
630   GNUNET_assert (off < path->entries_length);
631   return path->entries[off].peer;
632 }
633
634
635 /**
636  * Convert a path to a human-readable string.
637  *
638  * @param path path to convert
639  * @return string, to be freed by caller (unlike other *_2s APIs!)
640  */
641 const char *
642 GCPP_2s (struct CadetPeerPath *path)
643 {
644   static char buf[2048];
645   size_t off;
646   const unsigned int max_plen = sizeof(buf) / 5 - 2; /* 5 characters per entry */
647
648   off = 0;
649   for (unsigned int i = 0;
650        i < path->entries_length;
651        i++)
652   {
653     if ( (path->entries_length > max_plen) &&
654          (i == max_plen / 2) )
655       off += GNUNET_snprintf (&buf[off],
656                               sizeof (buf) - off,
657                               "... ");
658     if ( (path->entries_length > max_plen) &&
659          (i > max_plen / 2) &&
660          (i < path->entries_length - max_plen / 2) )
661       continue;
662     off += GNUNET_snprintf (&buf[off],
663                             sizeof (buf) - off,
664                             "%s ",
665                             GNUNET_i2s (GCP_get_id (GCPP_get_peer_at_offset (path,
666                                                                              i))));
667   }
668   return buf;
669 }
670
671
672 /* end of gnunet-service-cadet-new_paths.c */