NAT64 support

This implements #721.  Includes documentation and some very basic tests.
Please refer to doc for further detail.
This commit is contained in:
David Lamparter 2022-07-23 14:26:20 -04:00
parent f531faf163
commit 64fb06f892
16 changed files with 528 additions and 17 deletions

View File

@ -859,7 +859,8 @@ int print_deleg_lookup(RES* ssl, struct worker* worker, uint8_t* nm,
/* go up? */
if(iter_dp_is_useless(&qinfo, BIT_RD, dp,
(worker->env.cfg->do_ip4 && worker->back->num_ip4 != 0),
(worker->env.cfg->do_ip6 && worker->back->num_ip6 != 0))) {
(worker->env.cfg->do_ip6 && worker->back->num_ip6 != 0),
worker->env.cfg->do_nat64)) {
print_dp_main(ssl, dp, msg);
print_dp_details(ssl, worker, dp);
if(!ssl_printf(ssl, "cache delegation was "

View File

@ -1,3 +1,6 @@
5 November 2022: equinox (David Lamparter)
- Implemented NAT64 support
21 October 2022: George
- Merge #767 from jonathangray: consistently use IPv4/IPv6 in
unbound.conf.5.

View File

@ -28,3 +28,23 @@ prefix. For example:
;; ANSWER SECTION:
jazz-v4.viagenie.ca. 86400 IN AAAA 64:ff9b::ce7b:1f02
NAT64 support was added by David Lamparter in 2022; license(s) of the
surrounding code apply. Note that NAT64 is closely related but functionally
orthogonal to DNS64; it allows Unbound to send outgoing queries to IPv4-only
servers over IPv6 through the configured NAT64 prefix. This allows running
an Unbound instance on an IPv6-only host without breaking every single domain
that only has IPv4 servers. Whether that Unbound instance also does DNS64 is
an independent choice.
To enable NAT64 in Unbound, add to unbound.conf's "server" section:
do-nat64: yes
The NAT64 prefix defaults to the DNS64 prefix, which in turn defaults to the
standard 64:FF9B::/96 prefix. You can reconfigure it with:
nat64-prefix: 64:FF9B::/96
To test NAT64 operation, pick a domain that only has IPv4 reachability for its
nameservers and try resolving any names in that domain.

View File

@ -229,6 +229,16 @@ server:
# Enable IPv6, "yes" or "no".
# do-ip6: yes
# If running unbound on an IPv6-only host, domains that only have
# IPv4 servers would become unresolveable. If NAT64 is available in
# 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.
# do-nat64: no
# NAT64 prefix. Defaults to using dns64-prefix value.
# nat64-prefix: 64:ff9b::0/96
# Enable UDP, "yes" or "no".
# do-udp: yes

View File

@ -2268,6 +2268,18 @@ List domain for which the AAAA records are ignored and the A record is
used by dns64 processing instead. Can be entered multiple times, list a
new domain for which it applies, one per line. Applies also to names
underneath the name given.
.SS "NAT64 Operation"
.LP
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<yes or no>\fR
Use NAT64 to reach IPv4-only servers. Default no.
.TP
.B nat64\-prefix: \fI<IPv6 prefix>\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.
.SS "DNSCrypt Options"
.LP
The

View File

@ -71,6 +71,11 @@
/** time when nameserver glue is said to be 'recent' */
#define SUSPICION_RECENT_EXPIRY 86400
/** if NAT64 is enabled and no NAT64 prefix is configured, first fall back to
* DNS64 prefix. If that is not configured, fall back to this default value.
*/
static const char DEFAULT_NAT64_PREFIX[] = "64:ff9b::/96";
/** fillup fetch policy array */
static void
fetch_fill(struct iter_env* ie, const char* str)
@ -142,6 +147,7 @@ caps_white_apply_cfg(rbtree_type* ntree, struct config_file* cfg)
int
iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
{
const char *nat64_prefix;
int i;
/* target fetch policy */
if(!read_fetch_policy(iter_env, cfg->target_fetch_policy))
@ -172,8 +178,34 @@ iter_apply_cfg(struct iter_env* iter_env, struct config_file* cfg)
}
}
nat64_prefix = cfg->nat64_prefix;
if (!nat64_prefix)
nat64_prefix = cfg->dns64_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)) {
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);
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",
nat64_prefix);
return 0;
}
iter_env->supports_ipv6 = cfg->do_ip6;
iter_env->supports_ipv4 = cfg->do_ip4;
iter_env->use_nat64 = cfg->do_nat64;
iter_env->outbound_msg_retry = cfg->outbound_msg_retry;
return 1;
}
@ -238,7 +270,8 @@ iter_filter_unsuitable(struct iter_env* iter_env, struct module_env* env,
if(!iter_env->supports_ipv6 && addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip6 available */
}
if(!iter_env->supports_ipv4 && !addr_is_ip6(&a->addr, a->addrlen)) {
if(!iter_env->supports_ipv4 && !iter_env->use_nat64 &&
!addr_is_ip6(&a->addr, a->addrlen)) {
return -1; /* there is no ip4 available */
}
/* check lameness - need zone , class info */
@ -745,10 +778,14 @@ 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)
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)
supports_ipv4 = 1;
/* check:
* o RD qflag is on.
* o no addresses are provided.

View File

@ -192,7 +192,8 @@ void iter_mark_pside_cycle_targets(struct module_qstate* qstate,
* @return true if dp is useless.
*/
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
struct delegpt* dp, int supports_ipv4, int supports_ipv6);
struct delegpt* dp, int supports_ipv4, int supports_ipv6,
int use_nat64);
/**
* See if qname has DNSSEC needs. This is true if there is a trust anchor above

View File

@ -255,7 +255,7 @@ error_supers(struct module_qstate* qstate, int id, struct module_qstate* super)
log_err("out of memory adding missing");
}
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || !ie->supports_ipv4) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* mark as failed */
target_count_increase_nx(super_iq, 1);
@ -1571,7 +1571,8 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq,
* same server reply) if useless-checked.
*/
if(iter_dp_is_useless(&qstate->qinfo, qstate->query_flags,
iq->dp, ie->supports_ipv4, ie->supports_ipv6)) {
iq->dp, ie->supports_ipv4, ie->supports_ipv6,
ie->use_nat64)) {
struct delegpt* retdp = NULL;
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) {
if(retdp) {
@ -2085,14 +2086,14 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
/* if this nameserver is at a delegation point, but that
* delegation point is a stub and we cannot go higher, skip*/
if( ((ie->supports_ipv6 && !ns->done_pside6) ||
(ie->supports_ipv4 && !ns->done_pside4)) &&
((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4)) &&
!can_have_last_resort(qstate->env, ns->name, ns->namelen,
iq->qchase.qclass, NULL)) {
log_nametypeclass(VERB_ALGO, "cannot pside lookup ns "
"because it is also a stub/forward,",
ns->name, LDNS_RR_TYPE_NS, iq->qchase.qclass);
if(ie->supports_ipv6) ns->done_pside6 = 1;
if(ie->supports_ipv4) ns->done_pside4 = 1;
if(ie->supports_ipv4 || ie->use_nat64) ns->done_pside4 = 1;
continue;
}
/* query for parent-side A and AAAA for nameservers */
@ -2117,7 +2118,7 @@ processLastResort(struct module_qstate* qstate, struct iter_qstate* iq,
return 0;
}
}
if(ie->supports_ipv4 && !ns->done_pside4) {
if((ie->supports_ipv4 || ie->use_nat64) && !ns->done_pside4) {
/* Send the A request. */
if(!generate_parentside_target_query(qstate, iq, id,
ns->name, ns->namelen,
@ -2384,7 +2385,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
}
if(!ie->supports_ipv6)
delegpt_no_ipv6(iq->dp);
if(!ie->supports_ipv4)
if(!ie->supports_ipv4 && !ie->use_nat64)
delegpt_no_ipv4(iq->dp);
delegpt_log(VERB_ALGO, iq->dp);
@ -2811,6 +2812,41 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
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;
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);
}
fptr_ok(fptr_whitelist_modenv_send_query(qstate->env->send_query));
outq = (*qstate->env->send_query)(&iq->qinfo_out,
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0),
@ -2821,7 +2857,7 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq,
!qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env,
&iq->qinfo_out)||target->attempts==1)?0:BIT_CD),
iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted(
ie, iq), sq_check_ratelimit, &target->addr, target->addrlen,
ie, iq), sq_check_ratelimit, &real_addr, real_addrlen,
iq->dp->name, iq->dp->namelen,
(iq->dp->tcp_upstream || qstate->env->cfg->tcp_upstream),
(iq->dp->ssl_upstream || qstate->env->cfg->ssl_upstream),
@ -3564,7 +3600,7 @@ processTargetResponse(struct module_qstate* qstate, int id,
} else {
verbose(VERB_ALGO, "iterator TargetResponse failed");
delegpt_mark_neg(dpns, qstate->qinfo.qtype);
if((dpns->got4 == 2 || !ie->supports_ipv4) &&
if((dpns->got4 == 2 || (!ie->supports_ipv4 && !ie->use_nat64)) &&
(dpns->got6 == 2 || !ie->supports_ipv6)) {
dpns->resolved = 1; /* fail the target */
/* do not count cached answers */

View File

@ -117,6 +117,18 @@ struct iter_env {
/** A flag to indicate whether or not we have an IPv4 route */
int supports_ipv4;
/** A flag to locally apply NAT64 to make IPv4 addrs into IPv6 */
int use_nat64;
/** NAT64 prefix address, cf. dns64_env->prefix_addr */
struct sockaddr_storage nat64_prefix_addr;
/** sizeof(sockaddr_in6) */
socklen_t nat64_prefix_addrlen;
/** CIDR mask length of NAT64 prefix */
int nat64_prefix_net;
/** A set of inetaddrs that should never be queried. */
struct iter_donotq* donotq;

View File

@ -1378,7 +1378,7 @@ struct delegpt* dns_cache_find_delegation(struct module_env* env,
struct regional* region, struct dns_msg** msg, uint32_t timenow,
int noexpiredabove, uint8_t* expiretop, size_t expiretoplen);
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
struct delegpt* dp, int supports_ipv4, int supports_ipv6);
struct delegpt* dp, int supports_ipv4, int supports_ipv6, int use_nat64);
struct iter_hints_stub* hints_lookup_stub(struct iter_hints* hints,
uint8_t* qname, uint16_t qclass, struct delegpt* dp);
@ -1409,7 +1409,8 @@ struct delegpt* find_delegation(struct module_qstate* qstate, char *nm, size_t n
if(!dp)
return NULL;
if(iter_dp_is_useless(&qinfo, BIT_RD, dp,
qstate->env->cfg->do_ip4, qstate->env->cfg->do_ip6)) {
qstate->env->cfg->do_ip4, qstate->env->cfg->do_ip6,
qstate->env->cfg->do_nat64)) {
if (dname_is_root((uint8_t*)nm))
return NULL;
nm = (char*)dp->name;

117
testdata/iter_nat64.rpl vendored Normal file
View File

@ -0,0 +1,117 @@
; config options
server:
do-nat64: yes
target-fetch-policy: "0 0 0 0 0"
stub-zone:
name: "."
stub-addr: 2001:db8::1
CONFIG_END
SCENARIO_BEGIN Test NAT64 transport for a v4-only server.
RANGE_BEGIN 0 100
ADDRESS 2001:db8::1
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN NS
SECTION ANSWER
. IN NS FAKE.ROOT.
SECTION ADDITIONAL
FAKE.ROOT. IN AAAA 2001:db8::1
ENTRY_END
ENTRY_BEGIN
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
v4only. IN NS
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
RANGE_END
; replies from NS over "NAT64"
RANGE_BEGIN 0 100
ADDRESS 64:ff9b::c000:0201
; A over NAT64
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
ns.v4only. IN A
SECTION ANSWER
ns.v4only. IN A 192.0.2.1
SECTION AUTHORITY
v4only. IN NS ns.v4only.
ENTRY_END
; no AAAA
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
ns.v4only. IN AAAA
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
v4only. IN NS
SECTION ANSWER
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
test.v4only. IN A
SECTION ANSWER
test.v4only. IN A 192.0.2.2
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test.v4only. IN A
ENTRY_END
STEP 20 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
SECTION QUESTION
test.v4only. IN A
SECTION ANSWER
test.v4only. IN A 192.0.2.2
ENTRY_END
SCENARIO_END

118
testdata/iter_nat64_prefix.rpl vendored Normal file
View File

@ -0,0 +1,118 @@
; config options
server:
do-nat64: yes
nat64-prefix: 2001:db8:1234::/96
target-fetch-policy: "0 0 0 0 0"
stub-zone:
name: "."
stub-addr: 2001:db8::1
CONFIG_END
SCENARIO_BEGIN Test NAT64 transport for a v4-only server, custom NAT64 prefix.
RANGE_BEGIN 0 100
ADDRESS 2001:db8::1
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN NS
SECTION ANSWER
. IN NS FAKE.ROOT.
SECTION ADDITIONAL
FAKE.ROOT. IN AAAA 2001:db8::1
ENTRY_END
ENTRY_BEGIN
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
v4only. IN NS
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
RANGE_END
; replies from NS over "NAT64"
RANGE_BEGIN 0 100
ADDRESS 2001:db8:1234::c000:0201
; A over NAT64
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
ns.v4only. IN A
SECTION ANSWER
ns.v4only. IN A 192.0.2.1
SECTION AUTHORITY
v4only. IN NS ns.v4only.
ENTRY_END
; no AAAA
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
ns.v4only. IN AAAA
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
v4only. IN NS
SECTION ANSWER
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
test.v4only. IN A
SECTION ANSWER
test.v4only. IN A 192.0.2.2
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test.v4only. IN A
ENTRY_END
STEP 20 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
SECTION QUESTION
test.v4only. IN A
SECTION ANSWER
test.v4only. IN A 192.0.2.2
ENTRY_END
SCENARIO_END

118
testdata/iter_nat64_prefix48.rpl vendored Normal file
View File

@ -0,0 +1,118 @@
; config options
server:
do-nat64: yes
nat64-prefix: 2001:db8:2345::/48
target-fetch-policy: "0 0 0 0 0"
stub-zone:
name: "."
stub-addr: 2001:db8::1
CONFIG_END
SCENARIO_BEGIN Test NAT64 transport, this time with /48 NAT64 prefix.
RANGE_BEGIN 0 100
ADDRESS 2001:db8::1
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY QR NOERROR
SECTION QUESTION
. IN NS
SECTION ANSWER
. IN NS FAKE.ROOT.
SECTION ADDITIONAL
FAKE.ROOT. IN AAAA 2001:db8::1
ENTRY_END
ENTRY_BEGIN
MATCH opcode subdomain
ADJUST copy_id copy_query
REPLY QR NOERROR
SECTION QUESTION
v4only. IN NS
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
RANGE_END
; replies from NS over "NAT64"
RANGE_BEGIN 0 100
ADDRESS 2001:db8:2345:c000:0002:0100::
; A over NAT64
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
ns.v4only. IN A
SECTION ANSWER
ns.v4only. IN A 192.0.2.1
SECTION AUTHORITY
v4only. IN NS ns.v4only.
ENTRY_END
; no AAAA
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
ns.v4only. IN AAAA
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
v4only. IN NS
SECTION ANSWER
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
ENTRY_BEGIN
MATCH opcode qtype qname
ADJUST copy_id
REPLY AA QR NOERROR
SECTION QUESTION
test.v4only. IN A
SECTION ANSWER
test.v4only. IN A 192.0.2.2
SECTION AUTHORITY
v4only. IN NS ns.v4only.
SECTION ADDITIONAL
ns.v4only. IN A 192.0.2.1
ENTRY_END
RANGE_END
STEP 1 QUERY
ENTRY_BEGIN
REPLY RD
SECTION QUESTION
test.v4only. IN A
ENTRY_END
STEP 20 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
REPLY QR RD RA NOERROR
SECTION QUESTION
test.v4only. IN A
SECTION ANSWER
test.v4only. IN A 192.0.2.2
ENTRY_END
SCENARIO_END

View File

@ -86,6 +86,8 @@ struct config_file {
int do_ip4;
/** do ip6 query support. */
int do_ip6;
/** do nat64 on queries */
int do_nat64;
/** prefer ip4 upstream queries. */
int prefer_ip4;
/** prefer ip6 upstream queries. */
@ -533,6 +535,9 @@ struct config_file {
/** ignore AAAAs for these domain names and use A record anyway */
struct config_strlist* dns64_ignore_aaaa;
/* NAT64 prefix; if unset defaults to dns64_prefix */
char* nat64_prefix;
/** true to enable dnstap support */
int dnstap;
/** using bidirectional frame streams if true */

View File

@ -227,6 +227,7 @@ outgoing-num-tcp{COLON} { YDVAR(1, VAR_OUTGOING_NUM_TCP) }
incoming-num-tcp{COLON} { YDVAR(1, VAR_INCOMING_NUM_TCP) }
do-ip4{COLON} { YDVAR(1, VAR_DO_IP4) }
do-ip6{COLON} { YDVAR(1, VAR_DO_IP6) }
do-nat64{COLON} { YDVAR(1, VAR_DO_NAT64) }
prefer-ip4{COLON} { YDVAR(1, VAR_PREFER_IP4) }
prefer-ip6{COLON} { YDVAR(1, VAR_PREFER_IP6) }
do-udp{COLON} { YDVAR(1, VAR_DO_UDP) }
@ -461,6 +462,7 @@ max-udp-size{COLON} { YDVAR(1, VAR_MAX_UDP_SIZE) }
dns64-prefix{COLON} { YDVAR(1, VAR_DNS64_PREFIX) }
dns64-synthall{COLON} { YDVAR(1, VAR_DNS64_SYNTHALL) }
dns64-ignore-aaaa{COLON} { YDVAR(1, VAR_DNS64_IGNORE_AAAA) }
nat64-prefix{COLON} { YDVAR(1, VAR_NAT64_PREFIX) }
define-tag{COLON} { YDVAR(1, VAR_DEFINE_TAG) }
local-zone-tag{COLON} { YDVAR(2, VAR_LOCAL_ZONE_TAG) }
access-control-tag{COLON} { YDVAR(2, VAR_ACCESS_CONTROL_TAG) }

View File

@ -73,7 +73,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_FORCE_TOPLEVEL
%token VAR_SERVER VAR_VERBOSITY VAR_NUM_THREADS VAR_PORT
%token VAR_OUTGOING_RANGE VAR_INTERFACE VAR_PREFER_IP4
%token VAR_DO_IP4 VAR_DO_IP6 VAR_PREFER_IP6 VAR_DO_UDP VAR_DO_TCP
%token VAR_DO_IP4 VAR_DO_IP6 VAR_DO_NAT64 VAR_PREFER_IP6 VAR_DO_UDP VAR_DO_TCP
%token VAR_TCP_MSS VAR_OUTGOING_TCP_MSS VAR_TCP_IDLE_TIMEOUT
%token VAR_EDNS_TCP_KEEPALIVE VAR_EDNS_TCP_KEEPALIVE_TIMEOUT
%token VAR_CHROOT VAR_USERNAME VAR_DIRECTORY VAR_LOGFILE VAR_PIDFILE
@ -123,6 +123,7 @@ extern struct config_parser_state* cfg_parser;
%token VAR_UNBLOCK_LAN_ZONES VAR_INSECURE_LAN_ZONES
%token VAR_INFRA_CACHE_MIN_RTT VAR_INFRA_CACHE_MAX_RTT VAR_INFRA_KEEP_PROBING
%token VAR_DNS64_PREFIX VAR_DNS64_SYNTHALL VAR_DNS64_IGNORE_AAAA
%token VAR_NAT64_PREFIX
%token VAR_DNSTAP VAR_DNSTAP_ENABLE VAR_DNSTAP_SOCKET_PATH VAR_DNSTAP_IP
%token VAR_DNSTAP_TLS VAR_DNSTAP_TLS_SERVER_NAME VAR_DNSTAP_TLS_CERT_BUNDLE
%token VAR_DNSTAP_TLS_CLIENT_KEY_FILE VAR_DNSTAP_TLS_CLIENT_CERT_FILE
@ -222,8 +223,8 @@ contents_server: contents_server content_server
| ;
content_server: server_num_threads | server_verbosity | server_port |
server_outgoing_range | server_do_ip4 |
server_do_ip6 | server_prefer_ip4 | server_prefer_ip6 |
server_do_udp | server_do_tcp |
server_do_ip6 | server_do_nat64 | server_prefer_ip4 |
server_prefer_ip6 | server_do_udp | server_do_tcp |
server_tcp_mss | server_outgoing_tcp_mss | server_tcp_idle_timeout |
server_tcp_keepalive | server_tcp_keepalive_timeout |
server_interface | server_chroot | server_username |
@ -273,6 +274,7 @@ content_server: server_num_threads | server_verbosity | server_port |
server_so_reuseport | server_delay_close | server_udp_connect |
server_unblock_lan_zones | server_insecure_lan_zones |
server_dns64_prefix | server_dns64_synthall | server_dns64_ignore_aaaa |
server_nat64_prefix |
server_infra_cache_min_rtt | server_infra_cache_max_rtt | server_harden_algo_downgrade |
server_ip_transparent | server_ip_ratelimit | server_ratelimit |
server_ip_dscp | server_infra_keep_probing |
@ -840,6 +842,15 @@ server_do_ip6: VAR_DO_IP6 STRING_ARG
free($2);
}
;
server_do_nat64: VAR_DO_NAT64 STRING_ARG
{
OUTYY(("P(server_do_nat64:%s)\n", $2));
if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0)
yyerror("expected yes or no.");
else cfg_parser->cfg->do_nat64 = (strcmp($2, "yes")==0);
free($2);
}
;
server_do_udp: VAR_DO_UDP STRING_ARG
{
OUTYY(("P(server_do_udp:%s)\n", $2));
@ -2323,6 +2334,13 @@ server_dns64_ignore_aaaa: VAR_DNS64_IGNORE_AAAA STRING_ARG
fatal_exit("out of memory adding dns64-ignore-aaaa");
}
;
server_nat64_prefix: VAR_NAT64_PREFIX STRING_ARG
{
OUTYY(("P(nat64_prefix:%s)\n", $2));
free(cfg_parser->cfg->nat64_prefix);
cfg_parser->cfg->nat64_prefix = $2;
}
;
server_define_tag: VAR_DEFINE_TAG STRING_ARG
{
char* p, *s = $2;