session end function must include address to notify address
[oweals/gnunet.git] / src / nat / nat_auto.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file nat/nat_auto.c
23  * @brief functions for auto-configuration of the network
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_resolver_service.h"
29 #include "gnunet_nat_lib.h"
30 #include "nat.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "nat", __VA_ARGS__)
33
34
35 /**
36  * How long do we wait for the NAT test to report success?
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
39
40 /**
41  * Phases of the auto configuration.
42  */
43 enum AutoPhase
44 {
45   /**
46    * Initial start value.
47    */
48   AUTO_INIT = 0,
49
50   /**
51    * Test if we are online.
52    */
53   AUTO_ONLINE,
54
55   /**
56    * Test our external IP.
57    */
58   AUTO_EXTERNAL_IP,
59
60   /**
61    * Test our internal IP.
62    */
63   AUTO_LOCAL_IP,
64
65   /**
66    * Test if NAT was punched.
67    */
68   AUTO_NAT_PUNCHED,
69
70   /**
71    * Test if UPnP is working.
72    */
73   AUTO_UPNPC,
74
75   /**
76    * Test if ICMP server works.
77    */
78   AUTO_ICMP_SERVER,
79
80   /**
81    * Test if ICMP client works.
82    */
83   AUTO_ICMP_CLIENT,
84
85   /**
86    * Last phase, we're done.
87    */
88   AUTO_DONE
89
90 };
91
92
93 /**
94  * Handle to auto-configuration in progress.
95  */
96 struct GNUNET_NAT_AutoHandle
97 {
98
99   /**
100    * Handle to the active NAT test.
101    */
102   struct GNUNET_NAT_Test *tst;
103
104   /**
105    * Function to call when done.
106    */
107   GNUNET_NAT_AutoResultCallback fin_cb;
108
109   /**
110    * Closure for @e fin_cb.
111    */
112   void *fin_cb_cls;
113
114   /**
115    * Handle for active 'GNUNET_NAT_mini_get_external_ipv4'-operation.
116    */
117   struct GNUNET_NAT_ExternalHandle *eh;
118
119   /**
120    * Current configuration (with updates from previous phases)
121    */
122   struct GNUNET_CONFIGURATION_Handle *cfg;
123
124   /**
125    * Original configuration (used to calculate differences)
126    */
127   struct GNUNET_CONFIGURATION_Handle *initial_cfg;
128
129   /**
130    * Task identifier for the timeout.
131    */
132   GNUNET_SCHEDULER_TaskIdentifier task;
133
134   /**
135    * Where are we in the test?
136    */
137   enum AutoPhase phase;
138
139   /**
140    * Do we have IPv6?
141    */
142   int have_v6;
143
144 };
145
146
147 /**
148  * Run the next phase of the auto test.
149  *
150  * @param ah auto test handle
151  */
152 static void
153 next_phase (struct GNUNET_NAT_AutoHandle *ah);
154
155
156 /**
157  * Function called if NAT failed to confirm success.
158  * Clean up and update GUI (with failure).
159  *
160  * @param cls closure with setup context
161  * @param tc scheduler callback
162  */
163 static void
164 fail_timeout (void *cls,
165               const struct GNUNET_SCHEDULER_TaskContext *tc)
166 {
167   struct GNUNET_NAT_AutoHandle *ah = cls;
168
169   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
170               _("NAT traversal with ICMP Server timed out.\n"));
171   GNUNET_assert (NULL != ah->tst);
172   ah->task = GNUNET_SCHEDULER_NO_TASK;
173   GNUNET_NAT_test_stop (ah->tst);
174   ah->tst = NULL;
175   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
176                                          "ENABLE_ICMP_SERVER",
177                                          "NO");
178   next_phase (ah);
179 }
180
181
182 /**
183  * Function called by NAT on success.
184  * Clean up and update GUI (with success).
185  *
186  * @param cls the auto handle
187  * @param success currently always #GNUNET_OK
188  * @param emsg NULL on success, otherwise an error message
189  */
190 static void
191 result_callback (void *cls,
192                  int success,
193                  const char *emsg)
194 {
195   struct GNUNET_NAT_AutoHandle *ah = cls;
196
197   GNUNET_SCHEDULER_cancel (ah->task);
198   ah->task = GNUNET_SCHEDULER_NO_TASK;
199   GNUNET_NAT_test_stop (ah->tst);
200   ah->tst = NULL;
201   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
202               success
203               ? _("NAT traversal with ICMP Server succeeded.\n")
204               : _("NAT traversal with ICMP Server failed.\n"));
205   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER",
206                                          success ? "YES": "NO");
207   next_phase (ah);
208 }
209
210
211 /**
212  * Main function for the connection reversal test.
213  *
214  * @param cls the `struct GNUNET_NAT_AutoHandle`
215  * @param tc scheduler context
216  */
217 static void
218 reversal_test (void *cls,
219                const struct GNUNET_SCHEDULER_TaskContext *tc)
220 {
221   struct GNUNET_NAT_AutoHandle *ah = cls;
222
223   ah->task = GNUNET_SCHEDULER_NO_TASK;
224   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
225               _("Testing connection reversal with ICMP server.\n"));
226   GNUNET_RESOLVER_connect (ah->cfg);
227   ah->tst = GNUNET_NAT_test_start (ah->cfg, GNUNET_YES, 0, 0,
228                                    &result_callback, ah);
229   if (NULL == ah->tst)
230   {
231     next_phase (ah);
232     return;
233   }
234   ah->task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &fail_timeout, ah);
235 }
236
237
238 /**
239  * Test if we are online at all.
240  *
241  * @param ah auto setup context
242  */
243 static void
244 test_online (struct GNUNET_NAT_AutoHandle *ah)
245 {
246   // FIXME: not implemented
247   next_phase (ah);
248 }
249
250
251 /**
252  * Set our external IPv4 address.
253  *
254  * @param cls closure with our setup context
255  * @param addr the address, NULL on errors
256  * @param emsg NULL on success, otherwise an error message
257  */
258 static void
259 set_external_ipv4 (void *cls,
260                    const struct in_addr *addr,
261                    const char *emsg)
262 {
263   struct GNUNET_NAT_AutoHandle *ah = cls;
264   char buf[INET_ADDRSTRLEN];
265
266   ah->eh = NULL;
267   if (NULL == addr)
268   {
269     next_phase (ah);
270     return;
271   }
272   /* enable 'behind nat' */
273   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
274               _("Detected external IP `%s'\n"),
275               inet_ntop (AF_INET,
276                          addr,
277                          buf,
278                          sizeof (buf)));
279   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES");
280
281   /* set external IP address */
282   if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf)))
283   {
284     GNUNET_break (0);
285     next_phase (ah);
286     return;
287   }
288   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
289                                          buf);
290   next_phase (ah);
291 }
292
293
294 /**
295  * Determine our external IPv4 address.
296  *
297  * @param ah auto setup context
298  */
299 static void
300 test_external_ip (struct GNUNET_NAT_AutoHandle *ah)
301 {
302   // FIXME: CPS?
303   /* try to detect external IP */
304   ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT,
305                                               &set_external_ipv4, ah);
306 }
307
308
309 /**
310  * Process list of local IP addresses.  Find and set the
311  * one of the default interface.
312  *
313  * @param cls our `struct GNUNET_NAT_AutoHandle`
314  * @param name name of the interface (can be NULL for unknown)
315  * @param isDefault is this presumably the default interface
316  * @param addr address of this interface (can be NULL for unknown or unassigned)
317  * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
318  * @param netmask the network mask (can be NULL for unknown or unassigned))
319  * @param addrlen length of the @a addr and @a broadcast_addr
320  * @return GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
321  */
322 static int
323 nipo (void *cls,
324       const char *name,
325       int isDefault,
326       const struct sockaddr *addr,
327       const struct sockaddr *broadcast_addr,
328       const struct sockaddr *netmask,
329       socklen_t addrlen)
330 {
331   struct GNUNET_NAT_AutoHandle *ah = cls;
332   const struct sockaddr_in *in;
333   char buf[INET_ADDRSTRLEN];
334
335   if (!isDefault)
336     return GNUNET_OK;
337   if ( (sizeof (struct sockaddr_in6) == addrlen) &&
338        (0 != memcmp (&in6addr_loopback, &((const struct sockaddr_in6 *) addr)->sin6_addr,
339                      sizeof (struct in6_addr))) &&
340        (! IN6_IS_ADDR_LINKLOCAL(&((const struct sockaddr_in6 *) addr)->sin6_addr)) )
341   {
342     ah->have_v6 = GNUNET_YES;
343     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
344                 _("This system has a global IPv6 address, setting IPv6 to supported.\n"));
345     return GNUNET_OK;
346   }
347   if (addrlen != sizeof (struct sockaddr_in))
348     return GNUNET_OK;
349   in = (const struct sockaddr_in *) addr;
350
351   /* set internal IP address */
352   if (NULL == inet_ntop (AF_INET, &in->sin_addr, buf, sizeof (buf)))
353   {
354     GNUNET_break (0);
355     return GNUNET_OK;
356   }
357   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
358                                          buf);
359   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
360               _("Detected internal network address `%s'.\n"),
361               buf);
362   /* no need to continue iteration */
363   return GNUNET_SYSERR;
364 }
365
366
367 /**
368  * Determine our local IP addresses; detect internal IP & IPv6-support
369  *
370  * @param ah auto setup context
371  */
372 static void
373 test_local_ip (struct GNUNET_NAT_AutoHandle *ah)
374 {
375   ah->have_v6 = GNUNET_NO;
376   GNUNET_OS_network_interfaces_list (&nipo, ah);
377   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6",
378                                          (GNUNET_YES == ah->have_v6) ? "NO" : "YES");
379   next_phase (ah);
380 }
381
382
383 /**
384  * Test if NAT has been punched
385  *
386  * @param ah auto setup context
387  */
388 static void
389 test_nat_punched (struct GNUNET_NAT_AutoHandle *ah)
390 {
391   // FIXME: not implemented
392   next_phase (ah);
393 }
394
395
396 /**
397  * Test if UPnPC works.
398  *
399  * @param ah auto setup context
400  */
401 static void
402 test_upnpc (struct GNUNET_NAT_AutoHandle *ah)
403 {
404   int have_upnpc;
405
406   /* test if upnpc is available */
407   have_upnpc = (GNUNET_SYSERR !=
408                 GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL));
409   /* FIXME: test if upnpc is actually working, that is, if transports
410      start to work once we use UPnP */
411   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
412               (have_upnpc)
413               ? _("upnpc found, enabling its use\n")
414               : _("upnpc not found\n"));
415   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP",
416                                          (GNUNET_YES == have_upnpc) ? "YES" : "NO");
417   next_phase (ah);
418 }
419
420
421 /**
422  * Test if ICMP server is working
423  *
424  * @param ah auto setup context
425  */
426 static void
427 test_icmp_server (struct GNUNET_NAT_AutoHandle *ah)
428 {
429   int hns;
430   char *tmp;
431   char *binary;
432
433   tmp = NULL;
434   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server");
435   hns =
436       ((GNUNET_OK ==
437         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS",
438                                                &tmp)) && (0 < strlen (tmp)) &&
439        (GNUNET_YES ==
440         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")) &&
441        (GNUNET_YES ==
442         GNUNET_OS_check_helper_binary (binary, GNUNET_YES, "-d 127.0.0.1" ))); // use localhost as source for that one udp-port, ok for testing
443   GNUNET_free_non_null (tmp);
444   GNUNET_free (binary);
445   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
446               (hns)
447               ? _("gnunet-helper-nat-server found, testing it\n")
448               : _("No working gnunet-helper-nat-server found\n"));
449   if (hns)
450     ah->task = GNUNET_SCHEDULER_add_now (&reversal_test, ah);
451   else
452     next_phase (ah);
453 }
454
455
456 /**
457  * Test if ICMP client is working
458  *
459  * @param ah auto setup context
460  */
461 static void
462 test_icmp_client (struct GNUNET_NAT_AutoHandle *ah)
463 {
464   int hnc;
465   char *tmp;
466   char *binary;
467
468   tmp = NULL;
469   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client");
470   hnc =
471       ((GNUNET_OK ==
472         GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS",
473                                                &tmp)) && (0 < strlen (tmp)) &&
474        (GNUNET_YES !=
475         GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")) &&
476        (GNUNET_YES ==
477         GNUNET_OS_check_helper_binary (binary, GNUNET_YES, "-d 127.0.0.1 127.0.0.2 42"))); // none of these parameters are actually used in privilege testing mode
478   GNUNET_free_non_null (tmp);
479   GNUNET_free (binary);
480   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
481               (hnc)
482               ? _("gnunet-helper-nat-client found, enabling it\n")
483               : _("gnunet-helper-nat-client not found or behind NAT, disabling it\n"));
484   next_phase (ah);
485 }
486
487
488 /**
489  * Run the next phase of the auto test.
490  */
491 static void
492 next_phase (struct GNUNET_NAT_AutoHandle *ah)
493 {
494   struct GNUNET_CONFIGURATION_Handle *diff;
495
496   ah->phase++;
497   switch (ah->phase)
498   {
499   case AUTO_INIT:
500     GNUNET_assert (0);
501     break;
502   case AUTO_ONLINE:
503     test_online (ah);
504     break;
505   case AUTO_EXTERNAL_IP:
506     test_external_ip (ah);
507     break;
508   case AUTO_LOCAL_IP:
509     test_local_ip (ah);
510     break;
511   case AUTO_NAT_PUNCHED:
512     test_nat_punched (ah);
513     break;
514   case AUTO_UPNPC:
515     test_upnpc (ah);
516     break;
517   case AUTO_ICMP_SERVER:
518     test_icmp_server (ah);
519     break;
520   case AUTO_ICMP_CLIENT:
521     test_icmp_client (ah);
522     break;
523   case AUTO_DONE:
524     diff = GNUNET_CONFIGURATION_get_diff (ah->initial_cfg,
525                                           ah->cfg);
526     ah->fin_cb (ah->fin_cb_cls,
527                 diff,
528                 NULL);
529     GNUNET_CONFIGURATION_destroy (diff);
530     GNUNET_NAT_autoconfig_cancel (ah);
531     return;
532   }
533 }
534
535
536 /**
537  * Start auto-configuration routine.  The resolver service should
538  * be available when this function is called.
539  *
540  * @param cfg initial configuration
541  * @param cb function to call with autoconfiguration result
542  * @param cb_cls closure for @a cb
543  * @return handle to cancel operation
544  */
545 struct GNUNET_NAT_AutoHandle *
546 GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
547                              GNUNET_NAT_AutoResultCallback cb,
548                              void *cb_cls)
549 {
550   struct GNUNET_NAT_AutoHandle *ah;
551
552   ah = GNUNET_new (struct GNUNET_NAT_AutoHandle);
553   ah->fin_cb = cb;
554   ah->fin_cb_cls = cb_cls;
555   ah->cfg = GNUNET_CONFIGURATION_dup (cfg);
556   ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg);
557
558   /* never use loopback addresses if user wanted autoconfiguration */
559   GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat",
560                                          "USE_LOCALADDR",
561                                          "NO");
562   next_phase (ah);
563   return ah;
564 }
565
566
567 /**
568  * Abort autoconfiguration.
569  *
570  * @param ah handle for operation to abort
571  */
572 void
573 GNUNET_NAT_autoconfig_cancel (struct GNUNET_NAT_AutoHandle *ah)
574 {
575   if (NULL != ah->tst)
576   {
577     GNUNET_NAT_test_stop (ah->tst);
578     ah->tst = NULL;
579   }
580   if (NULL != ah->eh)
581   {
582     GNUNET_NAT_mini_get_external_ipv4_cancel (ah->eh);
583     ah->eh = NULL;
584   }
585   if (GNUNET_SCHEDULER_NO_TASK != ah->task)
586   {
587     GNUNET_SCHEDULER_cancel (ah->task);
588     ah->task = GNUNET_SCHEDULER_NO_TASK;
589   }
590   GNUNET_CONFIGURATION_destroy (ah->cfg);
591   GNUNET_CONFIGURATION_destroy (ah->initial_cfg);
592   GNUNET_free (ah);
593 }
594
595
596 /* end of nat_auto.c */