diff --git a/dns64/dns64.c b/dns64/dns64.c index 4b98b609e..cd53df278 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -59,7 +59,7 @@ ******************************************************************************/ /** - * This is the default DNS64 prefix that is used whent he dns64 module is listed + * This is the default DNS64 prefix that is used when the dns64 module is listed * in module-config but when the dns64-prefix variable is not present. */ static const char DEFAULT_DNS64_PREFIX[] = "64:ff9b::/96"; diff --git a/doc/example.conf.in b/doc/example.conf.in index 279bcccdd..0980212e1 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -248,6 +248,8 @@ server: # the network, unbound can use NAT64 to reach these servers with # the following option. This is NOT needed for enabling DNS64 on a # system that has IPv4 connectivity. + # Consider also enabling prefer-ip6 to prefer native IPv6 connections + # to nameservers. # do-nat64: no # NAT64 prefix. Defaults to using dns64-prefix value. diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 9e2edaba6..76ffde9bd 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2318,12 +2318,15 @@ NAT64 operation allows using a NAT64 prefix for outbound requests to IPv4-only servers. It is controlled by two options in the \fBserver:\fR section: .TP .B do\-nat64: \fI\fR -Use NAT64 to reach IPv4-only servers. Default no. +Use NAT64 to reach IPv4-only servers. +Consider also enabling \fBprefer\-ip6\fR to prefer native IPv6 connections to +nameservers. +Default no. .TP .B nat64\-prefix: \fI\fR Use a specific NAT64 prefix to reach IPv4-only servers. Defaults to using the prefix configured in \fBdns64\-prefix\fR, which in turn defaults to -64:ff9b::/96. Must be /96 or shorter. +64:ff9b::/96. The prefix length must be one of /32, /40, /48, /56, /64 or /96. .SS "DNSCrypt Options" .LP The diff --git a/iterator/iter_utils.c b/iterator/iter_utils.c index c8c41a196..961f76241 100644 --- a/iterator/iter_utils.c +++ b/iterator/iter_utils.c @@ -180,25 +180,23 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg) } nat64_prefix = cfg->nat64_prefix; - if (!nat64_prefix) + if(!nat64_prefix) nat64_prefix = cfg->dns64_prefix; - if (!nat64_prefix) + if(!nat64_prefix) nat64_prefix = DEFAULT_NAT64_PREFIX; - if (!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr, - &iter_env->nat64_prefix_addrlen, - &iter_env->nat64_prefix_net)) { + if(!netblockstrtoaddr(nat64_prefix, 0, &iter_env->nat64_prefix_addr, + &iter_env->nat64_prefix_addrlen, + &iter_env->nat64_prefix_net)) { log_err("cannot parse nat64-prefix netblock: %s", nat64_prefix); return 0; } - if (!addr_is_ip6(&iter_env->nat64_prefix_addr, - iter_env->nat64_prefix_addrlen)) { - log_err("nat64_prefix is not IPv6: %s", cfg->nat64_prefix); + if(!addr_is_ip6(&iter_env->nat64_prefix_addr, + iter_env->nat64_prefix_addrlen)) { + log_err("nat64-prefix is not IPv6: %s", cfg->nat64_prefix); return 0; } - if (iter_env->nat64_prefix_net != 32 && iter_env->nat64_prefix_net != 40 && - iter_env->nat64_prefix_net != 48 && iter_env->nat64_prefix_net != 56 && - iter_env->nat64_prefix_net != 64 && iter_env->nat64_prefix_net != 96 ) { - log_err("dns64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", + if(!prefixnet_is_nat64(iter_env->nat64_prefix_net)) { + log_err("nat64-prefix length it not 32, 40, 48, 56, 64 or 96: %s", nat64_prefix); return 0; } @@ -780,12 +778,13 @@ iter_mark_pside_cycle_targets(struct module_qstate* qstate, struct delegpt* dp) int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, - struct delegpt* dp, int supports_ipv4, int supports_ipv6, int use_nat64) + struct delegpt* dp, int supports_ipv4, int supports_ipv6, + int use_nat64) { struct delegpt_ns* ns; struct delegpt_addr* a; - if (supports_ipv6 && use_nat64) + if(supports_ipv6 && use_nat64) supports_ipv4 = 1; /* check: diff --git a/iterator/iter_utils.h b/iterator/iter_utils.h index 48ea8316b..fa860fa68 100644 --- a/iterator/iter_utils.h +++ b/iterator/iter_utils.h @@ -189,9 +189,11 @@ void iter_mark_pside_cycle_targets(struct module_qstate* qstate, * if not, then the IPv4 addresses are useless. * @param supports_ipv6: if we support ipv6 for lookups to the target. * if not, then the IPv6 addresses are useless. + * @param use_nat64: if we support NAT64 for lookups to the target. + * if yes, IPv4 addresses are useful even if we don't support IPv4. * @return true if dp is useless. */ -int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, +int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags, struct delegpt* dp, int supports_ipv4, int supports_ipv6, int use_nat64); diff --git a/iterator/iterator.c b/iterator/iterator.c index 3e0ee035e..e7365c566 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1557,17 +1557,17 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, /* see if this dp not useless. * It is useless if: - * o all NS items are required glue. + * o all NS items are required glue. * or the query is for NS item that is required glue. * o no addresses are provided. * o RD qflag is on. * Instead, go up one level, and try to get even further - * If the root was useless, use safety belt information. + * If the root was useless, use safety belt information. * Only check cache returns, because replies for servers * could be useless but lead to loops (bumping into the * same server reply) if useless-checked. */ - if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, + if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags, iq->dp, ie->supports_ipv4, ie->supports_ipv6, ie->use_nat64)) { struct delegpt* retdp = NULL; @@ -1930,7 +1930,7 @@ query_for_targets(struct module_qstate* qstate, struct iter_qstate* iq, break; } /* Send the A request. */ - if(ie->supports_ipv4 && + if((ie->supports_ipv4 || ie->use_nat64) && ((ns->lame && !ns->done_pside4) || (!ns->lame && !ns->got4))) { if(!generate_target_query(qstate, iq, id, @@ -2257,6 +2257,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, int tf_policy; struct delegpt_addr* target; struct outbound_entry* outq; + struct sockaddr_storage real_addr; + socklen_t real_addrlen; int auth_fallback = 0; uint8_t* qout_orig = NULL; size_t qout_orig_len = 0; @@ -2803,45 +2805,22 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* We have a valid target. */ if(verbosity >= VERB_QUERY) { log_query_info(VERB_QUERY, "sending query:", &iq->qinfo_out); - log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name, + log_name_addr(VERB_QUERY, "sending to target:", iq->dp->name, &target->addr, target->addrlen); verbose(VERB_ALGO, "dnssec status: %s%s", iq->dnssec_expected?"expected": "not expected", iq->dnssec_lame_query?" but lame_query anyway": ""); } - struct sockaddr_storage real_addr = target->addr; - socklen_t real_addrlen = target->addrlen; + real_addr = target->addr; + real_addrlen = target->addrlen; - if (ie->use_nat64 && real_addr.ss_family == AF_INET) { - struct sockaddr_in *sin = (struct sockaddr_in *)&target->addr; - struct sockaddr_in6 *sin6; - int plen = ie->nat64_prefix_net; - uint8_t *v4_byte; - - real_addr = ie->nat64_prefix_addr; - real_addrlen = ie->nat64_prefix_addrlen; - - sin6 = (struct sockaddr_in6 *)&real_addr; - sin6->sin6_flowinfo = 0; - sin6->sin6_port = sin->sin_port; - - /* config validation enforces these prefix lengths too */ - log_assert(plen == 32 || plen == 40 || plen == 48 || plen == 56 - || plen == 64 || plen == 96); - plen = plen / 8; - - v4_byte = (uint8_t *)&sin->sin_addr.s_addr; - for (int i = 0; i < 4; i++) { - if (plen == 8) - /* bits 64...72 are MBZ */ - sin6->sin6_addr.s6_addr[plen++] = 0; - - sin6->sin6_addr.s6_addr[plen++] = *v4_byte++; - } - - log_name_addr(VERB_QUERY, "applied NAT64:", iq->dp->name, - &real_addr, real_addrlen); + if(ie->use_nat64 && target->addr.ss_family == AF_INET) { + addr_to_nat64(&target->addr, &ie->nat64_prefix_addr, + ie->nat64_prefix_addrlen, ie->nat64_prefix_net, + &real_addr, &real_addrlen); + log_name_addr(VERB_QUERY, "applied NAT64:", + iq->dp->name, &real_addr, real_addrlen); } fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query)); @@ -2871,7 +2850,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, return error_response(qstate, id, LDNS_RCODE_SERVFAIL); } log_addr(VERB_QUERY, "error sending query to auth server", - &target->addr, target->addrlen); + &real_addr, real_addrlen); if(qstate->env->cfg->qname_minimisation) iq->minimisation_state = SKIP_MINIMISE_STATE; return next_state(iq, QUERYTARGETS_STATE); diff --git a/iterator/iterator.h b/iterator/iterator.h index 9171ff60e..74299e05a 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -103,7 +103,7 @@ extern int BLACKLIST_PENALTY; #define RTT_BAND 400 /** - * Global state for the iterator. + * Global state for the iterator. */ struct iter_env { /** A flag to indicate whether or not we have an IPv6 route */ diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c index f850469ba..ff8043711 100644 --- a/smallapp/unbound-checkconf.c +++ b/smallapp/unbound-checkconf.c @@ -714,7 +714,7 @@ morechecks(struct config_file* cfg) cfg->chrootdir, cfg); } #endif - /* remove chroot setting so that modules are not stripping pathnames*/ + /* remove chroot setting so that modules are not stripping pathnames */ free(cfg->chrootdir); cfg->chrootdir = NULL; diff --git a/testdata/iter_nat64_prefix.rpl b/testdata/iter_nat64_prefix.rpl index afc317e85..ecb6508dc 100644 --- a/testdata/iter_nat64_prefix.rpl +++ b/testdata/iter_nat64_prefix.rpl @@ -3,6 +3,7 @@ server: do-nat64: yes nat64-prefix: 2001:db8:1234::/96 target-fetch-policy: "0 0 0 0 0" + do-ip4: no stub-zone: name: "." diff --git a/util/config_file.c b/util/config_file.c index a04c8cf09..54bd5f952 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -1648,6 +1648,7 @@ config_delete(struct config_file* cfg) free(cfg->server_cert_file); free(cfg->control_key_file); free(cfg->control_cert_file); + free(cfg->nat64_prefix); free(cfg->dns64_prefix); config_delstrlist(cfg->dns64_ignore_aaaa); free(cfg->dnstap_socket_path); diff --git a/util/net_help.c b/util/net_help.c index de2d771bd..e559c9b2f 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -779,8 +779,8 @@ addr_in_common(struct sockaddr_storage* addr1, int net1, return match; } -void -addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, +void +addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, char* buf, size_t len) { int af = (int)((struct sockaddr_in*)addr)->sin_family; @@ -792,7 +792,50 @@ addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, } } -int +int +prefixnet_is_nat64(int prefixnet) +{ + return (prefixnet == 32 || prefixnet == 40 || + prefixnet == 48 || prefixnet == 56 || + prefixnet == 64 || prefixnet == 96); +} + +void +addr_to_nat64(const struct sockaddr_storage* addr, + const struct sockaddr_storage* nat64_prefix, + socklen_t nat64_prefixlen, int nat64_prefixnet, + struct sockaddr_storage* nat64_addr, socklen_t* nat64_addrlen) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + struct sockaddr_in6 *sin6; + uint8_t *v4_byte; + + /* This needs to be checked by the caller */ + log_assert(addr->ss_family == AF_INET); + /* Current usage is only from config values; prefix lengths enforced + * during config validation */ + log_assert(prefixnet_is_nat64(nat64_prefixnet)); + + *nat64_addr = *nat64_prefix; + *nat64_addrlen = nat64_prefixlen; + + sin6 = (struct sockaddr_in6 *)nat64_addr; + sin6->sin6_flowinfo = 0; + sin6->sin6_port = sin->sin_port; + + nat64_prefixnet = nat64_prefixnet / 8; + + v4_byte = (uint8_t *)&sin->sin_addr.s_addr; + for(int i = 0; i < 4; i++) { + if(nat64_prefixnet == 8) { + /* bits 64...71 are MBZ */ + sin6->sin6_addr.s6_addr[nat64_prefixnet++] = 0; + } + sin6->sin6_addr.s6_addr[nat64_prefixnet++] = *v4_byte++; + } +} + +int addr_is_ip4mapped(struct sockaddr_storage* addr, socklen_t addrlen) { /* prefix for ipv4 into ipv6 mapping is ::ffff:x.x.x.x */ diff --git a/util/net_help.h b/util/net_help.h index f1881b3ed..e931ed8a6 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -331,6 +331,30 @@ int addr_in_common(struct sockaddr_storage* addr1, int net1, void addr_to_str(struct sockaddr_storage* addr, socklen_t addrlen, char* buf, size_t len); +/** + * Check if the prefix network length is one of the allowed 32, 40, 48, 56, 64, + * or 96. + * @param prefixnet: prefix network length to check. + * @return 1 on success, 0 on failure. + */ +int prefixnet_is_nat64(int prefixnet); + +/** + * Create a NAT64 address from a given address (needs to be IPv4) and a given + * NAT64 prefix. The NAT64 prefix net needs to be one of 32, 40, 48, 56, 64, 96. + * @param addr: IPv4 address. + * @param nat64_prefix: NAT64 prefix. + * @param nat64_prefixlen: NAT64 prefix len. + * @param nat64_prefixnet: NAT64 prefix mask. + * @param nat64_addr: the resulting NAT64 address. + * @param nat64_addrlen: the resulting NAT64 address length. + * @return: 1 on success, 0 on input error. + */ +void addr_to_nat64(const struct sockaddr_storage* addr, + const struct sockaddr_storage* nat64_prefix, + socklen_t nat64_prefixlen, int nat64_prefixnet, + struct sockaddr_storage* nat64_addr, socklen_t* nat64_addrlen); + /** * See if sockaddr is an ipv6 mapped ipv4 address, "::ffff:0.0.0.0" * @param addr: address