2 This file is part of GNUnet.
3 Copyright (C) 2016, 2017 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 nat/gnunet-service-nat.c
23 * @brief network address translation traversal service
24 * @author Christian Grothoff
26 * The purpose of this service is to enable transports to
27 * traverse NAT routers, by providing traversal options and
28 * knowledge about the local network topology.
31 * - test and document (!) ICMP based NAT traversal
32 * - implement manual hole punching support (incl. DNS
33 * lookup for DynDNS setups!)
34 * - implement "more" autoconfig:
35 * re-work gnunet-nat-server & integrate!
36 * + test manually punched NAT (how?)
37 * - implement & test STUN processing to classify NAT;
38 * basically, open port & try different methods.
39 * - implement NEW logic for external IP detection
43 #include "gnunet_util_lib.h"
44 #include "gnunet_protocols.h"
45 #include "gnunet_signatures.h"
46 #include "gnunet_statistics_service.h"
47 #include "gnunet_resolver_service.h"
48 #include "gnunet_nat_service.h"
49 #include "gnunet-service-nat_stun.h"
50 #include "gnunet-service-nat_mini.h"
51 #include "gnunet-service-nat_helper.h"
57 * How often should we ask the OS about a list of active
60 #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
63 * How long do we wait until we forcefully terminate autoconfiguration?
65 #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
68 * How long do we wait until we re-try running `external-ip` if the
69 * command failed to terminate nicely?
71 #define EXTERN_IP_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
74 * How long do we wait until we re-try running `external-ip` if the
75 * command failed (but terminated)?
77 #define EXTERN_IP_RETRY_FAILURE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30)
80 * How long do we wait until we re-try running `external-ip` if the
83 #define EXTERN_IP_RETRY_SUCCESS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
86 * How often do we scan for changes in how our external (dyndns) hostname resolves?
88 #define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 7)
92 * Information we track per client address.
97 * Network address used by the client.
99 struct sockaddr_storage ss;
102 * Handle to active UPnP request where we asked upnpc to open
103 * a port at the NAT. NULL if we do not have such a request
106 struct GNUNET_NAT_MiniHandle *mh;
112 * Internal data structure we track for each of our clients.
120 struct ClientHandle *next;
125 struct ClientHandle *prev;
128 * Underlying handle for this client with the service.
130 struct GNUNET_SERVICE_Client *client;
133 * Message queue for communicating with the client.
135 struct GNUNET_MQ_Handle *mq;
138 * Array of addresses used by the service.
140 struct ClientAddress *caddrs;
143 * External DNS name and port given by user due to manual
144 * hole punching. Special DNS name 'AUTO' is used to indicate
145 * desire for automatic determination of the external IP
146 * (instead of DNS or manual configuration, i.e. to be used
147 * if the IP keeps changing and we have no DynDNS, but we do
148 * have a hole punched).
153 * Task for periodically re-running the @e ext_dns DNS lookup.
155 struct GNUNET_SCHEDULER_Task *ext_dns_task;
158 * Handle for (DYN)DNS lookup of our external IP as given in
161 struct GNUNET_RESOLVER_RequestHandle *ext_dns;
164 * External IP address as given in @e hole_external.
166 struct sockaddr_storage ext_addr;
169 * Do we currently have a valid @e ext_addr which we told the
175 * Port number we found in @e hole_external.
177 uint16_t ext_dns_port;
180 * What does this client care about?
182 enum GNUNET_NAT_RegisterFlags flags;
185 * Is any of the @e caddrs in a reserved subnet for NAT?
190 * Number of addresses that this service is bound to.
191 * Length of the @e caddrs array.
196 * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP.
204 * List of local addresses this system has.
206 struct LocalAddressList
209 * This is a linked list.
211 struct LocalAddressList *next;
216 struct LocalAddressList *prev;
219 * Context for a gnunet-helper-nat-server used to listen
220 * for ICMP messages to this client for connection reversal.
222 struct HelperContext *hc;
225 * The address itself (i.e. `struct sockaddr_in` or `struct
226 * sockaddr_in6`, in the respective byte order).
228 struct sockaddr_storage addr;
236 * What type of address is this?
238 enum GNUNET_NAT_AddressClass ac;
244 * External IP address as given to us via some STUN server.
246 struct StunExternalIP
251 struct StunExternalIP *next;
256 struct StunExternalIP *prev;
259 * Task we run to remove this entry when it is stale.
261 struct GNUNET_SCHEDULER_Task *timeout_task;
264 * Our external IP address as reported by the
267 struct sockaddr_in external_addr;
270 * Address of the reporting STUN server. Used to
271 * detect when a STUN server changes its opinion
272 * to more quickly remove stale results.
274 struct sockaddr_storage stun_server_addr;
277 * Number of bytes used in @e stun_server_addr.
279 size_t stun_server_addr_len;
284 * Context for autoconfiguration operations.
286 struct AutoconfigContext
291 struct AutoconfigContext *prev;
296 struct AutoconfigContext *next;
299 * Which client asked the question.
301 struct ClientHandle *ch;
304 * Configuration we are creating.
306 struct GNUNET_CONFIGURATION_Handle *c;
309 * Original configuration (for diffing).
311 struct GNUNET_CONFIGURATION_Handle *orig;
314 * Timeout task to force termination.
316 struct GNUNET_SCHEDULER_Task *timeout_task;
319 * What type of system are we on?
324 * Handle to activity to probe for our external IP.
326 struct GNUNET_NAT_ExternalHandle *probe_external;
329 * #GNUNET_YES if upnpc should be used,
330 * #GNUNET_NO if upnpc should not be used,
331 * #GNUNET_SYSERR if we should simply not change the option.
336 * Status code to return to the client.
338 enum GNUNET_NAT_StatusCode status_code;
341 * NAT type to return to the client.
343 enum GNUNET_NAT_Type type;
348 * DLL of our autoconfiguration operations.
350 static struct AutoconfigContext *ac_head;
353 * DLL of our autoconfiguration operations.
355 static struct AutoconfigContext *ac_tail;
358 * Timeout to use when STUN data is considered stale.
360 static struct GNUNET_TIME_Relative stun_stale_timeout;
363 * How often do we scan for changes in how our external (dyndns) hostname resolves?
365 static struct GNUNET_TIME_Relative dyndns_frequency;
368 * Handle to our current configuration.
370 static const struct GNUNET_CONFIGURATION_Handle *cfg;
373 * Handle to the statistics service.
375 static struct GNUNET_STATISTICS_Handle *stats;
378 * Task scheduled to periodically scan our network interfaces.
380 static struct GNUNET_SCHEDULER_Task *scan_task;
383 * Head of client DLL.
385 static struct ClientHandle *ch_head;
388 * Tail of client DLL.
390 static struct ClientHandle *ch_tail;
393 * Head of DLL of local addresses.
395 static struct LocalAddressList *lal_head;
398 * Tail of DLL of local addresses.
400 static struct LocalAddressList *lal_tail;
405 static struct StunExternalIP *se_head;
410 static struct StunExternalIP *se_tail;
413 * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
414 * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
416 static int enable_upnp;
419 * Task run to obtain our external IP (if #enable_upnp is set
420 * and if we find we have a NATed IP address).
422 static struct GNUNET_SCHEDULER_Task *probe_external_ip_task;
425 * Handle to our operation to run `external-ip`.
427 static struct GNUNET_NAT_ExternalHandle *probe_external_ip_op;
430 * What is our external IP address as claimed by `external-ip`?
433 static struct in_addr mini_external_ipv4;
437 * Remove and free an entry from the #lal_head DLL.
439 * @param lal entry to free
442 free_lal (struct LocalAddressList *lal)
444 GNUNET_CONTAINER_DLL_remove (lal_head,
449 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
450 "Lost NATed local address %s, stopping NAT server\n",
451 GNUNET_a2s ((const struct sockaddr *) &lal->addr,
452 sizeof (struct sockaddr_in)));
454 GN_stop_gnunet_nat_server_ (lal->hc);
462 * Free the DLL starting at #lal_head.
467 struct LocalAddressList *lal;
469 while (NULL != (lal = lal_head))
475 * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from
478 * @param cls client who sent the message
479 * @param message the message received
480 * @return #GNUNET_OK if message is well-formed
483 check_register (void *cls,
484 const struct GNUNET_NAT_RegisterMessage *message)
486 uint16_t num_addrs = ntohs (message->num_addrs);
487 const char *off = (const char *) &message[1];
488 size_t left = ntohs (message->header.size) - sizeof (*message);
490 for (unsigned int i=0;i<num_addrs;i++)
493 const struct sockaddr *sa = (const struct sockaddr *) off;
495 if (sizeof (sa_family_t) > left)
498 return GNUNET_SYSERR;
500 switch (sa->sa_family)
503 alen = sizeof (struct sockaddr_in);
506 alen = sizeof (struct sockaddr_in6);
510 alen = sizeof (struct sockaddr_un);
515 return GNUNET_SYSERR;
520 return GNUNET_SYSERR;
525 if (left != ntohs (message->hole_external_len))
528 return GNUNET_SYSERR;
535 * Check if @a ip is in @a network with @a bits netmask.
537 * @param network to test
538 * @param ip IP address to test
539 * @param bits bitmask for the network
540 * @return #GNUNET_YES if @a ip is in @a network
543 match_ipv4 (const char *network,
544 const struct in_addr *ip,
553 GNUNET_assert (1 == inet_pton (AF_INET,
556 return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits)));
561 * Check if @a ip is in @a network with @a bits netmask.
563 * @param network to test
564 * @param ip IP address to test
565 * @param bits bitmask for the network
566 * @return #GNUNET_YES if @a ip is in @a network
569 match_ipv6 (const char *network,
570 const struct in6_addr *ip,
574 struct in6_addr mask;
579 GNUNET_assert (1 == inet_pton (AF_INET6,
582 memset (&mask, 0, sizeof (mask));
583 if (0 == memcmp (&mask,
590 mask.s6_addr[off++] = 0xFF;
595 mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80;
598 for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++)
599 if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) !=
600 (((uint32_t *) &net)[j] & ((int *) &mask)[j]))
607 * Test if the given IPv4 address is in a known range
608 * for private networks.
610 * @param ip address to test
611 * @return #GNUNET_YES if @a ip is in a NAT range
614 is_nat_v4 (const struct in_addr *ip)
617 match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */
618 match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */
619 match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */
620 match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */
621 match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */
626 * Test if the given IPv6 address is in a known range
627 * for private networks.
629 * @param ip address to test
630 * @return #GNUNET_YES if @a ip is in a NAT range
633 is_nat_v6 (const struct in6_addr *ip)
636 match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */
637 match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */
638 match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */
643 * Closure for #ifc_proc.
645 struct IfcProcContext
649 * Head of DLL of local addresses.
651 struct LocalAddressList *lal_head;
654 * Tail of DLL of local addresses.
656 struct LocalAddressList *lal_tail;
662 * Callback function invoked for each interface found. Adds them
663 * to our new address list.
665 * @param cls a `struct IfcProcContext *`
666 * @param name name of the interface (can be NULL for unknown)
667 * @param isDefault is this presumably the default interface
668 * @param addr address of this interface (can be NULL for unknown or unassigned)
669 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
670 * @param netmask the network mask (can be NULL for unknown or unassigned)
671 * @param addrlen length of the address
672 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
678 const struct sockaddr *addr,
679 const struct sockaddr *broadcast_addr,
680 const struct sockaddr *netmask,
683 struct IfcProcContext *ifc_ctx = cls;
684 struct LocalAddressList *lal;
686 const struct in_addr *ip4;
687 const struct in6_addr *ip6;
688 enum GNUNET_NAT_AddressClass ac;
690 switch (addr->sa_family)
693 alen = sizeof (struct sockaddr_in);
694 ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
695 if (match_ipv4 ("127.0.0.0", ip4, 8))
696 ac = GNUNET_NAT_AC_LOOPBACK;
697 else if (is_nat_v4 (ip4))
698 ac = GNUNET_NAT_AC_LAN;
700 ac = GNUNET_NAT_AC_GLOBAL;
703 alen = sizeof (struct sockaddr_in6);
704 ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
705 if (match_ipv6 ("::1", ip6, 128))
706 ac = GNUNET_NAT_AC_LOOPBACK;
707 else if (is_nat_v6 (ip6))
708 ac = GNUNET_NAT_AC_LAN;
710 ac = GNUNET_NAT_AC_GLOBAL;
711 if ( (ip6->s6_addr[11] == 0xFF) &&
712 (ip6->s6_addr[12] == 0xFE) )
714 /* contains a MAC, be extra careful! */
715 ac |= GNUNET_NAT_AC_PRIVATE;
727 lal = GNUNET_malloc (sizeof (*lal));
728 lal->af = addr->sa_family;
730 GNUNET_memcpy (&lal->addr,
733 GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
741 * Notify client about a change in the list of addresses this peer
744 * @param ac address class of the entry in the list that changed
745 * @param ch client to contact
746 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
747 * @param addr the address that changed
748 * @param addr_len number of bytes in @a addr
751 notify_client (enum GNUNET_NAT_AddressClass ac,
752 struct ClientHandle *ch,
757 struct GNUNET_MQ_Envelope *env;
758 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
760 env = GNUNET_MQ_msg_extra (msg,
762 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
763 msg->add_remove = htonl (add);
764 msg->addr_class = htonl (ac);
765 GNUNET_memcpy (&msg[1],
768 GNUNET_MQ_send (ch->mq,
774 * Check if we should bother to notify this client about this
775 * address change, and if so, do it.
777 * @param delta the entry in the list that changed
778 * @param ch client to check
779 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
782 check_notify_client (struct LocalAddressList *delta,
783 struct ClientHandle *ch,
787 struct sockaddr_in v4;
788 struct sockaddr_in6 v6;
790 if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
795 alen = sizeof (struct sockaddr_in);
800 /* Check for client notifications */
801 for (unsigned int i=0;i<ch->num_caddrs;i++)
803 const struct sockaddr_in *c4;
805 if (AF_INET != ch->caddrs[i].ss.ss_family)
806 continue; /* IPv4 not relevant */
807 c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss;
808 if ( match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) &&
809 (0 != c4->sin_addr.s_addr) &&
810 (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) )
811 continue; /* bound to loopback, but this is not loopback */
812 if ( (! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) ) &&
813 (0 != c4->sin_addr.s_addr) &&
814 match_ipv4 ("127.0.0.1", &v4.sin_addr, 8) )
815 continue; /* bound to non-loopback, but this is loopback */
816 if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
817 (0 != c4->sin_addr.s_addr) &&
818 (! is_nat_v4 (&v4.sin_addr)) )
819 continue; /* based on external-IP, but this IP is not
820 from private address range. */
821 if ( (0 != memcmp (&v4.sin_addr,
823 sizeof (struct in_addr))) &&
824 (0 != c4->sin_addr.s_addr) &&
825 ( (! is_nat_v4 (&c4->sin_addr)) ||
826 (0 == (ch->flags & GNUNET_NAT_AC_EXTERN))) )
827 continue; /* this IP is not from private address range,
828 and IP does not match. */
830 /* OK, IP seems relevant, notify client */
831 v4.sin_port = c4->sin_port;
832 notify_client (delta->ac,
840 alen = sizeof (struct sockaddr_in6);
844 for (unsigned int i=0;i<ch->num_caddrs;i++)
846 const struct sockaddr_in6 *c6;
848 if (AF_INET6 != ch->caddrs[i].ss.ss_family)
849 continue; /* IPv4 not relevant */
850 c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss;
851 if ( match_ipv6 ("::1", &c6->sin6_addr, 128) &&
852 (0 != memcmp (&c6->sin6_addr,
854 sizeof (struct in6_addr))) &&
855 (! match_ipv6 ("::1", &v6.sin6_addr, 128)) )
856 continue; /* bound to loopback, but this is not loopback */
857 if ( (! match_ipv6 ("::1", &c6->sin6_addr, 128) ) &&
858 (0 != memcmp (&c6->sin6_addr,
860 sizeof (struct in6_addr))) &&
861 match_ipv6 ("::1", &v6.sin6_addr, 128) )
862 continue; /* bound to non-loopback, but this is loopback */
863 if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
864 (0 != memcmp (&c6->sin6_addr,
866 sizeof (struct in6_addr))) &&
867 (! is_nat_v6 (&v6.sin6_addr)) )
868 continue; /* based on external-IP, but this IP is not
869 from private address range. */
870 if ( (0 != memcmp (&v6.sin6_addr,
872 sizeof (struct in6_addr))) &&
873 (0 != memcmp (&c6->sin6_addr,
875 sizeof (struct in6_addr))) &&
876 (! is_nat_v6 (&c6->sin6_addr)) )
877 continue; /* this IP is not from private address range,
878 and IP does not match. */
879 if ( (match_ipv6 ("fe80::", &c6->sin6_addr, 10)) &&
880 (0 != memcmp (&c6->sin6_addr,
882 sizeof (struct in6_addr))) &&
883 (0 != memcmp (&v6.sin6_addr,
885 sizeof (struct in6_addr))) &&
886 (0 == (delta->ac & GNUNET_NAT_AC_EXTERN)) )
887 continue; /* client bound to link-local, and the other address
888 does not match and is not an external IP */
890 /* OK, IP seems relevant, notify client */
891 v6.sin6_port = c6->sin6_port;
892 notify_client (delta->ac,
907 * Notify all clients about a change in the list
908 * of addresses this peer has.
910 * @param delta the entry in the list that changed
911 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
914 notify_clients (struct LocalAddressList *delta,
917 for (struct ClientHandle *ch = ch_head;
920 check_notify_client (delta,
927 * Tell relevant client about a change in our external
930 * @param v4 the external address that changed
931 * @param ch client to check if it cares and possibly notify
932 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
935 check_notify_client_external_ipv4_change (const struct in_addr *v4,
936 struct ClientHandle *ch,
939 struct sockaddr_in sa;
942 /* (1) check if client cares. */
943 if (! ch->natted_address)
945 if (0 == (GNUNET_NAT_RF_ADDRESSES & ch->flags))
948 for (unsigned int i=0;i<ch->num_caddrs;i++)
950 const struct sockaddr_storage *ss = &ch->caddrs[i].ss;
952 if (AF_INET != ss->ss_family)
954 have_v4 = GNUNET_YES;
957 if (GNUNET_NO == have_v4)
958 return; /* IPv6-only */
960 /* build address info */
964 sa.sin_family = AF_INET;
966 sa.sin_port = htons (0);
968 /* (3) notify client of change */
969 notify_client (is_nat_v4 (v4)
970 ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN
971 : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL,
980 * Tell relevant clients about a change in our external
983 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
984 * @param v4 the external address that changed
987 notify_clients_external_ipv4_change (int add,
988 const struct in_addr *v4)
990 for (struct ClientHandle *ch = ch_head;
993 check_notify_client_external_ipv4_change (v4,
1000 * Task used to run `external-ip` to get our external IPv4
1001 * address and pass it to NATed clients if possible.
1006 run_external_ip (void *cls);
1010 * We learn our current external IP address. If it changed,
1011 * notify all of our applicable clients. Also re-schedule
1012 * #run_external_ip with an appropriate timeout.
1015 * @param addr the address, NULL on errors
1016 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
1019 handle_external_ip (void *cls,
1020 const struct in_addr *addr,
1021 enum GNUNET_NAT_StatusCode result)
1023 char buf[INET_ADDRSTRLEN];
1025 probe_external_ip_op = NULL;
1026 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
1027 probe_external_ip_task
1028 = GNUNET_SCHEDULER_add_delayed ((NULL == addr)
1029 ? EXTERN_IP_RETRY_FAILURE
1030 : EXTERN_IP_RETRY_SUCCESS,
1035 case GNUNET_NAT_ERROR_SUCCESS:
1036 if (addr->s_addr == mini_external_ipv4.s_addr)
1037 return; /* not change */
1038 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1039 "Our external IP is now %s\n",
1044 if (0 != mini_external_ipv4.s_addr)
1045 notify_clients_external_ipv4_change (GNUNET_NO,
1046 &mini_external_ipv4);
1047 mini_external_ipv4 = *addr;
1048 notify_clients_external_ipv4_change (GNUNET_YES,
1049 &mini_external_ipv4);
1052 if (0 != mini_external_ipv4.s_addr)
1053 notify_clients_external_ipv4_change (GNUNET_NO,
1054 &mini_external_ipv4);
1055 mini_external_ipv4.s_addr = 0;
1062 * Task used to run `external-ip` to get our external IPv4
1063 * address and pass it to NATed clients if possible.
1068 run_external_ip (void *cls)
1070 probe_external_ip_task
1071 = GNUNET_SCHEDULER_add_delayed (EXTERN_IP_RETRY_TIMEOUT,
1074 if (NULL != probe_external_ip_op)
1076 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
1077 probe_external_ip_op = NULL;
1079 probe_external_ip_op
1080 = GNUNET_NAT_mini_get_external_ipv4_ (&handle_external_ip,
1086 * We got a connection reversal request from another peer.
1087 * Notify applicable clients.
1089 * @param cls closure with the `struct LocalAddressList`
1090 * @param ra IP address of the peer who wants us to connect to it
1093 reversal_callback (void *cls,
1094 const struct sockaddr_in *ra)
1096 struct LocalAddressList *lal = cls;
1097 const struct sockaddr_in *l4;
1099 GNUNET_assert (AF_INET == lal->af);
1100 l4 = (const struct sockaddr_in *) &lal->addr;
1101 for (struct ClientHandle *ch = ch_head;
1105 struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm;
1106 struct GNUNET_MQ_Envelope *env;
1109 /* Check if client is in applicable range for ICMP NAT traversal
1110 for this local address */
1111 if (! ch->natted_address)
1114 for (unsigned int i=0;i<ch->num_caddrs;i++)
1116 struct ClientAddress *ca = &ch->caddrs[i];
1117 const struct sockaddr_in *c4;
1119 if (AF_INET != ca->ss.ss_family)
1121 c4 = (const struct sockaddr_in *) &ca->ss;
1122 if ( (0 != c4->sin_addr.s_addr) &&
1123 (l4->sin_addr.s_addr != c4->sin_addr.s_addr) )
1131 /* Notify applicable client about connection reversal request */
1132 env = GNUNET_MQ_msg_extra (crrm,
1133 sizeof (struct sockaddr_in),
1134 GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED);
1135 GNUNET_memcpy (&crrm[1],
1137 sizeof (struct sockaddr_in));
1138 GNUNET_MQ_send (ch->mq,
1145 * Task we run periodically to scan for network interfaces.
1150 run_scan (void *cls)
1152 struct IfcProcContext ifc_ctx;
1155 struct LocalAddressList *lnext;
1157 scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
1163 GNUNET_OS_network_interfaces_list (&ifc_proc,
1165 /* remove addresses that disappeared */
1166 for (struct LocalAddressList *lal = lal_head;
1172 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1176 if ( (pos->af == lal->af) &&
1177 (0 == memcmp (&lal->addr,
1179 (AF_INET == lal->af)
1180 ? sizeof (struct sockaddr_in)
1181 : sizeof (struct sockaddr_in6))) )
1186 if (GNUNET_NO == found)
1188 notify_clients (lal,
1194 /* add addresses that appeared */
1195 have_nat = GNUNET_NO;
1196 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1198 pos = ifc_ctx.lal_head)
1201 if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac))
1202 have_nat = GNUNET_YES;
1203 for (struct LocalAddressList *lal = lal_head;
1207 if ( (pos->af == lal->af) &&
1208 (0 == memcmp (&lal->addr,
1210 (AF_INET == lal->af)
1211 ? sizeof (struct sockaddr_in)
1212 : sizeof (struct sockaddr_in6))) )
1215 GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head,
1218 if (GNUNET_YES == found)
1224 notify_clients (pos,
1226 GNUNET_CONTAINER_DLL_insert (lal_head,
1229 if ( (AF_INET == pos->af) &&
1230 (NULL == pos->hc) &&
1231 (0 != (GNUNET_NAT_AC_LAN & pos->ac)) )
1233 const struct sockaddr_in *s4
1234 = (const struct sockaddr_in *) &pos->addr;
1236 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1237 "Found NATed local address %s, starting NAT server\n",
1238 GNUNET_a2s ((void *) &pos->addr, sizeof (*s4)));
1239 pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr,
1245 if ( (GNUNET_YES == have_nat) &&
1246 (GNUNET_YES == enable_upnp) &&
1247 (NULL == probe_external_ip_task) &&
1248 (NULL == probe_external_ip_op) )
1250 probe_external_ip_task
1251 = GNUNET_SCHEDULER_add_now (&run_external_ip,
1254 if ( (GNUNET_NO == have_nat) &&
1255 (GNUNET_YES == enable_upnp) )
1257 if (NULL != probe_external_ip_task)
1259 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
1260 probe_external_ip_task = NULL;
1262 if (NULL != probe_external_ip_op)
1264 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
1265 probe_external_ip_op = NULL;
1272 * Function called whenever our set of external addresses
1273 * as created by `upnpc` changes.
1275 * @param cls closure with our `struct ClientHandle *`
1276 * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
1277 * the previous (now invalid) one, #GNUNET_SYSERR indicates an error
1278 * @param addr either the previous or the new public IP address
1279 * @param addrlen actual length of the @a addr
1280 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
1283 upnp_addr_change_cb (void *cls,
1285 const struct sockaddr *addr,
1287 enum GNUNET_NAT_StatusCode result)
1289 struct ClientHandle *ch = cls;
1290 enum GNUNET_NAT_AddressClass ac;
1294 case GNUNET_NAT_ERROR_SUCCESS:
1295 GNUNET_assert (NULL != addr);
1297 case GNUNET_NAT_ERROR_UPNPC_FAILED:
1298 case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
1299 case GNUNET_NAT_ERROR_IPC_FAILURE:
1300 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1301 "Running upnpc failed: %d\n",
1304 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
1305 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1306 "external-ip binary not found\n");
1308 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
1309 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1310 "external-ip binary could not be run\n");
1312 case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
1313 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1314 "upnpc failed to create port mapping\n");
1316 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
1317 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1318 "Invalid output from upnpc\n");
1320 case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
1321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1322 "Invalid address returned by upnpc\n");
1325 GNUNET_break (0); /* should not be possible */
1328 switch (addr->sa_family)
1331 ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr)
1333 : GNUNET_NAT_AC_EXTERN;
1336 ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr)
1338 : GNUNET_NAT_AC_EXTERN;
1344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1345 "upnp external address %s: %s\n",
1346 add_remove ? "added" : "removed",
1358 * Resolve the `hole_external` name to figure out our
1359 * external address from a manually punched hole. The
1360 * port number has already been parsed, this task is
1361 * responsible for periodically doing a DNS lookup.
1363 * @param ch client handle to act upon
1366 dyndns_lookup (void *cls);
1370 * Our (external) hostname was resolved. Update lists of
1371 * current external IPs (note that DNS may return multiple
1372 * addresses!) and notify client accordingly.
1374 * @param cls the `struct ClientHandle`
1375 * @param addr NULL on error, otherwise result of DNS lookup
1376 * @param addrlen number of bytes in @a addr
1379 process_external_ip (void *cls,
1380 const struct sockaddr *addr,
1383 struct ClientHandle *ch = cls;
1389 = GNUNET_SCHEDULER_add_delayed (dyndns_frequency,
1392 /* Current iteration is over, remove 'old' IPs now */
1393 // FIXME: remove IPs we did NOT find!
1397 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1398 "Got IP `%s' for external address `%s'\n",
1402 // FIXME: notify client, and remember IP for later removal!
1407 * Resolve the `hole_external` name to figure out our
1408 * external address from a manually punched hole. The
1409 * port number has already been parsed, this task is
1410 * responsible for periodically doing a DNS lookup.
1412 * @param ch client handle to act upon
1415 dyndns_lookup (void *cls)
1417 struct ClientHandle *ch = cls;
1419 ch->ext_dns_task = NULL;
1420 ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external,
1422 GNUNET_TIME_UNIT_MINUTES,
1423 &process_external_ip,
1429 * Resolve the `hole_external` name to figure out our
1430 * external address from a manually punched hole. The
1431 * given name may be "AUTO" in which case we should use
1432 * the IP address(es) we have from upnpc or other methods.
1433 * The name can also be an IP address, in which case we
1434 * do not need to do DNS resolution. Finally, we also
1435 * need to parse the port number.
1437 * @param ch client handle to act upon
1440 lookup_hole_external (struct ClientHandle *ch)
1444 struct sockaddr_in *s4;
1445 struct LocalAddressList lal;
1447 port = strrchr (ch->hole_external, ':');
1450 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1451 _("Malformed punched hole specification `%s' (lacks port)\n"),
1455 if ( (1 != sscanf (port + 1,
1460 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1461 _("Invalid port number in punched hole specification `%s' (lacks port)\n"),
1465 ch->ext_dns_port = (uint16_t) pnum;
1467 if ('[' == *ch->hole_external)
1469 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ch->ext_addr;
1471 memset (s6, 0, sizeof (*s6));
1472 s6->sin6_family = AF_INET6;
1473 if (']' != (ch->hole_external[strlen(ch->hole_external)-1]))
1475 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1476 _("Malformed punched hole specification `%s' (lacks `]')\n"),
1480 ch->hole_external[strlen(ch->hole_external)-1] = '\0';
1481 if (1 != inet_pton (AF_INET6,
1482 ch->hole_external + 1,
1485 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1486 _("Malformed punched hole specification `%s' (IPv6 address invalid)"),
1487 ch->hole_external + 1);
1490 s6->sin6_port = htons (ch->ext_dns_port);
1491 memset (&lal, 0, sizeof (lal));
1492 GNUNET_memcpy (&lal.addr, s6, sizeof (*s6));
1494 lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1495 check_notify_client (&lal,
1498 ch->ext_addr_set = GNUNET_YES;
1501 s4 = (struct sockaddr_in *) &ch->ext_addr;
1502 memset (s4, 0, sizeof (*s4));
1503 s4->sin_family = AF_INET;
1504 if (1 == inet_pton (AF_INET,
1508 s4->sin_port = htons (ch->ext_dns_port);
1509 memset (&lal, 0, sizeof (lal));
1510 GNUNET_memcpy (&lal.addr, s4, sizeof (*s4));
1512 lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1513 check_notify_client (&lal,
1516 ch->ext_addr_set = GNUNET_YES;
1519 if (0 == strcasecmp (ch->hole_external,
1522 // FIXME: use `external-ip` address(es)!
1523 GNUNET_break (0); // not implemented!
1526 /* got a DNS name, trigger lookup! */
1528 = GNUNET_SCHEDULER_add_now (&dyndns_lookup,
1534 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
1535 * We remember the client for updates upon future NAT events.
1537 * @param cls client who sent the message
1538 * @param message the message received
1541 handle_register (void *cls,
1542 const struct GNUNET_NAT_RegisterMessage *message)
1544 struct ClientHandle *ch = cls;
1548 if ( (0 != ch->proto) ||
1549 (NULL != ch->caddrs) )
1551 /* double registration not allowed */
1553 GNUNET_SERVICE_client_drop (ch->client);
1556 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1557 "Received REGISTER message from client\n");
1558 ch->flags = message->flags;
1559 ch->proto = message->proto;
1560 ch->num_caddrs = ntohs (message->num_addrs);
1561 ch->caddrs = GNUNET_new_array (ch->num_caddrs,
1562 struct ClientAddress);
1563 left = ntohs (message->header.size) - sizeof (*message);
1564 off = (const char *) &message[1];
1565 for (unsigned int i=0;i<ch->num_caddrs;i++)
1568 const struct sockaddr *sa = (const struct sockaddr *) off;
1572 if (sizeof (sa_family_t) > left)
1575 GNUNET_SERVICE_client_drop (ch->client);
1579 switch (sa->sa_family)
1583 const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa;
1585 alen = sizeof (struct sockaddr_in);
1586 if (is_nat_v4 (&s4->sin_addr))
1587 is_nat = GNUNET_YES;
1588 port = ntohs (s4->sin_port);
1593 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa;
1595 alen = sizeof (struct sockaddr_in6);
1596 if (is_nat_v6 (&s6->sin6_addr))
1597 is_nat = GNUNET_YES;
1598 port = ntohs (s6->sin6_port);
1603 alen = sizeof (struct sockaddr_un);
1609 GNUNET_SERVICE_client_drop (ch->client);
1613 GNUNET_assert (alen <= left);
1614 GNUNET_assert (alen <= sizeof (struct sockaddr_storage));
1615 GNUNET_memcpy (&ch->caddrs[i].ss,
1619 /* If applicable, try UPNPC NAT punching */
1622 ( (IPPROTO_TCP == ch->proto) ||
1623 (IPPROTO_UDP == ch->proto) ) )
1625 ch->natted_address = GNUNET_YES;
1627 = GNUNET_NAT_mini_map_start (port,
1628 IPPROTO_TCP == ch->proto,
1629 &upnp_addr_change_cb,
1637 = GNUNET_strndup (off,
1638 ntohs (message->hole_external_len));
1639 if (0 != ntohs (message->hole_external_len))
1640 lookup_hole_external (ch);
1642 /* Actually send IP address list to client */
1643 for (struct LocalAddressList *lal = lal_head;
1647 check_notify_client (lal,
1651 /* Also consider IPv4 determined by `external-ip` */
1652 if (0 != mini_external_ipv4.s_addr)
1654 check_notify_client_external_ipv4_change (&mini_external_ipv4,
1658 GNUNET_SERVICE_client_continue (ch->client);
1663 * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
1666 * @param cls client who sent the message
1667 * @param message the message received
1668 * @return #GNUNET_OK if message is well-formed
1671 check_stun (void *cls,
1672 const struct GNUNET_NAT_HandleStunMessage *message)
1674 size_t sa_len = ntohs (message->sender_addr_size);
1675 size_t expect = sa_len + ntohs (message->payload_size);
1677 if (ntohs (message->header.size) - sizeof (*message) != expect)
1680 return GNUNET_SYSERR;
1682 if (sa_len < sizeof (sa_family_t))
1685 return GNUNET_SYSERR;
1692 * Notify all clients about our external IP address
1693 * as reported by the STUN server.
1695 * @param ip the external IP
1696 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
1699 notify_clients_stun_change (const struct sockaddr_in *ip,
1702 for (struct ClientHandle *ch = ch_head;
1706 struct sockaddr_in v4;
1707 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
1708 struct GNUNET_MQ_Envelope *env;
1710 if (! ch->natted_address)
1713 v4.sin_port = htons (0);
1714 env = GNUNET_MQ_msg_extra (msg,
1716 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
1717 msg->add_remove = htonl ((int32_t) add);
1718 msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN |
1719 GNUNET_NAT_AC_GLOBAL);
1720 GNUNET_memcpy (&msg[1],
1723 GNUNET_MQ_send (ch->mq,
1730 * Function to be called when we decide that an
1731 * external IP address as told to us by a STUN
1732 * server has gone stale.
1734 * @param cls the `struct StunExternalIP` to drop
1737 stun_ip_timeout (void *cls)
1739 struct StunExternalIP *se = cls;
1741 se->timeout_task = NULL;
1742 notify_clients_stun_change (&se->external_addr,
1744 GNUNET_CONTAINER_DLL_remove (se_head,
1752 * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
1755 * @param cls client who sent the message
1756 * @param message the message received
1759 handle_stun (void *cls,
1760 const struct GNUNET_NAT_HandleStunMessage *message)
1762 struct ClientHandle *ch = cls;
1763 const char *buf = (const char *) &message[1];
1764 const struct sockaddr *sa;
1765 const void *payload;
1767 size_t payload_size;
1768 struct sockaddr_in external_addr;
1770 sa_len = ntohs (message->sender_addr_size);
1771 payload_size = ntohs (message->payload_size);
1772 sa = (const struct sockaddr *) &buf[0];
1773 payload = (const struct sockaddr *) &buf[sa_len];
1774 switch (sa->sa_family)
1777 if (sa_len != sizeof (struct sockaddr_in))
1780 GNUNET_SERVICE_client_drop (ch->client);
1785 if (sa_len != sizeof (struct sockaddr_in6))
1788 GNUNET_SERVICE_client_drop (ch->client);
1793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1794 "Received HANDLE_STUN message from client\n");
1796 GNUNET_NAT_stun_handle_packet_ (payload,
1800 /* We now know that a server at "sa" claims that
1801 we are visible at IP "external_addr".
1803 We should (for some fixed period of time) tell
1804 all of our clients that listen to a NAT'ed address
1805 that they might want to consider the given 'external_ip'
1806 as their public IP address (this includes TCP and UDP
1807 clients, even if only UDP sends STUN requests).
1809 If we do not get a renewal, the "external_addr" should be
1810 removed again. The timeout frequency should be configurable
1811 (with a sane default), so that the UDP plugin can tell how
1812 often to re-request STUN.
1814 struct StunExternalIP *se;
1816 /* Check if we had a prior response from this STUN server */
1817 for (se = se_head; NULL != se; se = se->next)
1819 if ( (se->stun_server_addr_len != sa_len) ||
1821 &se->stun_server_addr,
1823 continue; /* different STUN server */
1824 if (0 != memcmp (&external_addr,
1826 sizeof (struct sockaddr_in)))
1828 /* external IP changed, update! */
1829 notify_clients_stun_change (&se->external_addr,
1831 se->external_addr = external_addr;
1832 notify_clients_stun_change (&se->external_addr,
1835 /* update timeout */
1836 GNUNET_SCHEDULER_cancel (se->timeout_task);
1838 = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
1843 /* STUN server is completely new, create fresh entry */
1844 se = GNUNET_new (struct StunExternalIP);
1845 se->external_addr = external_addr;
1846 GNUNET_memcpy (&se->stun_server_addr,
1849 se->stun_server_addr_len = sa_len;
1850 se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
1853 GNUNET_CONTAINER_DLL_insert (se_head,
1856 notify_clients_stun_change (&se->external_addr,
1859 GNUNET_SERVICE_client_continue (ch->client);
1865 * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from
1868 * @param cls client who sent the message
1869 * @param message the message received
1870 * @return #GNUNET_OK if message is well-formed
1873 check_request_connection_reversal (void *cls,
1874 const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
1878 expect = ntohs (message->local_addr_size)
1879 + ntohs (message->remote_addr_size);
1880 if (ntohs (message->header.size) - sizeof (*message) != expect)
1883 return GNUNET_SYSERR;
1890 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL
1891 * message from client.
1893 * @param cls client who sent the message
1894 * @param message the message received
1897 handle_request_connection_reversal (void *cls,
1898 const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
1900 struct ClientHandle *ch = cls;
1901 const char *buf = (const char *) &message[1];
1902 size_t local_sa_len = ntohs (message->local_addr_size);
1903 size_t remote_sa_len = ntohs (message->remote_addr_size);
1904 const struct sockaddr *local_sa = (const struct sockaddr *) &buf[0];
1905 const struct sockaddr *remote_sa = (const struct sockaddr *) &buf[local_sa_len];
1906 const struct sockaddr_in *l4 = NULL;
1907 const struct sockaddr_in *r4;
1910 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1911 "Received REQUEST CONNECTION REVERSAL message from client\n");
1912 switch (local_sa->sa_family)
1915 if (local_sa_len != sizeof (struct sockaddr_in))
1918 GNUNET_SERVICE_client_drop (ch->client);
1921 l4 = (const struct sockaddr_in *) local_sa;
1924 if (local_sa_len != sizeof (struct sockaddr_in6))
1927 GNUNET_SERVICE_client_drop (ch->client);
1930 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1931 _("Connection reversal for IPv6 not supported yet\n"));
1932 ret = GNUNET_SYSERR;
1936 GNUNET_SERVICE_client_drop (ch->client);
1939 switch (remote_sa->sa_family)
1942 if (remote_sa_len != sizeof (struct sockaddr_in))
1945 GNUNET_SERVICE_client_drop (ch->client);
1948 r4 = (const struct sockaddr_in *) remote_sa;
1949 ret = GN_request_connection_reversal (&l4->sin_addr,
1950 ntohs (l4->sin_port),
1954 if (remote_sa_len != sizeof (struct sockaddr_in6))
1957 GNUNET_SERVICE_client_drop (ch->client);
1960 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1961 _("Connection reversal for IPv6 not supported yet\n"));
1962 ret = GNUNET_SYSERR;
1966 GNUNET_SERVICE_client_drop (ch->client);
1969 if (GNUNET_OK != ret)
1970 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1971 _("Connection reversal request failed\n"));
1972 GNUNET_SERVICE_client_continue (ch->client);
1977 * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message
1980 * @param cls client who sent the message
1981 * @param message the message received
1982 * @return #GNUNET_OK if message is well-formed
1985 check_autoconfig_request (void *cls,
1986 const struct GNUNET_NAT_AutoconfigRequestMessage *message)
1988 return GNUNET_OK; /* checked later */
1993 * Stop all pending activities with respect to the @a ac
1995 * @param ac autoconfiguration to terminate activities for
1998 terminate_ac_activities (struct AutoconfigContext *ac)
2000 if (NULL != ac->probe_external)
2002 GNUNET_NAT_mini_get_external_ipv4_cancel_ (ac->probe_external);
2003 ac->probe_external = NULL;
2005 if (NULL != ac->timeout_task)
2007 GNUNET_SCHEDULER_cancel (ac->timeout_task);
2008 ac->timeout_task = NULL;
2014 * Finish handling the autoconfiguration request and send
2015 * the response to the client.
2017 * @param cls the `struct AutoconfigContext` to conclude
2020 conclude_autoconfig_request (void *cls)
2022 struct AutoconfigContext *ac = cls;
2023 struct ClientHandle *ch = ac->ch;
2024 struct GNUNET_NAT_AutoconfigResultMessage *arm;
2025 struct GNUNET_MQ_Envelope *env;
2028 struct GNUNET_CONFIGURATION_Handle *diff;
2030 ac->timeout_task = NULL;
2031 terminate_ac_activities (ac);
2033 /* Send back response */
2034 diff = GNUNET_CONFIGURATION_get_diff (ac->orig,
2036 buf = GNUNET_CONFIGURATION_serialize (diff,
2038 GNUNET_CONFIGURATION_destroy (diff);
2039 env = GNUNET_MQ_msg_extra (arm,
2041 GNUNET_MESSAGE_TYPE_NAT_AUTO_CFG_RESULT);
2042 arm->status_code = htonl ((uint32_t) ac->status_code);
2043 arm->type = htonl ((uint32_t) ac->type);
2044 GNUNET_memcpy (&arm[1],
2048 GNUNET_MQ_send (ch->mq,
2052 GNUNET_free (ac->system_type);
2053 GNUNET_CONFIGURATION_destroy (ac->orig);
2054 GNUNET_CONFIGURATION_destroy (ac->c);
2055 GNUNET_CONTAINER_DLL_remove (ac_head,
2059 GNUNET_SERVICE_client_continue (ch->client);
2064 * Check if all autoconfiguration operations have concluded,
2065 * and if they have, send the result back to the client.
2067 * @param ac autoconfiguation context to check
2070 check_autoconfig_finished (struct AutoconfigContext *ac)
2072 if (NULL != ac->probe_external)
2074 GNUNET_SCHEDULER_cancel (ac->timeout_task);
2076 = GNUNET_SCHEDULER_add_now (&conclude_autoconfig_request,
2082 * Update ENABLE_UPNPC configuration option.
2084 * @param ac autoconfiguration to update
2087 update_enable_upnpc_option (struct AutoconfigContext *ac)
2089 switch (ac->enable_upnpc)
2092 GNUNET_CONFIGURATION_set_value_string (ac->c,
2098 GNUNET_CONFIGURATION_set_value_string (ac->c,
2104 /* We are unsure, do not change option */
2111 * Handle result from external IP address probe during
2112 * autoconfiguration.
2114 * @param cls our `struct AutoconfigContext`
2115 * @param addr the address, NULL on errors
2116 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
2119 auto_external_result_cb (void *cls,
2120 const struct in_addr *addr,
2121 enum GNUNET_NAT_StatusCode result)
2123 struct AutoconfigContext *ac = cls;
2125 ac->probe_external = NULL;
2128 case GNUNET_NAT_ERROR_SUCCESS:
2129 ac->enable_upnpc = GNUNET_YES;
2131 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
2132 case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
2133 case GNUNET_NAT_ERROR_IPC_FAILURE:
2134 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2135 "Disabling UPNPC: %d\n",
2137 ac->enable_upnpc = GNUNET_NO; /* did not work */
2140 GNUNET_break (0); /* unexpected */
2141 ac->enable_upnpc = GNUNET_SYSERR;
2144 update_enable_upnpc_option (ac);
2145 check_autoconfig_finished (ac);
2150 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG message from
2153 * @param cls client who sent the message
2154 * @param message the message received
2157 handle_autoconfig_request (void *cls,
2158 const struct GNUNET_NAT_AutoconfigRequestMessage *message)
2160 struct ClientHandle *ch = cls;
2161 size_t left = ntohs (message->header.size) - sizeof (*message);
2162 struct LocalAddressList *lal;
2163 struct AutoconfigContext *ac;
2165 ac = GNUNET_new (struct AutoconfigContext);
2166 ac->status_code = GNUNET_NAT_ERROR_SUCCESS;
2168 ac->c = GNUNET_CONFIGURATION_create ();
2170 GNUNET_CONFIGURATION_deserialize (ac->c,
2171 (const char *) &message[1],
2176 GNUNET_SERVICE_client_drop (ch->client);
2177 GNUNET_CONFIGURATION_destroy (ac->c);
2181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2182 "Received REQUEST_AUTO_CONFIG message from client\n");
2185 GNUNET_CONFIGURATION_get_value_string (ac->c,
2189 ac->system_type = GNUNET_strdup ("UNKNOWN");
2191 GNUNET_CONTAINER_DLL_insert (ac_head,
2195 = GNUNET_CONFIGURATION_dup (ac->c);
2197 = GNUNET_SCHEDULER_add_delayed (AUTOCONFIG_TIMEOUT,
2198 &conclude_autoconfig_request,
2200 ac->enable_upnpc = GNUNET_SYSERR; /* undecided */
2202 /* Probe for upnpc */
2203 if (GNUNET_SYSERR ==
2204 GNUNET_OS_check_helper_binary ("upnpc",
2208 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2209 _("UPnP client `upnpc` command not found, disabling UPnP\n"));
2210 ac->enable_upnpc = GNUNET_NO;
2214 for (lal = lal_head; NULL != lal; lal = lal->next)
2215 if (GNUNET_NAT_AC_LAN == (lal->ac & GNUNET_NAT_AC_LAN))
2216 /* we are behind NAT, useful to try upnpc */
2217 ac->enable_upnpc = GNUNET_YES;
2219 if (GNUNET_YES == ac->enable_upnpc)
2221 /* If we are a mobile device, always leave it on as the network
2222 may change to one that supports UPnP anytime. If we are
2223 stationary, check if our network actually supports UPnP, and if
2225 if ( (0 == strcasecmp (ac->system_type,
2226 "INFRASTRUCTURE")) ||
2227 (0 == strcasecmp (ac->system_type,
2230 /* Check if upnpc gives us an external IP */
2232 = GNUNET_NAT_mini_get_external_ipv4_ (&auto_external_result_cb,
2236 if (NULL == ac->probe_external)
2237 update_enable_upnpc_option (ac);
2239 /* Finally, check if we are already done */
2240 check_autoconfig_finished (ac);
2245 * Task run during shutdown.
2250 shutdown_task (void *cls)
2252 struct StunExternalIP *se;
2253 struct AutoconfigContext *ac;
2255 while (NULL != (ac = ac_head))
2257 GNUNET_CONTAINER_DLL_remove (ac_head,
2260 terminate_ac_activities (ac);
2263 while (NULL != (se = se_head))
2265 GNUNET_CONTAINER_DLL_remove (se_head,
2268 GNUNET_SCHEDULER_cancel (se->timeout_task);
2271 if (NULL != probe_external_ip_task)
2273 GNUNET_SCHEDULER_cancel (probe_external_ip_task);
2274 probe_external_ip_task = NULL;
2276 if (NULL != probe_external_ip_op)
2278 GNUNET_NAT_mini_get_external_ipv4_cancel_ (probe_external_ip_op);
2279 probe_external_ip_op = NULL;
2281 if (NULL != scan_task)
2283 GNUNET_SCHEDULER_cancel (scan_task);
2288 GNUNET_STATISTICS_destroy (stats,
2297 * Setup NAT service.
2299 * @param cls closure
2300 * @param c configuration to use
2301 * @param service the initialized service
2305 const struct GNUNET_CONFIGURATION_Handle *c,
2306 struct GNUNET_SERVICE_Handle *service)
2310 GNUNET_CONFIGURATION_get_value_time (cfg,
2313 &stun_stale_timeout))
2314 stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
2316 /* Check for UPnP */
2318 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
2321 if (GNUNET_YES == enable_upnp)
2323 /* check if it works */
2324 if (GNUNET_SYSERR ==
2325 GNUNET_OS_check_helper_binary ("upnpc",
2329 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2330 _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n"));
2331 enable_upnp = GNUNET_SYSERR;
2335 GNUNET_CONFIGURATION_get_value_time (cfg,
2339 dyndns_frequency = DYNDNS_FREQUENCY;
2341 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
2343 stats = GNUNET_STATISTICS_create ("nat",
2345 scan_task = GNUNET_SCHEDULER_add_now (&run_scan,
2351 * Callback called when a client connects to the service.
2353 * @param cls closure for the service
2354 * @param c the new client that connected to the service
2355 * @param mq the message queue used to send messages to the client
2356 * @return a `struct ClientHandle`
2359 client_connect_cb (void *cls,
2360 struct GNUNET_SERVICE_Client *c,
2361 struct GNUNET_MQ_Handle *mq)
2363 struct ClientHandle *ch;
2365 ch = GNUNET_new (struct ClientHandle);
2368 GNUNET_CONTAINER_DLL_insert (ch_head,
2376 * Callback called when a client disconnected from the service
2378 * @param cls closure for the service
2379 * @param c the client that disconnected
2380 * @param internal_cls a `struct ClientHandle *`
2383 client_disconnect_cb (void *cls,
2384 struct GNUNET_SERVICE_Client *c,
2387 struct ClientHandle *ch = internal_cls;
2389 GNUNET_CONTAINER_DLL_remove (ch_head,
2392 for (unsigned int i=0;i<ch->num_caddrs;i++)
2394 if (NULL != ch->caddrs[i].mh)
2396 GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh);
2397 ch->caddrs[i].mh = NULL;
2400 GNUNET_free_non_null (ch->caddrs);
2401 if (NULL != ch->ext_dns_task)
2403 GNUNET_SCHEDULER_cancel (ch->ext_dns_task);
2404 ch->ext_dns_task = NULL;
2406 if (NULL != ch->ext_dns)
2408 GNUNET_RESOLVER_request_cancel (ch->ext_dns);
2411 GNUNET_free (ch->hole_external);
2417 * Define "main" method using service macro.
2421 GNUNET_SERVICE_OPTION_NONE,
2424 &client_disconnect_cb,
2426 GNUNET_MQ_hd_var_size (register,
2427 GNUNET_MESSAGE_TYPE_NAT_REGISTER,
2428 struct GNUNET_NAT_RegisterMessage,
2430 GNUNET_MQ_hd_var_size (stun,
2431 GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN,
2432 struct GNUNET_NAT_HandleStunMessage,
2434 GNUNET_MQ_hd_var_size (request_connection_reversal,
2435 GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL,
2436 struct GNUNET_NAT_RequestConnectionReversalMessage,
2438 GNUNET_MQ_hd_var_size (autoconfig_request,
2439 GNUNET_MESSAGE_TYPE_NAT_REQUEST_AUTO_CFG,
2440 struct GNUNET_NAT_AutoconfigRequestMessage,
2442 GNUNET_MQ_handler_end ());
2445 #if defined(LINUX) && defined(__GLIBC__)
2449 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
2451 void __attribute__ ((constructor))
2452 GNUNET_ARM_memory_init ()
2454 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
2455 mallopt (M_TOP_PAD, 1 * 1024);
2460 /* end of gnunet-service-nat.c */