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