another special case for loopback
[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 - 1,
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   }
373   for (i=num_peers-1;i >= 0;i--)
374   {
375     struct CadetPeerPathEntry *entry = path->entries[old_len + i];
376
377     GCP_path_entry_add (entry->peer,
378                         entry,
379                         old_len + i);
380   }
381
382   /* If we extend an existing path, detach it from the
383      old owner and re-attach to the new one */
384   hn = NULL;
385   for (i=num_peers-1;i>=0;i--)
386   {
387     struct CadetPeerPathEntry *entry = path->entries[old_len + i];
388
389     path->entries_length = old_len + i + 1;
390     /* FIXME: note that path->desirability is used, but not yet updated here! */
391     hn = GCP_attach_path (peers[i],
392                           path,
393                           old_len + (unsigned int) i,
394                           GNUNET_YES);
395     if (NULL != hn)
396       break;
397     GCP_path_entry_remove (entry->peer,
398                            entry,
399                            old_len + i);
400     GNUNET_free (entry);
401     path->entries[old_len + i] = NULL;
402   }
403   if (NULL == hn)
404   {
405     /* none of the peers is interested in this path;
406        shrink path back */
407     GNUNET_array_grow (path->entries,
408                        path->entries_length,
409                        old_len);
410     return;
411   }
412   GCP_detach_path (path->entries[old_len-1]->peer,
413                    path,
414                    path->hn);
415   path->hn = hn;
416   LOG (GNUNET_ERROR_TYPE_DEBUG,
417        "Extended path %s\n",
418        GCPP_2s (path));
419 }
420
421
422 /**
423  * Create a peer path based on the result of a DHT lookup.  If we
424  * already know this path, or one that is longer, simply return NULL.
425  * Otherwise, we try to extend an existing path, or create a new one
426  * if applicable.
427  *
428  * @param get_path path of the get request
429  * @param get_path_length lenght of @a get_path
430  * @param put_path path of the put request
431  * @param put_path_length length of the @a put_path
432  * @return a path through the network
433  */
434 void
435 GCPP_try_path_from_dht (const struct GNUNET_PeerIdentity *get_path,
436                         unsigned int get_path_length,
437                         const struct GNUNET_PeerIdentity *put_path,
438                         unsigned int put_path_length)
439 {
440   struct CadetPeer *cpath[get_path_length + put_path_length];
441   struct CheckMatchContext cm_ctx;
442   struct CadetPeerPath *path;
443   struct GNUNET_CONTAINER_HeapNode *hn;
444   int i;
445
446   /* precompute 'cpath' so we can avoid doing the lookups lots of times */
447   for (unsigned int off=0;off<get_path_length + put_path_length;off++)
448   {
449     const struct GNUNET_PeerIdentity *pid;
450
451     pid = (off < get_path_length)
452       ? &get_path[get_path_length - off]
453       : &put_path[get_path_length + put_path_length - off];
454     cpath[off] = GCP_get (pid,
455                           GNUNET_YES);
456   }
457
458   /* First figure out if this path is a subset of an existing path, an
459      extension of an existing path, or a new path. */
460   cm_ctx.cpath_length = get_path_length + put_path_length;
461   cm_ctx.cpath = cpath;
462   cm_ctx.match = NULL;
463   for (i=get_path_length + put_path_length-1;i>=0;i--)
464   {
465     GCP_iterate_paths_at (cpath[i],
466                           (unsigned int) i,
467                           &check_match,
468                           &cm_ctx);
469     if (NULL != cm_ctx.match)
470     {
471       if (i == get_path_length + put_path_length - 1)
472       {
473         /* Existing path includes this one, nothing to do! */
474         LOG (GNUNET_ERROR_TYPE_DEBUG,
475              "Path discovered from DHT is already known\n");
476         return;
477       }
478       if (cm_ctx.match->entries_length == i + 1)
479       {
480         /* Existing path ends in the middle of new path, extend it! */
481         LOG (GNUNET_ERROR_TYPE_DEBUG,
482              "Trying to extend existing path %s by additional links discovered from DHT\n",
483              GCPP_2s (cm_ctx.match));
484         extend_path (cm_ctx.match,
485                      &cpath[i],
486                      get_path_length + put_path_length - i,
487                      GNUNET_NO);
488         return;
489       }
490     }
491   }
492
493   /* No match at all, create completely new path */
494   path = GNUNET_new (struct CadetPeerPath);
495   path->entries_length = get_path_length + put_path_length;
496   path->entries = GNUNET_new_array (path->entries_length,
497                                     struct CadetPeerPathEntry *);
498   for (i=path->entries_length-1;i>=0;i--)
499   {
500     struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
501
502     path->entries[i] = entry;
503     entry->peer = cpath[i];
504     entry->path = path;
505   }
506   for (i=path->entries_length-1;i>=0;i--)
507   {
508     struct CadetPeerPathEntry *entry = path->entries[i];
509
510     GCP_path_entry_add (entry->peer,
511                         entry,
512                         i);
513   }
514
515   /* Finally, try to attach it */
516   hn = NULL;
517   for (i=get_path_length + put_path_length-1;i>=0;i--)
518   {
519     struct CadetPeerPathEntry *entry = path->entries[i];
520
521     path->entries_length = i + 1;
522     /* FIXME: note that path->desirability is used, but not yet initialized here! */
523     hn = GCP_attach_path (cpath[i],
524                           path,
525                           (unsigned int) i,
526                           GNUNET_NO);
527     if (NULL != hn)
528       break;
529     GCP_path_entry_remove (entry->peer,
530                            entry,
531                            i);
532     GNUNET_free (entry);
533     path->entries[i] = NULL;
534   }
535   if (NULL == hn)
536   {
537     /* None of the peers on the path care about it. */
538     LOG (GNUNET_ERROR_TYPE_DEBUG,
539          "Path discovered from DHT is not interesting to us\n");
540     GNUNET_free (path->entries);
541     GNUNET_free (path);
542     return;
543   }
544   path->hn = hn;
545   /* Shrink path to actual useful length */
546   GNUNET_array_grow (path->entries,
547                      path->entries_length,
548                      i + 1);
549   LOG (GNUNET_ERROR_TYPE_DEBUG,
550        "Created new path %s based on information from DHT\n",
551        GCPP_2s (path));
552 }
553
554
555 /**
556  * We got an incoming connection, obtain the corresponding path.
557  *
558  * @param path_length number of segments on the @a path
559  * @param pids path through the network, in reverse order (we are at the end at index @a path_length)
560  * @return corresponding path object
561  */
562 struct CadetPeerPath *
563 GCPP_get_path_from_route (unsigned int path_length,
564                           const struct GNUNET_PeerIdentity *pids)
565 {
566   struct CheckMatchContext cm_ctx;
567   struct CadetPeer *cpath[path_length];
568   struct CadetPeerPath *path;
569
570   /* precompute inverted 'cpath' so we can avoid doing the lookups and
571      have the correct order */
572   for (unsigned int off=0;off<path_length;off++)
573     cpath[off] = GCP_get (&pids[path_length - 1 - off],
574                           GNUNET_YES);
575
576   /* First figure out if this path is a subset of an existing path, an
577      extension of an existing path, or a new path. */
578   cm_ctx.cpath = cpath;
579   cm_ctx.cpath_length = path_length;
580   cm_ctx.match = NULL;
581   for (int i=path_length-1;i>=0;i--)
582   {
583     GCP_iterate_paths_at (cpath[i],
584                           (unsigned int) i,
585                           &check_match,
586                           &cm_ctx);
587     if (NULL != cm_ctx.match)
588     {
589       if (i == path_length - 1)
590       {
591         /* Existing path includes this one, return the match! */
592         LOG (GNUNET_ERROR_TYPE_DEBUG,
593              "Returning existing path %s as inverse for incoming connection\n",
594              GCPP_2s (cm_ctx.match));
595         return cm_ctx.match;
596       }
597       if (cm_ctx.match->entries_length == i + 1)
598       {
599         /* Existing path ends in the middle of new path, extend it! */
600         LOG (GNUNET_ERROR_TYPE_DEBUG,
601              "Extending existing path %s to create inverse for incoming connection\n",
602              GCPP_2s (cm_ctx.match));
603         extend_path (cm_ctx.match,
604                      &cpath[i],
605                      path_length - i,
606                      GNUNET_YES);
607         /* Check that extension was successful */
608         GNUNET_assert (cm_ctx.match->entries_length == path_length);
609         return cm_ctx.match;
610       }
611       /* Eh, we found a match but couldn't use it? Something is wrong. */
612       GNUNET_break (0);
613     }
614   }
615
616   /* No match at all, create completely new path */
617   path = GNUNET_new (struct CadetPeerPath);
618   path->entries_length = path_length;
619   path->entries = GNUNET_new_array (path->entries_length,
620                                     struct CadetPeerPathEntry *);
621   for (int i=path_length-1;i>=0;i--)
622   {
623     struct CadetPeerPathEntry *entry = GNUNET_new (struct CadetPeerPathEntry);
624
625     path->entries[i] = entry;
626     entry->peer = cpath[i];
627     entry->path = path;
628   }
629   for (int i=path_length-1;i>=0;i--)
630   {
631     struct CadetPeerPathEntry *entry = path->entries[i];
632
633     GCP_path_entry_add (entry->peer,
634                         entry,
635                         i);
636   }
637   LOG (GNUNET_ERROR_TYPE_DEBUG,
638        "Created new path %s to create inverse for incoming connection\n",
639        GCPP_2s (path));
640   path->hn = GCP_attach_path (cpath[path_length - 1],
641                               path,
642                               path_length - 1,
643                               GNUNET_YES);
644   return path;
645 }
646
647
648 /**
649  * Return the length of the path.  Excludes one end of the
650  * path, so the loopback path has length 0.
651  *
652  * @param path path to return the length for
653  * @return number of peers on the path
654  */
655 unsigned int
656 GCPP_get_length (struct CadetPeerPath *path)
657 {
658   return path->entries_length;
659 }
660
661
662 /**
663  * Find peer's offset on path.
664  *
665  * @param path path to search
666  * @param cp peer to look for
667  * @return offset of @a cp on @a path, or UINT_MAX if not found
668  */
669 unsigned int
670 GCPP_find_peer (struct CadetPeerPath *path,
671                 struct CadetPeer *cp)
672 {
673   for (unsigned int off = 0;
674        off < path->entries_length;
675        off++)
676     if (cp == GCPP_get_peer_at_offset (path,
677                                        off))
678       return off;
679   return UINT_MAX;
680 }
681
682
683 /**
684  * Obtain the peer at offset @a off in @a path.
685  *
686  * @param path peer path to inspect
687  * @param off offset to return, must be smaller than path length
688  * @return the peer at offset @a off
689  */
690 struct CadetPeer *
691 GCPP_get_peer_at_offset (struct CadetPeerPath *path,
692                          unsigned int off)
693 {
694   GNUNET_assert (off < path->entries_length);
695   return path->entries[off]->peer;
696 }
697
698
699 /**
700  * Convert a path to a human-readable string.
701  *
702  * @param path path to convert
703  * @return string, to be freed by caller (unlike other *_2s APIs!)
704  */
705 const char *
706 GCPP_2s (struct CadetPeerPath *path)
707 {
708   static char buf[2048];
709   size_t off;
710   const unsigned int max_plen = (sizeof(buf) - 16) / 5 - 2; /* 5 characters per entry */
711
712   off = 0;
713   for (unsigned int i = 0;
714        i < path->entries_length;
715        i++)
716   {
717     if ( (path->entries_length > max_plen) &&
718          (i == max_plen / 2) )
719       off += GNUNET_snprintf (&buf[off],
720                               sizeof (buf) - off,
721                               "...-");
722     if ( (path->entries_length > max_plen) &&
723          (i > max_plen / 2) &&
724          (i < path->entries_length - max_plen / 2) )
725       continue;
726     off += GNUNET_snprintf (&buf[off],
727                             sizeof (buf) - off,
728                             "%s%s",
729                             GNUNET_i2s (GCP_get_id (GCPP_get_peer_at_offset (path,
730                                                                              i))),
731                             (i == path->entries_length -1) ? "" : "-");
732   }
733   GNUNET_snprintf (&buf[off],
734                    sizeof (buf) - off,
735                    "(%p)",
736                    path);
737   return buf;
738 }
739
740
741 /* end of gnunet-service-cadet-new_paths.c */