mirror of
https://github.com/NLnetLabs/unbound.git
synced 2025-12-29 19:09:36 -05:00
Merge branch 'eqvinox-nat64'
This commit is contained in:
commit
20184483df
20 changed files with 592 additions and 30 deletions
|
|
@ -860,7 +860,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 "
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -243,6 +243,18 @@ 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.
|
||||
# Consider also enabling prefer-ip6 to prefer native IPv6 connections
|
||||
# to nameservers.
|
||||
# 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
|
||||
|
||||
|
|
|
|||
|
|
@ -2312,6 +2312,21 @@ 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.
|
||||
Consider also enabling \fBprefer\-ip6\fR to prefer native IPv6 connections to
|
||||
nameservers.
|
||||
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. The prefix length must be one of /32, /40, /48, /56, /64 or /96.
|
||||
.SS "DNSCrypt Options"
|
||||
.LP
|
||||
The
|
||||
|
|
|
|||
|
|
@ -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,32 @@ 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(!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;
|
||||
}
|
||||
|
||||
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;
|
||||
iter_env->max_sent_count = cfg->max_sent_count;
|
||||
iter_env->max_query_restarts = cfg->max_query_restarts;
|
||||
|
|
@ -240,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 */
|
||||
|
|
@ -747,10 +778,15 @@ 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.
|
||||
|
|
|
|||
|
|
@ -189,10 +189,13 @@ 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,
|
||||
struct delegpt* dp, int supports_ipv4, int supports_ipv6);
|
||||
int iter_dp_is_useless(struct query_info* qinfo, uint16_t qflags,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -1557,18 +1557,19 @@ 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,
|
||||
iq->dp, ie->supports_ipv4, ie->supports_ipv6)) {
|
||||
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;
|
||||
if(!can_have_last_resort(qstate->env, iq->dp->name, iq->dp->namelen, iq->qchase.qclass, &retdp)) {
|
||||
if(retdp) {
|
||||
|
|
@ -1929,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,
|
||||
|
|
@ -2082,14 +2083,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 */
|
||||
|
|
@ -2114,7 +2115,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,
|
||||
|
|
@ -2256,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;
|
||||
|
|
@ -2381,7 +2384,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);
|
||||
|
||||
|
|
@ -2802,12 +2805,24 @@ 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": "");
|
||||
}
|
||||
|
||||
real_addr = target->addr;
|
||||
real_addrlen = target->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));
|
||||
outq = (*qstate->env->send_query)(&iq->qinfo_out,
|
||||
iq->chase_flags | (iq->chase_to_rd?BIT_RD:0),
|
||||
|
|
@ -2818,7 +2833,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),
|
||||
|
|
@ -2835,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);
|
||||
|
|
@ -3566,7 +3581,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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
@ -112,6 +112,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
117
testdata/iter_nat64.rpl
vendored
Normal file
117
testdata/iter_nat64.rpl
vendored
Normal 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
|
||||
119
testdata/iter_nat64_prefix.rpl
vendored
Normal file
119
testdata/iter_nat64_prefix.rpl
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
; config options
|
||||
server:
|
||||
do-nat64: yes
|
||||
nat64-prefix: 2001:db8:1234::/96
|
||||
target-fetch-policy: "0 0 0 0 0"
|
||||
do-ip4: no
|
||||
|
||||
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
118
testdata/iter_nat64_prefix48.rpl
vendored
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -88,6 +88,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. */
|
||||
|
|
@ -540,6 +542,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 */
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
@ -464,6 +465,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) }
|
||||
|
|
|
|||
|
|
@ -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_SOCK_QUEUE_TIMEOUT
|
||||
|
|
@ -124,6 +124,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
|
||||
|
|
@ -224,8 +225,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_sock_queue_timeout |
|
||||
|
|
@ -276,6 +277,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 |
|
||||
|
|
@ -854,6 +856,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));
|
||||
|
|
@ -2361,6 +2372,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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue