mirror of
https://github.com/juanfont/headscale.git
synced 2024-12-20 19:09:07 +01:00
dns: IPv6 roots generation
This commit is contained in:
parent
1a6e5d8770
commit
115d0cbe85
69
dns.go
69
dns.go
@ -14,6 +14,11 @@ const (
|
|||||||
ByteSize = 8
|
ByteSize = 8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ipv4AddressLength = 32
|
||||||
|
ipv6AddressLength = 128
|
||||||
|
)
|
||||||
|
|
||||||
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
|
// generateMagicDNSRootDomains generates a list of DNS entries to be included in `Routes` in `MapResponse`.
|
||||||
// This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS
|
// This list of reverse DNS entries instructs the OS on what subnets and domains the Tailscale embedded DNS
|
||||||
// server (listening in 100.100.100.100 udp/53) should be used for.
|
// server (listening in 100.100.100.100 udp/53) should be used for.
|
||||||
@ -37,22 +42,25 @@ const (
|
|||||||
func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN {
|
func generateMagicDNSRootDomains(ipPrefixes []netaddr.IPPrefix) []dnsname.FQDN {
|
||||||
fqdns := make([]dnsname.FQDN, 0, len(ipPrefixes))
|
fqdns := make([]dnsname.FQDN, 0, len(ipPrefixes))
|
||||||
for _, ipPrefix := range ipPrefixes {
|
for _, ipPrefix := range ipPrefixes {
|
||||||
var generateDnsRoot func(netaddr.IPPrefix) []dnsname.FQDN
|
var generateDNSRoot func(netaddr.IPPrefix) []dnsname.FQDN
|
||||||
switch ipPrefix.IP().BitLen() {
|
switch ipPrefix.IP().BitLen() {
|
||||||
case 32:
|
case ipv4AddressLength:
|
||||||
generateDnsRoot = generateIPv4DNSRootDomain
|
generateDNSRoot = generateIPv4DNSRootDomain
|
||||||
|
|
||||||
|
case ipv6AddressLength:
|
||||||
|
generateDNSRoot = generateIPv6DNSRootDomain
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unsupported IP version with address length %d", ipPrefix.IP().BitLen()))
|
panic(fmt.Sprintf("unsupported IP version with address length %d", ipPrefix.IP().BitLen()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fqdns = append(fqdns, generateDnsRoot(ipPrefix)...)
|
fqdns = append(fqdns, generateDNSRoot(ipPrefix)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fqdns
|
return fqdns
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) (fqdns []dnsname.FQDN) {
|
func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
|
||||||
// Conversion to the std lib net.IPnet, a bit easier to operate
|
// Conversion to the std lib net.IPnet, a bit easier to operate
|
||||||
netRange := ipPrefix.IPNet()
|
netRange := ipPrefix.IPNet()
|
||||||
maskBits, _ := netRange.Mask.Size()
|
maskBits, _ := netRange.Mask.Size()
|
||||||
@ -76,6 +84,7 @@ func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) (fqdns []dnsname.FQDN)
|
|||||||
rdnsSlice = append(rdnsSlice, "in-addr.arpa.")
|
rdnsSlice = append(rdnsSlice, "in-addr.arpa.")
|
||||||
rdnsBase := strings.Join(rdnsSlice, ".")
|
rdnsBase := strings.Join(rdnsSlice, ".")
|
||||||
|
|
||||||
|
fqdns := make([]dnsname.FQDN, 0, max-min+1)
|
||||||
for i := min; i <= max; i++ {
|
for i := min; i <= max; i++ {
|
||||||
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%d.%s", i, rdnsBase))
|
fqdn, err := dnsname.ToFQDN(fmt.Sprintf("%d.%s", i, rdnsBase))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -84,7 +93,55 @@ func generateIPv4DNSRootDomain(ipPrefix netaddr.IPPrefix) (fqdns []dnsname.FQDN)
|
|||||||
fqdns = append(fqdns, fqdn)
|
fqdns = append(fqdns, fqdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return fqdns
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIPv6DNSRootDomain(ipPrefix netaddr.IPPrefix) []dnsname.FQDN {
|
||||||
|
const nibbleLen = 4
|
||||||
|
|
||||||
|
maskBits, _ := ipPrefix.IPNet().Mask.Size()
|
||||||
|
expanded := ipPrefix.IP().StringExpanded()
|
||||||
|
nibbleStr := strings.Map(func(r rune) rune {
|
||||||
|
if r == ':' {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}, expanded)
|
||||||
|
|
||||||
|
// TODO?: that does not look the most efficient implementation,
|
||||||
|
// but the inputs are not so long as to cause problems,
|
||||||
|
// and from what I can see, the generateMagicDNSRootDomains
|
||||||
|
// function is called only once over the lifetime of a server process.
|
||||||
|
prefixConstantParts := []string{}
|
||||||
|
for i := 0; i < maskBits/nibbleLen; i++ {
|
||||||
|
prefixConstantParts = append([]string{string(nibbleStr[i])}, prefixConstantParts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
makeDomain := func(variablePrefix ...string) (dnsname.FQDN, error) {
|
||||||
|
prefix := strings.Join(append(variablePrefix, prefixConstantParts...), ".")
|
||||||
|
|
||||||
|
return dnsname.ToFQDN(fmt.Sprintf("%s.ip6.arpa", prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
var fqdns []dnsname.FQDN
|
||||||
|
if maskBits%4 == 0 {
|
||||||
|
dom, _ := makeDomain()
|
||||||
|
fqdns = append(fqdns, dom)
|
||||||
|
} else {
|
||||||
|
domCount := 1 << (maskBits % nibbleLen)
|
||||||
|
fqdns = make([]dnsname.FQDN, 0, domCount)
|
||||||
|
for i := 0; i < domCount; i++ {
|
||||||
|
varNibble := fmt.Sprintf("%x", i)
|
||||||
|
dom, err := makeDomain(varNibble)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fqdns = append(fqdns, dom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fqdns
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMapResponseDNSConfig(
|
func getMapResponseDNSConfig(
|
||||||
|
46
dns_test.go
46
dns_test.go
@ -10,8 +10,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
|
func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
|
||||||
prefix := netaddr.MustParseIPPrefix("100.64.0.0/10")
|
prefixes := []netaddr.IPPrefix{
|
||||||
domains := generateMagicDNSRootDomains(prefix)
|
netaddr.MustParseIPPrefix("100.64.0.0/10"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
@ -45,8 +47,10 @@ func (s *Suite) TestMagicDNSRootDomains100(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
|
func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
|
||||||
prefix := netaddr.MustParseIPPrefix("172.16.0.0/16")
|
prefixes := []netaddr.IPPrefix{
|
||||||
domains := generateMagicDNSRootDomains(prefix)
|
netaddr.MustParseIPPrefix("172.16.0.0/16"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, domain := range domains {
|
for _, domain := range domains {
|
||||||
@ -69,6 +73,40 @@ func (s *Suite) TestMagicDNSRootDomains172(c *check.C) {
|
|||||||
c.Assert(found, check.Equals, true)
|
c.Assert(found, check.Equals, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Happens when netmask is a multiple of 4 bits (sounds likely).
|
||||||
|
func (s *Suite) TestMagicDNSRootDomainsIPv6Single(c *check.C) {
|
||||||
|
prefixes := []netaddr.IPPrefix{
|
||||||
|
netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
|
c.Assert(len(domains), check.Equals, 1)
|
||||||
|
c.Assert(domains[0].WithTrailingDot(), check.Equals, "0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestMagicDNSRootDomainsIPv6SingleMultiple(c *check.C) {
|
||||||
|
prefixes := []netaddr.IPPrefix{
|
||||||
|
netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/50"),
|
||||||
|
}
|
||||||
|
domains := generateMagicDNSRootDomains(prefixes)
|
||||||
|
|
||||||
|
yieldsRoot := func(dom string) bool {
|
||||||
|
for _, candidate := range domains {
|
||||||
|
if candidate.WithTrailingDot() == dom {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Assert(len(domains), check.Equals, 4)
|
||||||
|
c.Assert(yieldsRoot("0.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
c.Assert(yieldsRoot("1.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
c.Assert(yieldsRoot("2.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
c.Assert(yieldsRoot("3.0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa."), check.Equals, true)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
|
||||||
namespaceShared1, err := app.CreateNamespace("shared1")
|
namespaceShared1, err := app.CreateNamespace("shared1")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
|
Loading…
Reference in New Issue
Block a user