CIDR query routing (#1159)

* core: allow all CIDR ranges in zone specifications

Allow (e.g.) a v4 reverse on a /17. If a zone is specified in such a
way a FilterFunc is set in the config. This filter is checked against
incoming queries.

For all other queries this adds a 'x != nil' check which will not impact
performace too much. Benchmark function is added as well to check for
this as wel.

Add multiple tests in tests/server_reverse_test.go.

Benchmark shows in the non-reverse case this hardly impact the speed:

~~~
classless:
pkg: github.com/coredns/coredns/core/dnsserver
BenchmarkCoreServeDNS-4   	 1000000	      1431 ns/op	      16 B/op	       1 allocs/op

pkg: github.com/coredns/coredns/core/dnsserver
BenchmarkCoreServeDNS-4   	 1000000	      1429 ns/op	      16 B/op	       1 allocs/op

master:
pkg: github.com/coredns/coredns/core/dnsserver
BenchmarkCoreServeDNS-4   	 1000000	      1412 ns/op	      16 B/op	       1 allocs/op

pkg: github.com/coredns/coredns/core/dnsserver
BenchmarkCoreServeDNS-4   	 1000000	      1429 ns/op	      16 B/op	       1 allocs/op
~~~

* README.md updates
This commit is contained in:
Miek Gieben
2017-10-24 10:16:03 +01:00
committed by GitHub
parent 5f813bcc21
commit fcd0342e42
15 changed files with 269 additions and 120 deletions

View File

@@ -84,11 +84,10 @@ when resolving external pointing CNAMEs.
Reverse zones are supported. You need to make CoreDNS aware of the fact that you are also
authoritative for the reverse. For instance if you want to add the reverse for 10.0.0.0/24, you'll
need to add the zone `0.0.10.in-addr.arpa` to the list of zones. (The fun starts with IPv6 reverse zones
in the ip6.arpa domain.) Showing a snippet of a Corefile:
need to add the zone `0.0.10.in-addr.arpa` to the list of zones. Showing a snippet of a Corefile:
~~~
etcd skydns.local 0.0.10.in-addr.arpa {
etcd skydns.local 10.0.0.0/24 {
stubzones
...
~~~

View File

@@ -76,13 +76,12 @@ kubernetes [ZONES...] {
## Examples
Handle all queries in the `cluster.local` zone. Connect to Kubernetes in-cluster.
Also handle all `PTR` requests for `10.0.0.0/16` . Verify the existence of pods when answering pod
requests. Resolve upstream records against `10.102.3.10`. Note we show the entire server block
here:
Handle all queries in the `cluster.local` zone. Connect to Kubernetes in-cluster. Also handle all
`in-addr.arpa` `PTR` requests for `10.0.0.0/17` . Verify the existence of pods when answering pod
requests. Resolve upstream records against `10.102.3.10`. Note we show the entire server block here:
~~~ txt
10.0.0.0/16 cluster.local {
10.0.0.0/17 cluster.local {
kubernetes {
pods verified
upstream 10.102.3.10:53

View File

@@ -12,7 +12,7 @@ func TestKubernetesParseReverseZone(t *testing.T) {
expectedZones []string // expected count of defined zones.
}{
{`kubernetes coredns.local 10.0.0.0/16`, []string{"coredns.local.", "0.10.in-addr.arpa."}},
{`kubernetes coredns.local 10.0.0.0/17`, []string{"coredns.local.", "10.0.0.0/17."}},
{`kubernetes coredns.local 10.0.0.0/17`, []string{"coredns.local.", "0.10.in-addr.arpa."}},
}
for i, tc := range tests {

View File

@@ -75,13 +75,14 @@ func (h Host) Normalize() string {
// The error can be ignore here, because this function is called after the corefile
// has already been vetted.
host, _, _ := SplitHostPort(s)
host, _, _, _ := SplitHostPort(s)
return Name(host).Normalize()
}
// SplitHostPort splits s up in a host and port portion, taking reverse address notation into account.
// String the string s should *not* be prefixed with any protocols, i.e. dns://
func SplitHostPort(s string) (host, port string, err error) {
// String the string s should *not* be prefixed with any protocols, i.e. dns://. The returned ipnet is the
// *net.IPNet that is used when the zone is a reverse and a netmask is given.
func SplitHostPort(s string) (host, port string, ipnet *net.IPNet, err error) {
// If there is: :[0-9]+ on the end we assume this is the port. This works for (ascii) domain
// names and our reverse syntax, which always needs a /mask *before* the port.
// So from the back, find first colon, and then check if its a number.
@@ -89,7 +90,7 @@ func SplitHostPort(s string) (host, port string, err error) {
colon := strings.LastIndex(s, ":")
if colon == len(s)-1 {
return "", "", fmt.Errorf("expecting data after last colon: %q", s)
return "", "", nil, fmt.Errorf("expecting data after last colon: %q", s)
}
if colon != -1 {
if p, err := strconv.Atoi(s[colon+1:]); err == nil {
@@ -100,33 +101,34 @@ func SplitHostPort(s string) (host, port string, err error) {
// TODO(miek): this should take escaping into account.
if len(host) > 255 {
return "", "", fmt.Errorf("specified zone is too long: %d > 255", len(host))
return "", "", nil, fmt.Errorf("specified zone is too long: %d > 255", len(host))
}
_, d := dns.IsDomainName(host)
if !d {
return "", "", fmt.Errorf("zone is not a valid domain name: %s", host)
return "", "", nil, fmt.Errorf("zone is not a valid domain name: %s", host)
}
// Check if it parses as a reverse zone, if so we use that. Must be fully
// specified IP and mask and mask % 8 = 0.
ip, net, err := net.ParseCIDR(host)
// Check if it parses as a reverse zone, if so we use that. Must be fully specified IP and mask.
ip, n, err := net.ParseCIDR(host)
ones, bits := 0, 0
if err == nil {
if rev, e := dns.ReverseAddr(ip.String()); e == nil {
ones, bits := net.Mask.Size()
if (bits-ones)%8 == 0 {
offset, end := 0, false
for i := 0; i < (bits-ones)/8; i++ {
offset, end = dns.NextLabel(rev, offset)
if end {
break
}
ones, bits = n.Mask.Size()
// Get the first lower octet boundary to see what encompassing zone we should be authoritative for.
mod := (bits - ones) % 8
nearest := (bits - ones) + mod
offset, end := 0, false
for i := 0; i < nearest/8; i++ {
offset, end = dns.NextLabel(rev, offset)
if end {
break
}
host = rev[offset:]
}
host = rev[offset:]
}
}
return host, port, nil
return host, port, n, nil
}
// Duplicated from core/dnsserver/address.go !

View File

@@ -70,7 +70,7 @@ func TestNameNormalize(t *testing.T) {
func TestHostNormalize(t *testing.T) {
hosts := []string{".:53", ".", "example.org:53", "example.org.", "example.org.:53", "example.org.",
"10.0.0.0/8:53", "10.in-addr.arpa.", "10.0.0.0/9", "10.0.0.0/9.",
"10.0.0.0/8:53", "10.in-addr.arpa.", "10.0.0.0/9", "10.in-addr.arpa.",
"dns://example.org", "example.org."}
for i := 0; i < len(hosts); i += 2 {
@@ -82,3 +82,31 @@ func TestHostNormalize(t *testing.T) {
}
}
}
func TestSplitHostPortReverse(t *testing.T) {
tests := map[string]int{
"example.org.": 0,
"10.0.0.0/9": 32 - 9,
"10.0.0.0/8": 32 - 8,
"10.0.0.0/17": 32 - 17,
"10.0.0.0/0": 32 - 0,
"10.0.0.0/64": 0,
"10.0.0.0": 0,
"10.0.0": 0,
"2003::1/65": 128 - 65,
}
for in, expect := range tests {
_, _, n, err := SplitHostPort(in)
if err != nil {
t.Errorf("Expected no error, got %q for %s", in, err)
}
if n == nil {
continue
}
ones, bits := n.Mask.Size()
got := bits - ones
if got != expect {
t.Errorf("Expected %d, got %d for %s", expect, got, in)
}
}
}

View File

@@ -9,7 +9,7 @@ import (
// into an IP address. This works for ipv4 or ipv6.
//
// 54.119.58.176.in-addr.arpa. becomes 176.58.119.54. If the conversion
// failes the empty string is returned.
// fails the empty string is returned.
func ExtractAddressFromReverse(reverseName string) string {
search := ""