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