2 This file is part of GNUnet.
3 Copyright (C) 2015, 2016 GNUnet e.V.
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.
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.
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.
22 * @file src/nat/gnunet-nat.c
23 * @brief Command-line tool to interact with the NAT service
24 * @author Christian Grothoff
25 * @author Bruno Cabral
28 #include "gnunet_util_lib.h"
29 #include "gnunet_nat_service.h"
32 * Value to return from #main().
34 static int global_ret;
37 * Handle to ongoing autoconfiguration.
39 static struct GNUNET_NAT_AutoHandle *ah;
42 * External hostname and port, if user manually punched
45 static char *hole_external;
48 * Flag set to 1 if we use IPPROTO_UDP.
53 * Flag set to 1 if we are to listen for connection reversal requests.
55 static int listen_reversal;
58 * Flag set to 1 if we use IPPROTO_TCP.
63 * If we do auto-configuration, should we write the result
69 * Configuration filename.
71 static const char *cfg_file;
74 * Original configuration.
76 static const struct GNUNET_CONFIGURATION_Handle *cfg;
84 * Address we are bound to (in test), or should bind to
85 * (if #do_stun is set).
87 static char *bind_addr;
90 * External IP address and port to use for the test.
91 * If not set, use #bind_addr.
93 static char *extern_addr;
96 * Local address to use for connection reversal request.
98 static char *local_addr;
101 * Remote address to use for connection reversal request.
103 static char *remote_addr;
106 * Should we actually bind to #bind_addr and receive and process STUN requests?
108 static unsigned int do_stun;
111 * Should we run autoconfiguration?
113 static unsigned int do_auto;
116 * Handle to a NAT test operation.
118 static struct GNUNET_NAT_Test *nt;
121 * Handle to NAT operation.
123 static struct GNUNET_NAT_Handle *nh;
126 * Listen socket for STUN processing.
128 static struct GNUNET_NETWORK_Handle *ls;
131 * Task for reading STUN packets.
133 static struct GNUNET_SCHEDULER_Task *rtask;
137 * Test if all activities have finished, and if so,
151 GNUNET_SCHEDULER_shutdown ();
156 * Function to iterate over sugested changes options
159 * @param section name of the section
160 * @param option name of the option
161 * @param value value of the option
164 auto_conf_iter (void *cls,
169 struct GNUNET_CONFIGURATION_Handle *new_cfg = cls;
175 GNUNET_CONFIGURATION_set_value_string (new_cfg,
183 * Function called with the result from the autoconfiguration.
186 * @param diff minimal suggested changes to the original configuration
187 * to make it work (as best as we can)
188 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
189 * @param type what the situation of the NAT
192 auto_config_cb (void *cls,
193 const struct GNUNET_CONFIGURATION_Handle *diff,
194 enum GNUNET_NAT_StatusCode result,
195 enum GNUNET_NAT_Type type)
197 const char *nat_type;
198 char unknown_type[64];
199 struct GNUNET_CONFIGURATION_Handle *new_cfg;
204 case GNUNET_NAT_TYPE_NO_NAT:
207 case GNUNET_NAT_TYPE_UNREACHABLE_NAT:
208 nat_type = "NAT but we can traverse";
210 case GNUNET_NAT_TYPE_STUN_PUNCHED_NAT:
211 nat_type = "NAT but STUN is able to identify the correct information";
213 case GNUNET_NAT_TYPE_UPNP_NAT:
214 nat_type = "NAT but UPNP opened the ports";
217 SPRINTF (unknown_type,
218 "NAT unknown, type %u",
220 nat_type = unknown_type;
224 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
225 "NAT status: %s/%s\n",
226 GNUNET_NAT_status2string (result),
229 /* Shortcut: if there are no changes suggested, bail out early. */
231 GNUNET_CONFIGURATION_is_dirty (diff))
237 /* Apply diff to original configuration and show changes
239 new_cfg = write_cfg ? GNUNET_CONFIGURATION_dup (cfg) : NULL;
243 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
244 _("Suggested configuration changes:\n"));
245 GNUNET_CONFIGURATION_iterate_section_values (diff,
251 /* If desired, write configuration to file; we write only the
252 changes to the defaults to keep things compact. */
256 struct GNUNET_CONFIGURATION_Handle *def_cfg;
258 GNUNET_CONFIGURATION_set_value_string (new_cfg,
262 def_cfg = GNUNET_CONFIGURATION_create ();
263 GNUNET_break (GNUNET_OK ==
264 GNUNET_CONFIGURATION_load (def_cfg,
267 GNUNET_CONFIGURATION_write_diffs (def_cfg,
271 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
272 _("Failed to write configuration to `%s'\n"),
278 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
279 _("Wrote updated configuration to `%s'\n"),
282 GNUNET_CONFIGURATION_destroy (def_cfg);
286 GNUNET_CONFIGURATION_destroy (new_cfg);
292 * Function called to report success or failure for
293 * NAT configuration test.
296 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
299 test_report_cb (void *cls,
300 enum GNUNET_NAT_StatusCode result)
303 PRINTF ("NAT test result: %s\n",
304 GNUNET_NAT_status2string (result));
310 * Signature of the callback passed to #GNUNET_NAT_register() for
311 * a function to call whenever our set of 'valid' addresses changes.
313 * @param cls closure, NULL
314 * @param add_remove #GNUNET_YES to add a new public IP address,
315 * #GNUNET_NO to remove a previous (now invalid) one
316 * @param ac address class the address belongs to
317 * @param addr either the previous or the new public IP address
318 * @param addrlen actual length of the @a addr
321 address_cb (void *cls,
323 enum GNUNET_NAT_AddressClass ac,
324 const struct sockaddr *addr,
327 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
329 add_remove ? "+" : "-",
337 * Signature of the callback passed to #GNUNET_NAT_register().
338 * for a function to call whenever someone asks us to do connection
341 * @param cls closure, NULL
342 * @param remote_addr public IP address of the other peer
343 * @param remote_addrlen actual length of the @a remote_addr
346 reversal_cb (void *cls,
347 const struct sockaddr *remote_addr,
348 socklen_t remote_addrlen)
350 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
351 "Connection reversal requested by %s\n",
352 GNUNET_a2s (remote_addr,
358 * Task run on shutdown.
363 do_shutdown (void *cls)
367 GNUNET_NAT_autoconfig_cancel (ah);
372 GNUNET_NAT_test_stop (nt);
377 GNUNET_NAT_unregister (nh);
382 GNUNET_NETWORK_socket_close (ls);
387 GNUNET_SCHEDULER_cancel (rtask);
394 * Task to receive incoming packets for STUN processing.
397 stun_read_task (void *cls)
401 rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
405 size = GNUNET_NETWORK_socket_recvfrom_amount (ls);
409 GNUNET_SCHEDULER_shutdown ();
415 struct sockaddr_storage sa;
416 socklen_t salen = sizeof (sa);
419 ret = GNUNET_NETWORK_socket_recvfrom (ls,
422 (struct sockaddr *) &sa,
427 GNUNET_SCHEDULER_shutdown ();
431 (void) GNUNET_NAT_stun_handle_packet (nh,
432 (const struct sockaddr *) &sa,
441 * Main function that will be run.
444 * @param args remaining command-line arguments
445 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
446 * @param c configuration
452 const struct GNUNET_CONFIGURATION_Handle *c)
455 struct sockaddr_in bind_sa;
456 struct sockaddr_in extern_sa;
457 struct sockaddr *local_sa;
458 struct sockaddr *remote_sa;
465 if (use_tcp && use_udp)
467 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
468 "Cannot use TCP and UDP\n");
478 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
483 ah = GNUNET_NAT_autoconfig_start (c,
491 return; /* all good, we just run auto config */
492 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
493 "Must specify either TCP or UDP\n");
497 if (NULL != bind_addr)
500 GNUNET_STRINGS_to_address_ipv4 (bind_addr,
504 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
505 "Invalid socket address `%s'\n",
511 if (NULL != extern_addr)
514 GNUNET_STRINGS_to_address_ipv4 (extern_addr,
515 strlen (extern_addr),
518 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
519 "Invalid socket address `%s'\n",
525 if (NULL != local_addr)
527 local_len = (socklen_t) GNUNET_STRINGS_parse_socket_addr (local_addr,
532 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
533 "Invalid socket address `%s'\n",
539 if (NULL != remote_addr)
541 remote_len = GNUNET_STRINGS_parse_socket_addr (remote_addr,
546 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
547 "Invalid socket address `%s'\n",
554 if (NULL != bind_addr)
556 if (NULL == extern_addr)
558 nt = GNUNET_NAT_test_start (c,
561 ntohs (bind_sa.sin_port),
563 ntohs (extern_sa.sin_port),
568 if (NULL != local_addr)
570 nh = GNUNET_NAT_register (c,
574 (const struct sockaddr **) &local_sa,
577 (listen_reversal) ? &reversal_cb : NULL,
580 else if (listen_reversal)
582 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
583 "Use of `-W` only effective in combination with `-i`\n");
585 GNUNET_SCHEDULER_shutdown ();
589 if (NULL != remote_addr)
594 (sizeof (struct sockaddr_in) != local_len) )
596 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
597 "Require IPv4 local address to initiate connection reversal\n");
599 GNUNET_SCHEDULER_shutdown ();
602 if (sizeof (struct sockaddr_in) != remote_len)
604 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
605 "Require IPv4 reversal target address\n");
607 GNUNET_SCHEDULER_shutdown ();
610 ret = GNUNET_NAT_request_reversal (nh,
611 (const struct sockaddr_in *) &local_sa,
612 (const struct sockaddr_in *) &remote_sa);
616 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
617 "Connection reversal internal error\n");
620 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
621 "Connection reversal unavailable\n");
624 /* operation in progress */
631 if (NULL == local_addr)
633 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
634 "Require local address to support STUN requests\n");
636 GNUNET_SCHEDULER_shutdown ();
639 if (IPPROTO_UDP != proto)
641 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
642 "STUN only supported over UDP\n");
644 GNUNET_SCHEDULER_shutdown ();
647 ls = GNUNET_NETWORK_socket_create (af,
651 GNUNET_NETWORK_socket_bind (ls,
655 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
656 "Failed to bind to %s: %s\n",
657 GNUNET_a2s (local_sa,
661 GNUNET_SCHEDULER_shutdown ();
664 rtask = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
675 * Main function of gnunet-nat
677 * @param argc number of command-line arguments
678 * @param argv command line
679 * @return 0 on success, -1 on error
685 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
687 gettext_noop ("run autoconfiguration"),
688 GNUNET_NO, &GNUNET_GETOPT_set_one, &do_auto },
689 {'b', "bind", "ADDRESS",
690 gettext_noop ("which IP and port are we bound to"),
691 GNUNET_YES, &GNUNET_GETOPT_set_string, &bind_addr },
692 {'e', "external", "ADDRESS",
693 gettext_noop ("which external IP and port should be used to test"),
694 GNUNET_YES, &GNUNET_GETOPT_set_string, &extern_addr },
695 {'i', "in", "ADDRESS",
696 gettext_noop ("which IP and port are we locally using to bind/listen to"),
697 GNUNET_YES, &GNUNET_GETOPT_set_string, &local_addr },
698 {'r', "remote", "ADDRESS",
699 gettext_noop ("which remote IP and port should be asked for connection reversal"),
700 GNUNET_YES, &GNUNET_GETOPT_set_string, &remote_addr },
701 {'p', "punched", NULL,
702 gettext_noop ("external hostname and port of NAT, if punched manually; use AUTO for hostname for automatic determination of the external IP"),
703 GNUNET_YES, &GNUNET_GETOPT_set_string, &hole_external },
705 gettext_noop ("enable STUN processing"),
706 GNUNET_NO, &GNUNET_GETOPT_set_one, &do_stun },
708 gettext_noop ("use TCP"),
709 GNUNET_NO, &GNUNET_GETOPT_set_one, &use_tcp },
711 gettext_noop ("use UDP"),
712 GNUNET_NO, &GNUNET_GETOPT_set_one, &use_udp },
714 gettext_noop ("write configuration file (for autoconfiguration)"),
715 GNUNET_NO, &GNUNET_GETOPT_set_one, &write_cfg },
717 gettext_noop ("watch for connection reversal requests"),
718 GNUNET_NO, &GNUNET_GETOPT_set_one, &listen_reversal },
719 GNUNET_GETOPT_OPTION_END
723 GNUNET_STRINGS_get_utf8_args (argc, argv,
727 GNUNET_PROGRAM_run (argc, argv,
728 "gnunet-nat [options]",
729 _("GNUnet NAT traversal autoconfigure daemon"),
736 GNUNET_free ((void*) argv);
741 /* end of gnunet-nat.c */