From 8108e41d89bb771fe8bcf887919de3f581f1cc0d Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 16 Dec 2016 10:53:58 +0100 Subject: add PORT to NAT configuration, generate nat.conf from nat.conf.in, implement more of new NAT service --- src/nat/Makefile.am | 2 +- src/nat/gnunet-service-nat.c | 274 +++++++++++++++++++++--------------- src/nat/gnunet-service-nat_helper.c | 40 ++++-- src/nat/gnunet-service-nat_helper.h | 8 +- src/nat/nat.conf | 64 --------- src/nat/nat.conf.in | 74 ++++++++++ 6 files changed, 269 insertions(+), 193 deletions(-) delete mode 100644 src/nat/nat.conf create mode 100644 src/nat/nat.conf.in (limited to 'src/nat') diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am index d304a57d0..c62a8d2cf 100644 --- a/src/nat/Makefile.am +++ b/src/nat/Makefile.am @@ -12,7 +12,7 @@ libexecdir= $(pkglibdir)/libexec/ pkgcfgdir= $(pkgdatadir)/config.d/ -dist_pkgcfg_DATA = \ +pkgcfg_DATA = \ nat.conf if LINUX diff --git a/src/nat/gnunet-service-nat.c b/src/nat/gnunet-service-nat.c index f4755659f..1ebf75608 100644 --- a/src/nat/gnunet-service-nat.c +++ b/src/nat/gnunet-service-nat.c @@ -28,10 +28,8 @@ * knowledge about the local network topology. * * TODO: - * - call GN_start_gnunet_nat_server_() if possible (i.e. - * when we find we have a non-global IPv4 address) + * - implement UPnPC/PMP-based NAT traversal * - implement autoconfig - * - implmeent UPnPC/PMP-based NAT traversal * - implement NEW logic for external IP detection */ #include "platform.h" @@ -90,6 +88,11 @@ struct ClientHandle */ enum GNUNET_NAT_RegisterFlags flags; + /** + * Is any of the @e addrs in a reserved subnet for NAT? + */ + int natted_address; + /** * Port we would like as we are configured to use this one for * advertising (in addition to the one we are binding to). @@ -305,6 +308,108 @@ check_register (void *cls, } +/** + * Check if @a ip is in @a network with @a bits netmask. + * + * @param network to test + * @param ip IP address to test + * @param bits bitmask for the network + * @return #GNUNET_YES if @a ip is in @a network + */ +static int +match_ipv4 (const char *network, + const struct in_addr *ip, + uint8_t bits) +{ + struct in_addr net; + + if (0 == bits) + return GNUNET_YES; + GNUNET_assert (1 == inet_pton (AF_INET, + network, + &net)); + return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); +} + + +/** + * Check if @a ip is in @a network with @a bits netmask. + * + * @param network to test + * @param ip IP address to test + * @param bits bitmask for the network + * @return #GNUNET_YES if @a ip is in @a network + */ +static int +match_ipv6 (const char *network, + const struct in6_addr *ip, + uint8_t bits) +{ + struct in6_addr net; + struct in6_addr mask; + unsigned int off; + + if (0 == bits) + return GNUNET_YES; + GNUNET_assert (1 == inet_pton (AF_INET, + network, + &net)); + memset (&mask, 0, sizeof (mask)); + off = 0; + while (bits > 8) + { + mask.s6_addr[off++] = 0xFF; + bits -= 8; + } + while (bits > 0) + { + mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; + bits--; + } + for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++) + if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != + (((uint32_t *) &net)[j] & ((int *) &mask)[j])) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** + * Test if the given IPv4 address is in a known range + * for private networks. + * + * @param ip address to test + * @return #GNUNET_YES if @a ip is in a NAT range + */ +static int +is_nat_v4 (const struct in_addr *ip) +{ + return + match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ + match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ + match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ + match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ + match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ +} + + +/** + * Test if the given IPv6 address is in a known range + * for private networks. + * + * @param ip address to test + * @return #GNUNET_YES if @a ip is in a NAT range + */ +static int +is_nat_v6 (const struct in6_addr *ip) +{ + return + match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ + match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ + match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ +} + + /** * Handler for #GNUNET_MESSAGE_TYPE_NAT_REGISTER message from client. * We remember the client for updates upon future NAT events. @@ -352,10 +457,22 @@ handle_register (void *cls, switch (sa->sa_family) { case AF_INET: - alen = sizeof (struct sockaddr_in); + { + const struct sockaddr_in *s4 = (const struct sockaddr_in *) sa; + + alen = sizeof (struct sockaddr_in); + if (is_nat_v4 (&s4->sin_addr)) + ch->natted_address = GNUNET_YES; + } break; case AF_INET6: - alen = sizeof (struct sockaddr_in6); + { + const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *) sa; + + alen = sizeof (struct sockaddr_in6); + if (is_nat_v6 (&s6->sin6_addr)) + ch->natted_address = GNUNET_YES; + } break; #if AF_UNIX case AF_UNIX: @@ -418,7 +535,30 @@ static void notify_clients_stun_change (const struct sockaddr_in *ip, int add) { - /* FIXME: notify clients about add/drop */ + for (struct ClientHandle *ch = ch_head; + NULL != ch; + ch = ch->next) + { + struct sockaddr_in v4; + struct GNUNET_NAT_AddressChangeNotificationMessage *msg; + struct GNUNET_MQ_Envelope *env; + + if (! ch->natted_address) + continue; + v4 = *ip; + v4.sin_port = htons (ch->adv_port); + env = GNUNET_MQ_msg_extra (msg, + sizeof (v4), + GNUNET_MESSAGE_TYPE_NAT_ADDRESS_CHANGE); + msg->add_remove = htonl ((int32_t) add); + msg->addr_class = htonl (GNUNET_NAT_AC_GLOBAL_EXTERN | + GNUNET_NAT_AC_GLOBAL); + GNUNET_memcpy (&msg[1], + &v4, + sizeof (v4)); + GNUNET_MQ_send (ch->mq, + env); + } } @@ -599,6 +739,9 @@ handle_request_connection_reversal (void *cls, size_t remote_sa_len = ntohs (message->remote_addr_size); const struct sockaddr *local_sa = (const struct sockaddr *) &buf[0]; const struct sockaddr *remote_sa = (const struct sockaddr *) &buf[local_sa_len]; + const struct sockaddr_in *l4 = NULL; + const struct sockaddr_in *r4; + int ret; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received REQUEST CONNECTION REVERSAL message from client\n"); @@ -611,6 +754,7 @@ handle_request_connection_reversal (void *cls, GNUNET_SERVICE_client_drop (ch->client); return; } + l4 = (const struct sockaddr_in *) local_sa; break; case AF_INET6: if (local_sa_len != sizeof (struct sockaddr_in6)) @@ -619,6 +763,9 @@ handle_request_connection_reversal (void *cls, GNUNET_SERVICE_client_drop (ch->client); return; } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection reversal for IPv6 not supported yet\n")); + ret = GNUNET_SYSERR; break; default: GNUNET_break (0); @@ -634,6 +781,10 @@ handle_request_connection_reversal (void *cls, GNUNET_SERVICE_client_drop (ch->client); return; } + r4 = (const struct sockaddr_in *) remote_sa; + ret = GN_request_connection_reversal (&l4->sin_addr, + ntohs (l4->sin_port), + &r4->sin_addr); break; case AF_INET6: if (remote_sa_len != sizeof (struct sockaddr_in6)) @@ -642,15 +793,18 @@ handle_request_connection_reversal (void *cls, GNUNET_SERVICE_client_drop (ch->client); return; } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection reversal for IPv6 not supported yet\n")); + ret = GNUNET_SYSERR; break; default: GNUNET_break (0); GNUNET_SERVICE_client_drop (ch->client); return; } - /* FIXME: actually run the logic by - calling 'GN_request_connection_reversal()' */ - + if (GNUNET_OK != ret) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Connection reversal request failed\n")); GNUNET_SERVICE_client_continue (ch->client); } @@ -756,108 +910,6 @@ struct IfcProcContext }; -/** - * Check if @a ip is in @a network with @a bits netmask. - * - * @param network to test - * @param ip IP address to test - * @param bits bitmask for the network - * @return #GNUNET_YES if @a ip is in @a network - */ -static int -match_ipv4 (const char *network, - const struct in_addr *ip, - uint8_t bits) -{ - struct in_addr net; - - if (0 == bits) - return GNUNET_YES; - GNUNET_assert (1 == inet_pton (AF_INET, - network, - &net)); - return ! ((ip->s_addr ^ net.s_addr) & htonl (0xFFFFFFFFu << (32 - bits))); -} - - -/** - * Check if @a ip is in @a network with @a bits netmask. - * - * @param network to test - * @param ip IP address to test - * @param bits bitmask for the network - * @return #GNUNET_YES if @a ip is in @a network - */ -static int -match_ipv6 (const char *network, - const struct in6_addr *ip, - uint8_t bits) -{ - struct in6_addr net; - struct in6_addr mask; - unsigned int off; - - if (0 == bits) - return GNUNET_YES; - GNUNET_assert (1 == inet_pton (AF_INET, - network, - &net)); - memset (&mask, 0, sizeof (mask)); - off = 0; - while (bits > 8) - { - mask.s6_addr[off++] = 0xFF; - bits -= 8; - } - while (bits > 0) - { - mask.s6_addr[off] = (mask.s6_addr[off] >> 1) + 0x80; - bits--; - } - for (unsigned j = 0; j < sizeof (struct in6_addr) / sizeof (uint32_t); j++) - if (((((uint32_t *) ip)[j] & ((uint32_t *) &mask)[j])) != - (((uint32_t *) &net)[j] & ((int *) &mask)[j])) - return GNUNET_NO; - return GNUNET_YES; -} - - -/** - * Test if the given IPv4 address is in a known range - * for private networks. - * - * @param ip address to test - * @return #GNUNET_YES if @a ip is in a NAT range - */ -static int -is_nat_v4 (const struct in_addr *ip) -{ - return - match_ipv4 ("10.0.0.0", ip, 8) || /* RFC 1918 */ - match_ipv4 ("100.64.0.0", ip, 10) || /* CG-NAT, RFC 6598 */ - match_ipv4 ("192.168.0.0", ip, 12) || /* RFC 1918 */ - match_ipv4 ("169.254.0.0", ip, 16) || /* AUTO, RFC 3927 */ - match_ipv4 ("172.16.0.0", ip, 16); /* RFC 1918 */ -} - - -/** - * Test if the given IPv6 address is in a known range - * for private networks. - * - * @param ip address to test - * @return #GNUNET_YES if @a ip is in a NAT range - */ -static int -is_nat_v6 (const struct in6_addr *ip) -{ - return - match_ipv6 ("fc00::", ip, 7) || /* RFC 4193 */ - match_ipv6 ("fec0::", ip, 10) || /* RFC 3879 */ - match_ipv6 ("fe80::", ip, 10); /* RFC 4291, link-local */ -} - - /** * Callback function invoked for each interface found. Adds them * to our new address list. diff --git a/src/nat/gnunet-service-nat_helper.c b/src/nat/gnunet-service-nat_helper.c index e476da12d..379603ae2 100644 --- a/src/nat/gnunet-service-nat_helper.c +++ b/src/nat/gnunet-service-nat_helper.c @@ -319,7 +319,7 @@ GN_stop_gnunet_nat_server_ (struct HelperContext *h) GNUNET_SCHEDULER_cancel (h->server_read_task); h->server_read_task = NULL; } - if (NULL != h->server_proc) + if (NULL != h->server_proc) { if (0 != GNUNET_OS_process_kill (h->server_proc, GNUNET_TERM_SIG)) @@ -349,23 +349,25 @@ GN_stop_gnunet_nat_server_ (struct HelperContext *h) * that peer to connect to us (connection reversal). * * @param internal_address out internal address to use - * @param sa the address of the peer (IPv4-only) + * @param internal_port port to use + * @param remote_v4 the address of the peer (IPv4-only) * @return #GNUNET_SYSERR on error, * #GNUNET_OK otherwise */ int -GN_request_connection_reversal (const char *internal_address, - const struct sockaddr_in *sa) +GN_request_connection_reversal (const struct in_addr *internal_address, + uint16_t internal_port, + const struct in_addr *remote_v4) { - char inet4[INET_ADDRSTRLEN]; + char intv4[INET_ADDRSTRLEN]; + char remv4[INET_ADDRSTRLEN]; char port_as_string[6]; struct GNUNET_OS_Process *proc; char *binary; - GNUNET_assert (sa->sin_family == AF_INET); if (NULL == inet_ntop (AF_INET, - &sa->sin_addr, - inet4, + internal_address, + intv4, INET_ADDRSTRLEN)) { GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING, @@ -373,15 +375,25 @@ GN_request_connection_reversal (const char *internal_address, "inet_ntop"); return GNUNET_SYSERR; } + if (NULL == inet_ntop (AF_INET, + remote_v4, + remv4, + INET_ADDRSTRLEN)) + { + GNUNET_log_from_strerror (GNUNET_ERROR_TYPE_WARNING, + "nat", + "inet_ntop"); + return GNUNET_SYSERR; + } GNUNET_snprintf (port_as_string, sizeof (port_as_string), "%d", - ntohs (sa->sin_port)); + internal_port); LOG (GNUNET_ERROR_TYPE_DEBUG, _("Running gnunet-helper-nat-client %s %s %u\n"), - internal_address, - inet4, - ntohs (sa->sin_port)); + intv4, + remv4, + internal_port); binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client"); proc @@ -392,8 +404,8 @@ GN_request_connection_reversal (const char *internal_address, NULL, binary, "gnunet-helper-nat-client", - internal_address, - inet4, + intv4, + remv4, port_as_string, NULL); GNUNET_free (binary); diff --git a/src/nat/gnunet-service-nat_helper.h b/src/nat/gnunet-service-nat_helper.h index 861d62c1d..d3f1a757c 100644 --- a/src/nat/gnunet-service-nat_helper.h +++ b/src/nat/gnunet-service-nat_helper.h @@ -77,13 +77,15 @@ GN_stop_gnunet_nat_server_ (struct HelperContext *h); * that peer to connect to us (connection reversal). * * @param internal_address out internal address to use - * @param sa the address of the peer (IPv4-only) + * @param internal_port internal port to use + * @param remote_v4 the address of the peer (IPv4-only) * @return #GNUNET_SYSERR on error, * #GNUNET_OK otherwise */ int -GN_request_connection_reversal (const char *internal_address, - const struct sockaddr_in *sa); +GN_request_connection_reversal (const struct in_addr *internal_address, + uint16_t internal_port, + const struct in_addr *sa); /* end of gnunet-service-nat_helper.h */ diff --git a/src/nat/nat.conf b/src/nat/nat.conf deleted file mode 100644 index 4493a6ec9..000000000 --- a/src/nat/nat.conf +++ /dev/null @@ -1,64 +0,0 @@ -[nat] -# Are we behind NAT? -BEHIND_NAT = NO - -# Is the NAT hole-punched? -PUNCHED_NAT = NO - -# Enable UPNP by default? -ENABLE_UPNP = NO - -# Use addresses from the local network interfaces (inluding loopback, but also others) -USE_LOCALADDR = YES - -# Use address obtained from a DNS lookup of our hostname -USE_HOSTNAME = NO - -# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) -# normal interface IP address for non-NATed peers; -# possibly auto-detected (using UPnP) if possible if not specified -# EXTERNAL_ADDRESS = - -# Should we use ICMP-based NAT traversal to try connect to NATed peers -# or, if we are behind NAT, to allow connections to us? -ENABLE_ICMP_CLIENT = NO -ENABLE_ICMP_SERVER = NO - -# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; -# normal interface IP address for non-NATed peers; -# likely auto-detected (via interface list) if not specified (!) -# INTERNAL_ADDRESS = - -# Disable IPv6 support -DISABLEV6 = NO - -# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8) -RETURN_LOCAL_ADDRESSES = NO - -# How often do we query the DNS resolver -# for our hostname (to get our own IP) -HOSTNAME_DNS_FREQUENCY = 20 min - -# How often do we iterate over our -# network interfaces to check for changes -# in our IP address? -IFC_SCAN_FREQUENCY = 15 min - -# How often do we query the DNS resolver -# for our hostname (to get our own IP) -DYNDNS_FREQUENCY = 7 min - -# SHOULD USE STUN ? -USE_STUN = YES -STUN_FREQUENCY = 5 min -# Default list of stun servers -STUN_SERVERS = stun.gnunet.org stun.services.mozilla.com:3478 stun.ekiga.net:3478 - -# After how long do we consider STUN data stale? -STUN_STALE = 60 min - - -[gnunet-nat-server] -HOSTNAME = gnunet.org -PORT = 5724 -NOARMBIND = YES diff --git a/src/nat/nat.conf.in b/src/nat/nat.conf.in new file mode 100644 index 000000000..304db3c15 --- /dev/null +++ b/src/nat/nat.conf.in @@ -0,0 +1,74 @@ +[nat] +AUTOSTART = @AUTOSTART@ +@UNIXONLY@ PORT = 2121 +HOSTNAME = localhost +BINARY = gnunet-service-nat +ACCEPT_FROM = 127.0.0.1; +ACCEPT_FROM6 = ::1; +UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-nat.sock +UNIX_MATCH_UID = YES +UNIX_MATCH_GID = YES + +# Are we behind NAT? +BEHIND_NAT = NO + +# Is the NAT hole-punched? +PUNCHED_NAT = NO + +# Enable UPNP by default? +ENABLE_UPNP = NO + +# Use addresses from the local network interfaces (inluding loopback, but also others) +USE_LOCALADDR = YES + +# Use address obtained from a DNS lookup of our hostname +USE_HOSTNAME = NO + +# External IP address of the NAT box (if known); IPv4 dotted-decimal ONLY at this time (should allow DynDNS!) +# normal interface IP address for non-NATed peers; +# possibly auto-detected (using UPnP) if possible if not specified +# EXTERNAL_ADDRESS = + +# Should we use ICMP-based NAT traversal to try connect to NATed peers +# or, if we are behind NAT, to allow connections to us? +ENABLE_ICMP_CLIENT = NO +ENABLE_ICMP_SERVER = NO + +# IP address of the interface connected to the NAT box; IPv4 dotted-decimal ONLY; +# normal interface IP address for non-NATed peers; +# likely auto-detected (via interface list) if not specified (!) +# INTERNAL_ADDRESS = + +# Disable IPv6 support +DISABLEV6 = NO + +# Do we use addresses from localhost address ranges? (::1, 127.0.0.0/8) +RETURN_LOCAL_ADDRESSES = NO + +# How often do we query the DNS resolver +# for our hostname (to get our own IP) +HOSTNAME_DNS_FREQUENCY = 20 min + +# How often do we iterate over our +# network interfaces to check for changes +# in our IP address? +IFC_SCAN_FREQUENCY = 15 min + +# How often do we query the DNS resolver +# for our hostname (to get our own IP) +DYNDNS_FREQUENCY = 7 min + +# SHOULD USE STUN ? +USE_STUN = YES +STUN_FREQUENCY = 5 min +# Default list of stun servers +STUN_SERVERS = stun.gnunet.org stun.services.mozilla.com:3478 stun.ekiga.net:3478 + +# After how long do we consider STUN data stale? +STUN_STALE = 60 min + + +[gnunet-nat-server] +HOSTNAME = gnunet.org +PORT = 5724 +NOARMBIND = YES -- cgit v1.2.3