esc
move open

DNS64

Synthesising AAAA records so IPv6-only clients can reach IPv4-only services.

[dns64]
enabled  = true
prefixes = ["64:ff9b::/96"]

An IPv6-only client asks for a AAAA record; the service only has an A record. DNS64 (RFC 6147) synthesises a AAAA by embedding the IPv4 address inside a Pref64 prefix, which a NAT64 gateway then translates. Off by default.

When synthesis happens

When a client’s AAAA query returns NOERROR with no data — or any nonzero RCODE other than NXDOMAIN — sdns issues an A query for the same name and synthesises one AAAA per (A record, prefix) pair.

Three cases deliberately pass through untouched:

  • NXDOMAIN. The name does not exist. Synthesising anything would invent it.
  • SERVFAIL carrying a DNSSEC-failure Extended DNS Error. DNS64 must never mask a validation failure (RFC 6147 §5.5), so a signed name that failed to validate stays failed.
  • Clients that set RD=0 or CD=1. Both say “do not do anything clever on my behalf”, and DNS64 is the definition of clever.

Prefixes

prefixes = ["64:ff9b::/96"]

Lengths must be one of /32, /40, /48, /56, /64 or /96 (RFC 6052). List several to synthesise one AAAA per prefix, so a client sees every reachable NAT64 path in a single reply.

64:ff9b::/96 is the IANA Well-Known Prefix and the usual choice. If DNS64 is enabled with no prefixes configured, that is the runtime default.

Scoping

client_networks = ["2001:db8:1::/48"]
exclude_zones   = ["example.com."]

client_networks limits synthesis to given client CIDRs; empty means every client. Restrict it to your IPv6-only subnets so dual-stack clients keep their original answers.

exclude_zones names zones that must never be synthesised. The match is by suffix: "example.com." covers the zone and everything under it.

Address exclusions

exclude_aaaa_networks = ["::ffff:0:0/96"]
exclude_a_networks    = ["10.0.0.0/8", "192.168.0.0/16", ...]

exclude_aaaa_networks filters AAAA records out of the upstream response before deciding between pass-through and synthesis (RFC 6147 §5.1.4). The default is the IPv4-mapped range: an upstream that wrongly returns ::ffff:... AAAAs is treated as having returned no AAAA at all, so sdns synthesises a routable address from the corresponding A instead of handing the client something unusable.

exclude_a_networks lists IPv4 networks not to synthesise from when the Well-Known Prefix is active — RFC 6147 forbids embedding non-global addresses in it. The shipped defaults mirror the IANA Special-Purpose Address Registry. Operator-chosen network-specific prefixes ignore this list, since the constraint is specific to the Well-Known Prefix.