plugin/dns64 : add support for DNS requests over IPv4 network (#4809)

This commit is contained in:
Ondřej Benkovský 2022-01-07 17:16:23 +01:00 committed by GitHub
parent 39a99a5bbe
commit b546031f9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 106 additions and 9 deletions

View file

@ -27,11 +27,13 @@ Or use this slightly longer form with more options:
dns64 [PREFIX] {
[translate_all]
prefix PREFIX
[allow_ipv4]
}
~~~
* `prefix` specifies any local IPv6 prefix to use, instead of the well known prefix (64:ff9b::/96)
* `translate_all` translates all queries, including responses that have AAAA results.
* `allow_ipv4` Allow translating queries if they come in over IPv4, default is IPv6 only translation.
## Examples
@ -70,6 +72,19 @@ Enable translation even if an existing AAAA record is present.
}
~~~
Apply translation even to the requests which arrived over IPv4 network. Warning, the `allow_ipv4` feature will apply
translations to requests coming from dual-stack clients. This means that a request for a client that sends an `AAAA`
that would normal result in an `NXDOMAIN` would get a translated result.
This may cause unwanted IPv6 dns64 traffic when a dualstack client would normally use the result of an `A` record request.
~~~ corefile
. {
dns64 {
allow_ipv4
}
}
~~~
## Metrics
If monitoring is enabled (via the _prometheus_ plugin) then the following metrics are exported:

View file

@ -28,13 +28,14 @@ type DNS64 struct {
Next plugin.Handler
Prefix *net.IPNet
TranslateAll bool // Not comply with 5.1.1
AllowIPv4 bool
Upstream UpstreamInt
}
// ServeDNS implements the plugin.Handler interface.
func (d *DNS64) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
// Don't proxy if we don't need to.
if !requestShouldIntercept(&request.Request{W: w, Req: r}) {
if !d.requestShouldIntercept(&request.Request{W: w, Req: r}) {
return d.Next.ServeDNS(ctx, w, r)
}
@ -69,13 +70,13 @@ func (d *DNS64) Name() string { return "dns64" }
// requestShouldIntercept returns true if the request represents one that is eligible
// for DNS64 rewriting:
// 1. The request came in over IPv6 (not in RFC)
// 1. The request came in over IPv6 or the 'allow_ipv4' option is set
// 2. The request is of type AAAA
// 3. The request is of class INET
func requestShouldIntercept(req *request.Request) bool {
// Only intercept with this when the request came in over IPv6. This is not mentioned in the RFC.
// File an issue if you think we should translate even requests made using IPv4, or have a configuration flag
if req.Family() == 1 { // If it came in over v4, don't do anything.
func (d *DNS64) requestShouldIntercept(req *request.Request) bool {
// Make sure that request came in over IPv4 unless AllowIPv4 option is enabled.
// Translating requests without taking into consideration client (source) IP might be problematic in dual-stack networks.
if !d.AllowIPv4 && req.Family() == 1 {
return false
}

View file

@ -21,6 +21,58 @@ func To6(prefix, address string) (net.IP, error) {
return to6(pref, addr)
}
func TestRequestShouldIntercept(t *testing.T) {
tests := []struct {
name string
allowIpv4 bool
remoteIP string
msg *dns.Msg
want bool
}{
{
name: "should intercept request from IPv6 network - AAAA - IN",
allowIpv4: true,
remoteIP: "::1",
msg: new(dns.Msg).SetQuestion("example.com", dns.TypeAAAA),
want: true,
},
{
name: "should intercept request from IPv4 network - AAAA - IN",
allowIpv4: true,
remoteIP: "127.0.0.1",
msg: new(dns.Msg).SetQuestion("example.com", dns.TypeAAAA),
want: true,
},
{
name: "should not intercept request from IPv4 network - AAAA - IN",
allowIpv4: false,
remoteIP: "127.0.0.1",
msg: new(dns.Msg).SetQuestion("example.com", dns.TypeAAAA),
want: false,
},
{
name: "should not intercept request from IPv6 network - A - IN",
allowIpv4: false,
remoteIP: "::1",
msg: new(dns.Msg).SetQuestion("example.com", dns.TypeA),
want: false,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
h := DNS64{AllowIPv4: tc.allowIpv4}
rec := dnstest.NewRecorder(&test.ResponseWriter{RemoteIP: tc.remoteIP})
r := request.Request{W: rec, Req: tc.msg}
actual := h.requestShouldIntercept(&r)
if actual != tc.want {
t.Fatalf("Expected %v, but got %v", tc.want, actual)
}
})
}
}
func TestTo6(t *testing.T) {
v6, err := To6("64:ff9b::/96", "64.64.64.64")

View file

@ -66,6 +66,8 @@ func dns64Parse(c *caddy.Controller) (*DNS64, error) {
dns64.Prefix = pref
case "translate_all":
dns64.TranslateAll = true
case "allow_ipv4":
dns64.AllowIPv4 = true
default:
return nil, c.Errf("unknown property '%s'", c.Val())
}

View file

@ -10,17 +10,20 @@ func TestSetupDns64(t *testing.T) {
tests := []struct {
inputUpstreams string
shouldErr bool
prefix string
wantPrefix string
wantAllowIpv4 bool
}{
{
`dns64`,
false,
"64:ff9b::/96",
false,
},
{
`dns64 64:dead::/96`,
false,
"64:dead::/96",
false,
},
{
`dns64 {
@ -28,11 +31,13 @@ func TestSetupDns64(t *testing.T) {
}`,
false,
"64:ff9b::/96",
false,
},
{
`dns64`,
false,
"64:ff9b::/96",
false,
},
{
`dns64 {
@ -40,6 +45,7 @@ func TestSetupDns64(t *testing.T) {
}`,
false,
"64:ff9b::/96",
false,
},
{
`dns64 {
@ -47,6 +53,7 @@ func TestSetupDns64(t *testing.T) {
}`,
false,
"64:ff9b::/32",
false,
},
{
`dns64 {
@ -54,6 +61,7 @@ func TestSetupDns64(t *testing.T) {
}`,
true,
"64:ff9b::/52",
false,
},
{
`dns64 {
@ -61,6 +69,7 @@ func TestSetupDns64(t *testing.T) {
}`,
true,
"64:ff9b::/104",
false,
},
{
`dns64 {
@ -68,6 +77,7 @@ func TestSetupDns64(t *testing.T) {
}`,
true,
"8.8.9.9/24",
false,
},
{
`dns64 {
@ -75,6 +85,7 @@ func TestSetupDns64(t *testing.T) {
}`,
false,
"64:ff9b::/96",
false,
},
{
`dns64 {
@ -82,6 +93,7 @@ func TestSetupDns64(t *testing.T) {
}`,
false,
"2002:ac12:b083::/96",
false,
},
{
`dns64 {
@ -89,6 +101,7 @@ func TestSetupDns64(t *testing.T) {
}`,
false,
"2002:c0a8:a88a::/48",
false,
},
{
`dns64 foobar {
@ -96,11 +109,13 @@ func TestSetupDns64(t *testing.T) {
}`,
true,
"64:ff9b::/96",
false,
},
{
`dns64 foobar`,
true,
"64:ff9b::/96",
false,
},
{
`dns64 {
@ -108,6 +123,15 @@ func TestSetupDns64(t *testing.T) {
}`,
true,
"64:ff9b::/96",
false,
},
{
`dns64 {
allow_ipv4
}`,
false,
"64:ff9b::/96",
true,
},
}
@ -118,8 +142,11 @@ func TestSetupDns64(t *testing.T) {
t.Errorf("Test %d expected %v error, got %v for %s", i+1, test.shouldErr, err, test.inputUpstreams)
}
if err == nil {
if dns64.Prefix.String() != test.prefix {
t.Errorf("Test %d expected prefix %s, got %v", i+1, test.prefix, dns64.Prefix.String())
if dns64.Prefix.String() != test.wantPrefix {
t.Errorf("Test %d expected prefix %s, got %v", i+1, test.wantPrefix, dns64.Prefix.String())
}
if dns64.AllowIPv4 != test.wantAllowIpv4 {
t.Errorf("Test %d expected prefix %v, got %v", i+1, test.wantAllowIpv4, dns64.AllowIPv4)
}
}
}