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 * - migrate test cases to new NAT service
32 * - add new traceroute-based logic for external IP detection
34 * - implement & test STUN processing to classify NAT;
35 * basically, open port & try different methods.
39 #include "gnunet_util_lib.h"
40 #include "gnunet_protocols.h"
41 #include "gnunet_signatures.h"
42 #include "gnunet_statistics_service.h"
43 #include "gnunet_resolver_service.h"
44 #include "gnunet_nat_service.h"
45 #include "gnunet-service-nat.h"
46 #include "gnunet-service-nat_externalip.h"
47 #include "gnunet-service-nat_stun.h"
48 #include "gnunet-service-nat_mini.h"
49 #include "gnunet-service-nat_helper.h"
55 * How often should we ask the OS about a list of active
58 #define SCAN_FREQ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
61 * How long do we wait until we forcefully terminate autoconfiguration?
63 #define AUTOCONFIG_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
66 * How often do we scan for changes in how our external (dyndns) hostname resolves?
68 #define DYNDNS_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 7)
72 * Information we track per client address.
77 * Network address used by the client.
79 struct sockaddr_storage ss;
82 * Handle to active UPnP request where we asked upnpc to open
83 * a port at the NAT. NULL if we do not have such a request
86 struct GNUNET_NAT_MiniHandle *mh;
92 * List of local addresses this system has.
94 struct LocalAddressList
97 * This is a linked list.
99 struct LocalAddressList *next;
104 struct LocalAddressList *prev;
107 * Context for a gnunet-helper-nat-server used to listen
108 * for ICMP messages to this client for connection reversal.
110 struct HelperContext *hc;
113 * The address itself (i.e. `struct sockaddr_in` or `struct
114 * sockaddr_in6`, in the respective byte order).
116 struct sockaddr_storage addr;
119 * Address family. (FIXME: redundant, addr.ss_family! Remove!?)
124 * #GNUNET_YES if we saw this one in the previous iteration,
125 * but not in the current iteration and thus might need to
126 * remove it at the end.
131 * What type of address is this?
133 enum GNUNET_NAT_AddressClass ac;
139 * Internal data structure we track for each of our clients.
147 struct ClientHandle *next;
152 struct ClientHandle *prev;
155 * Underlying handle for this client with the service.
157 struct GNUNET_SERVICE_Client *client;
160 * Message queue for communicating with the client.
162 struct GNUNET_MQ_Handle *mq;
165 * Array of addresses used by the service.
167 struct ClientAddress *caddrs;
170 * External DNS name and port given by user due to manual
171 * hole punching. Special DNS name 'AUTO' is used to indicate
172 * desire for automatic determination of the external IP
173 * (instead of DNS or manual configuration, i.e. to be used
174 * if the IP keeps changing and we have no DynDNS, but we do
175 * have a hole punched).
180 * Name of the configuration section this client cares about.
185 * Task for periodically re-running the @e ext_dns DNS lookup.
187 struct GNUNET_SCHEDULER_Task *ext_dns_task;
190 * Handle for (DYN)DNS lookup of our external IP as given in
193 struct GNUNET_RESOLVER_RequestHandle *ext_dns;
196 * Handle for monitoring external IP changes.
198 struct GN_ExternalIPMonitor *external_monitor;
201 * DLL of external IP addresses as given in @e hole_external.
203 struct LocalAddressList *ext_addr_head;
206 * DLL of external IP addresses as given in @e hole_external.
208 struct LocalAddressList *ext_addr_tail;
211 * Port number we found in @e hole_external.
213 uint16_t ext_dns_port;
216 * What does this client care about?
218 enum GNUNET_NAT_RegisterFlags flags;
221 * Is any of the @e caddrs in a reserved subnet for NAT?
226 * Number of addresses that this service is bound to.
227 * Length of the @e caddrs array.
232 * Client's IPPROTO, e.g. IPPROTO_UDP or IPPROTO_TCP.
240 * External IP address as given to us via some STUN server.
242 struct StunExternalIP
247 struct StunExternalIP *next;
252 struct StunExternalIP *prev;
255 * Task we run to remove this entry when it is stale.
257 struct GNUNET_SCHEDULER_Task *timeout_task;
260 * Our external IP address as reported by the
263 struct sockaddr_in external_addr;
266 * Address of the reporting STUN server. Used to
267 * detect when a STUN server changes its opinion
268 * to more quickly remove stale results.
270 struct sockaddr_storage stun_server_addr;
273 * Number of bytes used in @e stun_server_addr.
275 size_t stun_server_addr_len;
280 * Timeout to use when STUN data is considered stale.
282 static struct GNUNET_TIME_Relative stun_stale_timeout;
285 * How often do we scan for changes in how our external (dyndns) hostname resolves?
287 static struct GNUNET_TIME_Relative dyndns_frequency;
290 * Handle to our current configuration.
292 static const struct GNUNET_CONFIGURATION_Handle *cfg;
295 * Handle to the statistics service.
297 static struct GNUNET_STATISTICS_Handle *stats;
300 * Task scheduled to periodically scan our network interfaces.
302 static struct GNUNET_SCHEDULER_Task *scan_task;
305 * Head of client DLL.
307 static struct ClientHandle *ch_head;
310 * Tail of client DLL.
312 static struct ClientHandle *ch_tail;
315 * Head of DLL of local addresses.
317 static struct LocalAddressList *lal_head;
320 * Tail of DLL of local addresses.
322 static struct LocalAddressList *lal_tail;
327 static struct StunExternalIP *se_head;
332 static struct StunExternalIP *se_tail;
335 * Is UPnP enabled? #GNUNET_YES if enabled, #GNUNET_NO if disabled,
336 * #GNUNET_SYSERR if configuration enabled but binary is unavailable.
342 * Remove and free an entry from the #lal_head DLL.
344 * @param lal entry to free
347 free_lal (struct LocalAddressList *lal)
349 GNUNET_CONTAINER_DLL_remove (lal_head,
354 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
355 "Lost NATed local address %s, stopping NAT server\n",
356 GNUNET_a2s ((const struct sockaddr *) &lal->addr,
357 sizeof (struct sockaddr_in)));
359 GN_stop_gnunet_nat_server_ (lal->hc);
367 * Free the DLL starting at #lal_head.
372 struct LocalAddressList *lal;
374 while (NULL != (lal = lal_head))
380 * Check validity of #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from
383 * @param cls client who sent the message
384 * @param message the message received
385 * @return #GNUNET_OK if message is well-formed
388 check_register (void *cls,
389 const struct GNUNET_NAT_RegisterMessage *message)
391 uint16_t num_addrs = ntohs (message->num_addrs);
392 const char *off = (const char *) &message[1];
393 size_t left = ntohs (message->header.size) - sizeof (*message);
395 for (unsigned int i=0;i<num_addrs;i++)
398 const struct sockaddr *sa = (const struct sockaddr *) off;
400 if (sizeof (sa_family_t) > left)
403 return GNUNET_SYSERR;
405 switch (sa->sa_family)
408 alen = sizeof (struct sockaddr_in);
411 alen = sizeof (struct sockaddr_in6);
415 alen = sizeof (struct sockaddr_un);
420 return GNUNET_SYSERR;
425 return GNUNET_SYSERR;
430 if (left != ntohs (message->str_len))
433 return GNUNET_SYSERR;
440 * Check if @a ip is in @a network with @a bits netmask.
442 * @param network to test
443 * @param ip IP address to test
444 * @param bits bitmask for the network
445 * @return #GNUNET_YES if @a ip is in @a network
448 match_ipv4 (const char *network,
449 const struct in_addr *ip,
458 GNUNET_assert (1 == inet_pton (AF_INET,
461 return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits)));
466 * Check if @a ip is in @a network with @a bits netmask.
468 * @param network to test
469 * @param ip IP address to test
470 * @param bits bitmask for the network
471 * @return #GNUNET_YES if @a ip is in @a network
474 match_ipv6 (const char *network,
475 const struct in6_addr *ip,
479 struct in6_addr mask;
484 GNUNET_assert (1 == inet_pton (AF_INET6,
487 memset (&mask, 0, sizeof (mask));
488 if (0 == memcmp (&mask,
495 mask.s6_addr[off++] = 0xFF;
500 mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80;
503 for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++)
504 if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) !=
505 (((uint32_t *) &net)[j] & ((int *) &mask)[j]))
512 * Test if the given IPv4 address is in a known range
513 * for private networks.
515 * @param ip address to test
516 * @return #GNUNET_YES if @a ip is in a NAT range
519 is_nat_v4 (const struct in_addr *ip)
522 match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */
523 match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */
524 match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */
525 match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */
526 match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */
531 * Test if the given IPv6 address is in a known range
532 * for private networks.
534 * @param ip address to test
535 * @return #GNUNET_YES if @a ip is in a NAT range
538 is_nat_v6 (const struct in6_addr *ip)
541 match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */
542 match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */
543 match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */
548 * Closure for #ifc_proc.
550 struct IfcProcContext
554 * Head of DLL of local addresses.
556 struct LocalAddressList *lal_head;
559 * Tail of DLL of local addresses.
561 struct LocalAddressList *lal_tail;
567 * Callback function invoked for each interface found. Adds them
568 * to our new address list.
570 * @param cls a `struct IfcProcContext *`
571 * @param name name of the interface (can be NULL for unknown)
572 * @param isDefault is this presumably the default interface
573 * @param addr address of this interface (can be NULL for unknown or unassigned)
574 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
575 * @param netmask the network mask (can be NULL for unknown or unassigned)
576 * @param addrlen length of the address
577 * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort
583 const struct sockaddr *addr,
584 const struct sockaddr *broadcast_addr,
585 const struct sockaddr *netmask,
588 struct IfcProcContext *ifc_ctx = cls;
589 struct LocalAddressList *lal;
591 const struct in_addr *ip4;
592 const struct in6_addr *ip6;
593 enum GNUNET_NAT_AddressClass ac;
595 switch (addr->sa_family)
598 alen = sizeof (struct sockaddr_in);
599 ip4 = &((const struct sockaddr_in *) addr)->sin_addr;
600 if (match_ipv4 ("127.0.0.0", ip4, 8))
601 ac = GNUNET_NAT_AC_LOOPBACK;
602 else if (is_nat_v4 (ip4))
603 ac = GNUNET_NAT_AC_LAN;
605 ac = GNUNET_NAT_AC_GLOBAL;
608 alen = sizeof (struct sockaddr_in6);
609 ip6 = &((const struct sockaddr_in6 *) addr)->sin6_addr;
610 if (match_ipv6 ("::1", ip6, 128))
611 ac = GNUNET_NAT_AC_LOOPBACK;
612 else if (is_nat_v6 (ip6))
613 ac = GNUNET_NAT_AC_LAN;
615 ac = GNUNET_NAT_AC_GLOBAL;
616 if ( (ip6->s6_addr[11] == 0xFF) &&
617 (ip6->s6_addr[12] == 0xFE) )
619 /* contains a MAC, be extra careful! */
620 ac |= GNUNET_NAT_AC_PRIVATE;
632 lal = GNUNET_malloc (sizeof (*lal));
633 lal->af = addr->sa_family;
635 GNUNET_memcpy (&lal->addr,
638 GNUNET_CONTAINER_DLL_insert (ifc_ctx->lal_head,
646 * Notify client about a change in the list of addresses this peer
649 * @param ac address class of the entry in the list that changed
650 * @param ch client to contact
651 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
652 * @param addr the address that changed
653 * @param addr_len number of bytes in @a addr
656 notify_client (enum GNUNET_NAT_AddressClass ac,
657 struct ClientHandle *ch,
662 struct GNUNET_MQ_Envelope *env;
663 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
665 env = GNUNET_MQ_msg_extra (msg,
667 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
668 msg->add_remove = htonl (add);
669 msg->addr_class = htonl (ac);
670 GNUNET_memcpy (&msg[1],
673 GNUNET_MQ_send (ch->mq,
679 * Check if we should bother to notify this client about this
680 * address change, and if so, do it.
682 * @param delta the entry in the list that changed
683 * @param ch client to check
684 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
687 check_notify_client (struct LocalAddressList *delta,
688 struct ClientHandle *ch,
692 struct sockaddr_in v4;
693 struct sockaddr_in6 v6;
695 if (0 == (ch->flags & GNUNET_NAT_RF_ADDRESSES))
700 alen = sizeof (struct sockaddr_in);
705 /* Check for client notifications */
706 for (unsigned int i=0;i<ch->num_caddrs;i++)
708 const struct sockaddr_in *c4;
710 if (AF_INET != ch->caddrs[i].ss.ss_family)
711 continue; /* IPv4 not relevant */
712 c4 = (const struct sockaddr_in *) &ch->caddrs[i].ss;
713 if ( match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) &&
714 (0 != c4->sin_addr.s_addr) &&
715 (! match_ipv4 ("127.0.0.1", &v4.sin_addr, 8)) )
716 continue; /* bound to loopback, but this is not loopback */
717 if ( (! match_ipv4 ("127.0.0.1", &c4->sin_addr, 8) ) &&
718 (0 != c4->sin_addr.s_addr) &&
719 match_ipv4 ("127.0.0.1", &v4.sin_addr, 8) )
720 continue; /* bound to non-loopback, but this is loopback */
721 if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
722 (0 != c4->sin_addr.s_addr) &&
723 (! is_nat_v4 (&v4.sin_addr)) )
724 continue; /* based on external-IP, but this IP is not
725 from private address range. */
726 if ( (0 != memcmp (&v4.sin_addr,
728 sizeof (struct in_addr))) &&
729 (0 != c4->sin_addr.s_addr) &&
730 ( (! is_nat_v4 (&c4->sin_addr)) ||
731 (0 == (ch->flags & GNUNET_NAT_AC_EXTERN))) )
732 continue; /* this IP is not from private address range,
733 and IP does not match. */
735 /* OK, IP seems relevant, notify client */
736 v4.sin_port = c4->sin_port;
737 notify_client (delta->ac,
745 alen = sizeof (struct sockaddr_in6);
749 for (unsigned int i=0;i<ch->num_caddrs;i++)
751 const struct sockaddr_in6 *c6;
753 if (AF_INET6 != ch->caddrs[i].ss.ss_family)
754 continue; /* IPv4 not relevant */
755 c6 = (const struct sockaddr_in6 *) &ch->caddrs[i].ss;
756 if ( match_ipv6 ("::1", &c6->sin6_addr, 128) &&
757 (0 != memcmp (&c6->sin6_addr,
759 sizeof (struct in6_addr))) &&
760 (! match_ipv6 ("::1", &v6.sin6_addr, 128)) )
761 continue; /* bound to loopback, but this is not loopback */
762 if ( (! match_ipv6 ("::1", &c6->sin6_addr, 128) ) &&
763 (0 != memcmp (&c6->sin6_addr,
765 sizeof (struct in6_addr))) &&
766 match_ipv6 ("::1", &v6.sin6_addr, 128) )
767 continue; /* bound to non-loopback, but this is loopback */
768 if ( (0 != (ch->flags & GNUNET_NAT_AC_EXTERN)) &&
769 (0 != memcmp (&c6->sin6_addr,
771 sizeof (struct in6_addr))) &&
772 (! is_nat_v6 (&v6.sin6_addr)) )
773 continue; /* based on external-IP, but this IP is not
774 from private address range. */
775 if ( (0 != memcmp (&v6.sin6_addr,
777 sizeof (struct in6_addr))) &&
778 (0 != memcmp (&c6->sin6_addr,
780 sizeof (struct in6_addr))) &&
781 (! is_nat_v6 (&c6->sin6_addr)) )
782 continue; /* this IP is not from private address range,
783 and IP does not match. */
784 if ( (match_ipv6 ("fe80::", &c6->sin6_addr, 10)) &&
785 (0 != memcmp (&c6->sin6_addr,
787 sizeof (struct in6_addr))) &&
788 (0 != memcmp (&v6.sin6_addr,
790 sizeof (struct in6_addr))) &&
791 (0 == (delta->ac & GNUNET_NAT_AC_EXTERN)) )
792 continue; /* client bound to link-local, and the other address
793 does not match and is not an external IP */
795 /* OK, IP seems relevant, notify client */
796 v6.sin6_port = c6->sin6_port;
797 notify_client (delta->ac,
812 * Notify all clients about a change in the list
813 * of addresses this peer has.
815 * @param delta the entry in the list that changed
816 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
819 notify_clients (struct LocalAddressList *delta,
822 for (struct ClientHandle *ch = ch_head;
825 check_notify_client (delta,
832 * Tell relevant client about a change in our external
835 * @param cls client to check if it cares and possibly notify
836 * @param v4 the external address that changed
837 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
840 notify_client_external_ipv4_change (void *cls,
841 const struct in_addr *v4,
844 struct ClientHandle *ch = cls;
845 struct sockaddr_in sa;
848 /* (0) check if this impacts 'hole_external' */
849 if ( (NULL != ch->hole_external) &&
850 (0 == strcasecmp (ch->hole_external,
853 struct LocalAddressList lal;
854 struct sockaddr_in *s4;
856 memset (&lal, 0, sizeof (lal));
857 s4 = (struct sockaddr_in *) &lal.addr;
858 s4->sin_family = AF_INET;
859 s4->sin_port = htons (ch->ext_dns_port);
862 lal.ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
863 check_notify_client (&lal,
868 /* (1) check if client cares. */
869 if (! ch->natted_address)
871 if (0 == (GNUNET_NAT_RF_ADDRESSES & ch->flags))
874 for (unsigned int i=0;i<ch->num_caddrs;i++)
876 const struct sockaddr_storage *ss = &ch->caddrs[i].ss;
878 if (AF_INET != ss->ss_family)
880 have_v4 = GNUNET_YES;
883 if (GNUNET_NO == have_v4)
884 return; /* IPv6-only */
886 /* (2) build address info */
890 sa.sin_family = AF_INET;
892 sa.sin_port = htons (0);
894 /* (3) notify client of change */
895 notify_client (is_nat_v4 (v4)
896 ? GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_LAN
897 : GNUNET_NAT_AC_EXTERN | GNUNET_NAT_AC_GLOBAL,
906 * We got a connection reversal request from another peer.
907 * Notify applicable clients.
909 * @param cls closure with the `struct LocalAddressList`
910 * @param ra IP address of the peer who wants us to connect to it
913 reversal_callback (void *cls,
914 const struct sockaddr_in *ra)
916 struct LocalAddressList *lal = cls;
917 const struct sockaddr_in *l4;
919 GNUNET_assert (AF_INET == lal->af);
920 l4 = (const struct sockaddr_in *) &lal->addr;
921 for (struct ClientHandle *ch = ch_head;
925 struct GNUNET_NAT_ConnectionReversalRequestedMessage *crrm;
926 struct GNUNET_MQ_Envelope *env;
929 /* Check if client is in applicable range for ICMP NAT traversal
930 for this local address */
931 if (! ch->natted_address)
934 for (unsigned int i=0;i<ch->num_caddrs;i++)
936 struct ClientAddress *ca = &ch->caddrs[i];
937 const struct sockaddr_in *c4;
939 if (AF_INET != ca->ss.ss_family)
941 c4 = (const struct sockaddr_in *) &ca->ss;
942 if ( (0 != c4->sin_addr.s_addr) &&
943 (l4->sin_addr.s_addr != c4->sin_addr.s_addr) )
951 /* Notify applicable client about connection reversal request */
952 env = GNUNET_MQ_msg_extra (crrm,
953 sizeof (struct sockaddr_in),
954 GNUNET_MESSAGE_TYPE_NAT_CONNECTION_REVERSAL_REQUESTED);
955 GNUNET_memcpy (&crrm[1],
957 sizeof (struct sockaddr_in));
958 GNUNET_MQ_send (ch->mq,
965 * Task we run periodically to scan for network interfaces.
972 struct IfcProcContext ifc_ctx;
975 struct LocalAddressList *lnext;
977 scan_task = GNUNET_SCHEDULER_add_delayed (SCAN_FREQ,
983 GNUNET_OS_network_interfaces_list (&ifc_proc,
985 /* remove addresses that disappeared */
986 for (struct LocalAddressList *lal = lal_head;
992 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
996 if ( (pos->af == lal->af) &&
997 (0 == memcmp (&lal->addr,
1000 ? sizeof (struct sockaddr_in)
1001 : sizeof (struct sockaddr_in6))) )
1006 if (GNUNET_NO == found)
1008 notify_clients (lal,
1014 /* add addresses that appeared */
1015 have_nat = GNUNET_NO;
1016 for (struct LocalAddressList *pos = ifc_ctx.lal_head;
1018 pos = ifc_ctx.lal_head)
1021 if (GNUNET_NAT_AC_LAN == (GNUNET_NAT_AC_LAN & pos->ac))
1022 have_nat = GNUNET_YES;
1023 for (struct LocalAddressList *lal = lal_head;
1027 if ( (pos->af == lal->af) &&
1028 (0 == memcmp (&lal->addr,
1030 (AF_INET == lal->af)
1031 ? sizeof (struct sockaddr_in)
1032 : sizeof (struct sockaddr_in6))) )
1035 GNUNET_CONTAINER_DLL_remove (ifc_ctx.lal_head,
1038 if (GNUNET_YES == found)
1044 notify_clients (pos,
1046 GNUNET_CONTAINER_DLL_insert (lal_head,
1049 if ( (AF_INET == pos->af) &&
1050 (NULL == pos->hc) &&
1051 (0 != (GNUNET_NAT_AC_LAN & pos->ac)) )
1053 const struct sockaddr_in *s4
1054 = (const struct sockaddr_in *) &pos->addr;
1056 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1057 "Found NATed local address %s, starting NAT server\n",
1058 GNUNET_a2s ((void *) &pos->addr, sizeof (*s4)));
1059 pos->hc = GN_start_gnunet_nat_server_ (&s4->sin_addr,
1065 GN_nat_status_changed (have_nat);
1070 * Function called whenever our set of external addresses
1071 * as created by `upnpc` changes.
1073 * @param cls closure with our `struct ClientHandle *`
1074 * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean
1075 * the previous (now invalid) one, #GNUNET_SYSERR indicates an error
1076 * @param addr either the previous or the new public IP address
1077 * @param addrlen actual length of the @a addr
1078 * @param result #GNUNET_NAT_ERROR_SUCCESS on success, otherwise the specific error code
1081 upnp_addr_change_cb (void *cls,
1083 const struct sockaddr *addr,
1085 enum GNUNET_NAT_StatusCode result)
1087 struct ClientHandle *ch = cls;
1088 enum GNUNET_NAT_AddressClass ac;
1092 case GNUNET_NAT_ERROR_SUCCESS:
1093 GNUNET_assert (NULL != addr);
1095 case GNUNET_NAT_ERROR_UPNPC_FAILED:
1096 case GNUNET_NAT_ERROR_UPNPC_TIMEOUT:
1097 case GNUNET_NAT_ERROR_IPC_FAILURE:
1098 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1099 "Running upnpc failed: %d\n",
1102 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_NOT_FOUND:
1103 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1104 "external-ip binary not found\n");
1106 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_FAILED:
1107 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1108 "external-ip binary could not be run\n");
1110 case GNUNET_NAT_ERROR_UPNPC_PORTMAP_FAILED:
1111 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1112 "upnpc failed to create port mapping\n");
1114 case GNUNET_NAT_ERROR_EXTERNAL_IP_UTILITY_OUTPUT_INVALID:
1115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1116 "Invalid output from upnpc\n");
1118 case GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID:
1119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1120 "Invalid address returned by upnpc\n");
1123 GNUNET_break (0); /* should not be possible */
1126 switch (addr->sa_family)
1129 ac = is_nat_v4 (&((const struct sockaddr_in *) addr)->sin_addr)
1131 : GNUNET_NAT_AC_EXTERN;
1134 ac = is_nat_v6 (&((const struct sockaddr_in6 *) addr)->sin6_addr)
1136 : GNUNET_NAT_AC_EXTERN;
1142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1143 "upnp external address %s: %s\n",
1144 add_remove ? "added" : "removed",
1156 * Resolve the `hole_external` name to figure out our
1157 * external address from a manually punched hole. The
1158 * port number has already been parsed, this task is
1159 * responsible for periodically doing a DNS lookup.
1161 * @param ch client handle to act upon
1164 dyndns_lookup (void *cls);
1168 * Our (external) hostname was resolved. Update lists of
1169 * current external IPs (note that DNS may return multiple
1170 * addresses!) and notify client accordingly.
1172 * @param cls the `struct ClientHandle`
1173 * @param addr NULL on error, otherwise result of DNS lookup
1174 * @param addrlen number of bytes in @a addr
1177 process_external_ip (void *cls,
1178 const struct sockaddr *addr,
1181 struct ClientHandle *ch = cls;
1182 struct LocalAddressList *lal;
1183 struct sockaddr_storage ss;
1184 struct sockaddr_in *v4;
1185 struct sockaddr_in6 *v6;
1189 struct LocalAddressList *laln;
1193 = GNUNET_SCHEDULER_add_delayed (dyndns_frequency,
1196 /* Current iteration is over, remove 'old' IPs now */
1197 for (lal = ch->ext_addr_head; NULL != lal; lal = laln)
1200 if (GNUNET_YES == lal->old)
1202 GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head,
1205 check_notify_client (lal,
1213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1214 "Got IP `%s' for external address `%s'\n",
1219 /* build sockaddr storage with port number */
1220 memset (&ss, 0, sizeof (ss));
1221 memcpy (&ss, addr, addrlen);
1222 switch (addr->sa_family)
1225 v4 = (struct sockaddr_in *) &ss;
1226 v4->sin_port = htons (ch->ext_dns_port);
1229 v6 = (struct sockaddr_in6 *) &ss;
1230 v6->sin6_port = htons (ch->ext_dns_port);
1236 /* See if 'ss' matches any of our known addresses */
1237 for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next)
1239 if (GNUNET_NO == lal->old)
1240 continue; /* already processed, skip */
1241 if ( (addr->sa_family == lal->addr.ss_family) &&
1246 /* Address unchanged, remember so we do not remove */
1247 lal->old = GNUNET_NO;
1248 return; /* done here */
1251 /* notify client, and remember IP for later removal! */
1252 lal = GNUNET_new (struct LocalAddressList);
1254 lal->af = ss.ss_family;
1255 lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1256 GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
1259 check_notify_client (lal,
1266 * Resolve the `hole_external` name to figure out our
1267 * external address from a manually punched hole. The
1268 * port number has already been parsed, this task is
1269 * responsible for periodically doing a DNS lookup.
1271 * @param ch client handle to act upon
1274 dyndns_lookup (void *cls)
1276 struct ClientHandle *ch = cls;
1277 struct LocalAddressList *lal;
1279 for (lal = ch->ext_addr_head; NULL != lal; lal = lal->next)
1280 lal->old = GNUNET_YES;
1281 ch->ext_dns_task = NULL;
1282 ch->ext_dns = GNUNET_RESOLVER_ip_get (ch->hole_external,
1284 GNUNET_TIME_UNIT_MINUTES,
1285 &process_external_ip,
1291 * Resolve the `hole_external` name to figure out our
1292 * external address from a manually punched hole. The
1293 * given name may be "AUTO" in which case we should use
1294 * the IP address(es) we have from upnpc or other methods.
1295 * The name can also be an IP address, in which case we
1296 * do not need to do DNS resolution. Finally, we also
1297 * need to parse the port number.
1299 * @param ch client handle to act upon
1302 lookup_hole_external (struct ClientHandle *ch)
1306 struct sockaddr_in *s4;
1307 struct LocalAddressList *lal;
1309 port = strrchr (ch->hole_external, ':');
1312 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1313 _("Malformed punched hole specification `%s' (lacks port)\n"),
1317 if ( (1 != sscanf (port + 1,
1322 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1323 _("Invalid port number in punched hole specification `%s' (lacks port)\n"),
1327 ch->ext_dns_port = (uint16_t) pnum;
1330 lal = GNUNET_new (struct LocalAddressList);
1331 if ('[' == *ch->hole_external)
1333 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &lal->addr;
1335 s6->sin6_family = AF_INET6;
1336 if (']' != (ch->hole_external[strlen(ch->hole_external)-1]))
1338 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1339 _("Malformed punched hole specification `%s' (lacks `]')\n"),
1344 ch->hole_external[strlen(ch->hole_external)-1] = '\0';
1345 if (1 != inet_pton (AF_INET6,
1346 ch->hole_external + 1,
1349 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1350 _("Malformed punched hole specification `%s' (IPv6 address invalid)"),
1351 ch->hole_external + 1);
1355 s6->sin6_port = htons (ch->ext_dns_port);
1357 lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1358 GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
1361 check_notify_client (lal,
1367 s4 = (struct sockaddr_in *) &lal->addr;
1368 s4->sin_family = AF_INET;
1369 if (1 == inet_pton (AF_INET,
1373 s4->sin_port = htons (ch->ext_dns_port);
1375 lal->ac = GNUNET_NAT_AC_GLOBAL | GNUNET_NAT_AC_MANUAL;
1376 GNUNET_CONTAINER_DLL_insert (ch->ext_addr_head,
1379 check_notify_client (lal,
1384 if (0 == strcasecmp (ch->hole_external,
1387 /* handled in #notify_client_external_ipv4_change() */
1391 /* got a DNS name, trigger lookup! */
1394 = GNUNET_SCHEDULER_add_now (&dyndns_lookup,
1400 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client.
1401 * We remember the client for updates upon future NAT events.
1403 * @param cls client who sent the message
1404 * @param message the message received
1407 handle_register (void *cls,
1408 const struct GNUNET_NAT_RegisterMessage *message)
1410 struct ClientHandle *ch = cls;
1414 if ( (0 != ch->proto) ||
1415 (NULL != ch->caddrs) )
1417 /* double registration not allowed */
1419 GNUNET_SERVICE_client_drop (ch->client);
1422 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1423 "Received REGISTER message from client\n");
1424 ch->flags = message->flags;
1425 ch->proto = message->proto;
1426 ch->num_caddrs = ntohs (message->num_addrs);
1427 ch->caddrs = GNUNET_new_array (ch->num_caddrs,
1428 struct ClientAddress);
1429 left = ntohs (message->header.size) - sizeof (*message);
1430 off = (const char *) &message[1];
1431 for (unsigned int i=0;i<ch->num_caddrs;i++)
1434 const struct sockaddr *sa = (const struct sockaddr *) off;
1438 if (sizeof (sa_family_t) > left)
1441 GNUNET_SERVICE_client_drop (ch->client);
1445 switch (sa->sa_family)
1449 const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa;
1451 alen = sizeof (struct sockaddr_in);
1452 if (is_nat_v4 (&s4->sin_addr))
1453 is_nat = GNUNET_YES;
1454 port = ntohs (s4->sin_port);
1459 const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa;
1461 alen = sizeof (struct sockaddr_in6);
1462 if (is_nat_v6 (&s6->sin6_addr))
1463 is_nat = GNUNET_YES;
1464 port = ntohs (s6->sin6_port);
1469 alen = sizeof (struct sockaddr_un);
1475 GNUNET_SERVICE_client_drop (ch->client);
1479 GNUNET_assert (alen <= left);
1480 GNUNET_assert (alen <= sizeof (struct sockaddr_storage));
1481 GNUNET_memcpy (&ch->caddrs[i].ss,
1485 /* If applicable, try UPNPC NAT punching */
1488 ( (IPPROTO_TCP == ch->proto) ||
1489 (IPPROTO_UDP == ch->proto) ) )
1491 ch->natted_address = GNUNET_YES;
1493 = GNUNET_NAT_mini_map_start (port,
1494 IPPROTO_TCP == ch->proto,
1495 &upnp_addr_change_cb,
1503 = GNUNET_strndup (off,
1504 ntohs (message->str_len));
1506 GNUNET_CONFIGURATION_get_value_string (cfg,
1509 &ch->hole_external))
1510 lookup_hole_external (ch);
1512 /* Actually send IP address list to client */
1513 for (struct LocalAddressList *lal = lal_head;
1517 check_notify_client (lal,
1521 /* Also consider IPv4 determined by `external-ip` */
1522 ch->external_monitor
1523 = GN_external_ipv4_monitor_start (¬ify_client_external_ipv4_change,
1525 GNUNET_SERVICE_client_continue (ch->client);
1530 * Check validity of #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
1533 * @param cls client who sent the message
1534 * @param message the message received
1535 * @return #GNUNET_OK if message is well-formed
1538 check_stun (void *cls,
1539 const struct GNUNET_NAT_HandleStunMessage *message)
1541 size_t sa_len = ntohs (message->sender_addr_size);
1542 size_t expect = sa_len + ntohs (message->payload_size);
1544 if (ntohs (message->header.size) - sizeof (*message) != expect)
1547 return GNUNET_SYSERR;
1549 if (sa_len < sizeof (sa_family_t))
1552 return GNUNET_SYSERR;
1559 * Notify all clients about our external IP address
1560 * as reported by the STUN server.
1562 * @param ip the external IP
1563 * @param add #GNUNET_YES to add, #GNUNET_NO to remove
1566 notify_clients_stun_change (const struct sockaddr_in *ip,
1569 for (struct ClientHandle *ch = ch_head;
1573 struct sockaddr_in v4;
1574 struct GNUNET_NAT_AddressChangeNotificationMessage *msg;
1575 struct GNUNET_MQ_Envelope *env;
1577 if (! ch->natted_address)
1580 v4.sin_port = htons (0);
1581 env = GNUNET_MQ_msg_extra (msg,
1583 GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE);
1584 msg->add_remove = htonl ((int32_t) add);
1585 msg->addr_class = htonl (GNUNET_NAT_AC_EXTERN |
1586 GNUNET_NAT_AC_GLOBAL);
1587 GNUNET_memcpy (&msg[1],
1590 GNUNET_MQ_send (ch->mq,
1597 * Function to be called when we decide that an
1598 * external IP address as told to us by a STUN
1599 * server has gone stale.
1601 * @param cls the `struct StunExternalIP` to drop
1604 stun_ip_timeout (void *cls)
1606 struct StunExternalIP *se = cls;
1608 se->timeout_task = NULL;
1609 notify_clients_stun_change (&se->external_addr,
1611 GNUNET_CONTAINER_DLL_remove (se_head,
1619 * Handler for #GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN message from
1622 * @param cls client who sent the message
1623 * @param message the message received
1626 handle_stun (void *cls,
1627 const struct GNUNET_NAT_HandleStunMessage *message)
1629 struct ClientHandle *ch = cls;
1630 const char *buf = (const char *) &message[1];
1631 const struct sockaddr *sa;
1632 const void *payload;
1634 size_t payload_size;
1635 struct sockaddr_in external_addr;
1637 sa_len = ntohs (message->sender_addr_size);
1638 payload_size = ntohs (message->payload_size);
1639 sa = (const struct sockaddr *) &buf[0];
1640 payload = (const struct sockaddr *) &buf[sa_len];
1641 switch (sa->sa_family)
1644 if (sa_len != sizeof (struct sockaddr_in))
1647 GNUNET_SERVICE_client_drop (ch->client);
1652 if (sa_len != sizeof (struct sockaddr_in6))
1655 GNUNET_SERVICE_client_drop (ch->client);
1660 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1661 "Received HANDLE_STUN message from client\n");
1663 GNUNET_NAT_stun_handle_packet_ (payload,
1667 /* We now know that a server at "sa" claims that
1668 we are visible at IP "external_addr".
1670 We should (for some fixed period of time) tell
1671 all of our clients that listen to a NAT'ed address
1672 that they might want to consider the given 'external_ip'
1673 as their public IP address (this includes TCP and UDP
1674 clients, even if only UDP sends STUN requests).
1676 If we do not get a renewal, the "external_addr" should be
1677 removed again. The timeout frequency should be configurable
1678 (with a sane default), so that the UDP plugin can tell how
1679 often to re-request STUN.
1681 struct StunExternalIP *se;
1683 /* Check if we had a prior response from this STUN server */
1684 for (se = se_head; NULL != se; se = se->next)
1686 if ( (se->stun_server_addr_len != sa_len) ||
1688 &se->stun_server_addr,
1690 continue; /* different STUN server */
1691 if (0 != memcmp (&external_addr,
1693 sizeof (struct sockaddr_in)))
1695 /* external IP changed, update! */
1696 notify_clients_stun_change (&se->external_addr,
1698 se->external_addr = external_addr;
1699 notify_clients_stun_change (&se->external_addr,
1702 /* update timeout */
1703 GNUNET_SCHEDULER_cancel (se->timeout_task);
1705 = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
1710 /* STUN server is completely new, create fresh entry */
1711 se = GNUNET_new (struct StunExternalIP);
1712 se->external_addr = external_addr;
1713 GNUNET_memcpy (&se->stun_server_addr,
1716 se->stun_server_addr_len = sa_len;
1717 se->timeout_task = GNUNET_SCHEDULER_add_delayed (stun_stale_timeout,
1720 GNUNET_CONTAINER_DLL_insert (se_head,
1723 notify_clients_stun_change (&se->external_addr,
1726 GNUNET_SERVICE_client_continue (ch->client);
1732 * #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL message from
1735 * @param cls client who sent the message
1736 * @param message the message received
1737 * @return #GNUNET_OK if message is well-formed
1740 check_request_connection_reversal (void *cls,
1741 const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
1745 expect = ntohs (message->local_addr_size)
1746 + ntohs (message->remote_addr_size);
1747 if (ntohs (message->header.size) - sizeof (*message) != expect)
1750 return GNUNET_SYSERR;
1757 * Handler for #GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL
1758 * message from client.
1760 * @param cls client who sent the message
1761 * @param message the message received
1764 handle_request_connection_reversal (void *cls,
1765 const struct GNUNET_NAT_RequestConnectionReversalMessage *message)
1767 struct ClientHandle *ch = cls;
1768 const char *buf = (const char *) &message[1];
1769 size_t local_sa_len = ntohs (message->local_addr_size);
1770 size_t remote_sa_len = ntohs (message->remote_addr_size);
1771 struct sockaddr_in l4;
1772 struct sockaddr_in r4;
1775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1776 "Received REQUEST CONNECTION REVERSAL message from client\n");
1777 if (local_sa_len != sizeof (struct sockaddr_in))
1779 GNUNET_break_op (0);
1780 GNUNET_SERVICE_client_drop (ch->client);
1783 if (remote_sa_len != sizeof (struct sockaddr_in))
1785 GNUNET_break_op (0);
1786 GNUNET_SERVICE_client_drop (ch->client);
1791 sizeof (struct sockaddr_in));
1792 GNUNET_break_op (AF_INET == l4.sin_family);
1793 buf += sizeof (struct sockaddr_in);
1796 sizeof (struct sockaddr_in));
1797 GNUNET_break_op (AF_INET == r4.sin_family);
1798 ret = GN_request_connection_reversal (&l4.sin_addr,
1799 ntohs (l4.sin_port),
1801 if (GNUNET_OK != ret)
1802 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1803 _("Connection reversal request failed\n"));
1804 GNUNET_SERVICE_client_continue (ch->client);
1809 * Task run during shutdown.
1814 shutdown_task (void *cls)
1816 struct StunExternalIP *se;
1818 while (NULL != (se = se_head))
1820 GNUNET_CONTAINER_DLL_remove (se_head,
1823 GNUNET_SCHEDULER_cancel (se->timeout_task);
1826 GN_nat_status_changed (GNUNET_NO);
1827 if (NULL != scan_task)
1829 GNUNET_SCHEDULER_cancel (scan_task);
1834 GNUNET_STATISTICS_destroy (stats,
1843 * Setup NAT service.
1845 * @param cls closure
1846 * @param c configuration to use
1847 * @param service the initialized service
1851 const struct GNUNET_CONFIGURATION_Handle *c,
1852 struct GNUNET_SERVICE_Handle *service)
1856 GNUNET_CONFIGURATION_get_value_time (cfg,
1859 &stun_stale_timeout))
1860 stun_stale_timeout = GNUNET_TIME_UNIT_HOURS;
1862 /* Check for UPnP */
1864 = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1867 if (GNUNET_YES == enable_upnp)
1869 /* check if it works */
1870 if (GNUNET_SYSERR ==
1871 GNUNET_OS_check_helper_binary ("upnpc",
1875 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1876 _("UPnP enabled in configuration, but UPnP client `upnpc` command not found, disabling UPnP\n"));
1877 enable_upnp = GNUNET_SYSERR;
1881 GNUNET_CONFIGURATION_get_value_time (cfg,
1885 dyndns_frequency = DYNDNS_FREQUENCY;
1887 GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1889 stats = GNUNET_STATISTICS_create ("nat",
1891 scan_task = GNUNET_SCHEDULER_add_now (&run_scan,
1897 * Callback called when a client connects to the service.
1899 * @param cls closure for the service
1900 * @param c the new client that connected to the service
1901 * @param mq the message queue used to send messages to the client
1902 * @return a `struct ClientHandle`
1905 client_connect_cb (void *cls,
1906 struct GNUNET_SERVICE_Client *c,
1907 struct GNUNET_MQ_Handle *mq)
1909 struct ClientHandle *ch;
1911 ch = GNUNET_new (struct ClientHandle);
1914 GNUNET_CONTAINER_DLL_insert (ch_head,
1922 * Callback called when a client disconnected from the service
1924 * @param cls closure for the service
1925 * @param c the client that disconnected
1926 * @param internal_cls a `struct ClientHandle *`
1929 client_disconnect_cb (void *cls,
1930 struct GNUNET_SERVICE_Client *c,
1933 struct ClientHandle *ch = internal_cls;
1934 struct LocalAddressList *lal;
1936 GNUNET_CONTAINER_DLL_remove (ch_head,
1939 for (unsigned int i=0;i<ch->num_caddrs;i++)
1941 if (NULL != ch->caddrs[i].mh)
1943 GNUNET_NAT_mini_map_stop (ch->caddrs[i].mh);
1944 ch->caddrs[i].mh = NULL;
1947 GNUNET_free_non_null (ch->caddrs);
1948 while (NULL != (lal = ch->ext_addr_head))
1950 GNUNET_CONTAINER_DLL_remove (ch->ext_addr_head,
1955 if (NULL != ch->ext_dns_task)
1957 GNUNET_SCHEDULER_cancel (ch->ext_dns_task);
1958 ch->ext_dns_task = NULL;
1960 if (NULL != ch->external_monitor)
1962 GN_external_ipv4_monitor_stop (ch->external_monitor);
1963 ch->external_monitor = NULL;
1965 if (NULL != ch->ext_dns)
1967 GNUNET_RESOLVER_request_cancel (ch->ext_dns);
1970 GNUNET_free_non_null (ch->hole_external);
1971 GNUNET_free (ch->section_name);
1977 * Define "main" method using service macro.
1981 GNUNET_SERVICE_OPTION_NONE,
1984 &client_disconnect_cb,
1986 GNUNET_MQ_hd_var_size (register,
1987 GNUNET_MESSAGE_TYPE_NAT_REGISTER,
1988 struct GNUNET_NAT_RegisterMessage,
1990 GNUNET_MQ_hd_var_size (stun,
1991 GNUNET_MESSAGE_TYPE_NAT_HANDLE_STUN,
1992 struct GNUNET_NAT_HandleStunMessage,
1994 GNUNET_MQ_hd_var_size (request_connection_reversal,
1995 GNUNET_MESSAGE_TYPE_NAT_REQUEST_CONNECTION_REVERSAL,
1996 struct GNUNET_NAT_RequestConnectionReversalMessage,
1998 GNUNET_MQ_handler_end ());
2001 #if defined(LINUX) && defined(__GLIBC__)
2005 * MINIMIZE heap size (way below 128k) since this process doesn't need much.
2007 void __attribute__ ((constructor))
2008 GNUNET_ARM_memory_init ()
2010 mallopt (M_TRIM_THRESHOLD, 4 * 1024);
2011 mallopt (M_TOP_PAD, 1 * 1024);
2016 /* end of gnunet-service-nat.c */