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