mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 13:05:46 +00:00
ipnlocal, net/{dns,tsaddr,tstun}, wgengine: support MagicDNS on IPv6
Fixes #3660 RELNOTE=MagicDNS now works over IPv6 when CGNAT IPv4 is disabled. Change-Id: I001e983df5feeb65289abe5012dedd177b841b45 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
e2d9c99e5b
commit
506c727e30
@ -111,7 +111,8 @@ func TestDNSConfigForNetmap(t *testing.T) {
|
|||||||
},
|
},
|
||||||
prefs: &ipn.Prefs{},
|
prefs: &ipn.Prefs{},
|
||||||
want: &dns.Config{
|
want: &dns.Config{
|
||||||
Routes: map[dnsname.FQDN][]dnstype.Resolver{},
|
OnlyIPv6: true,
|
||||||
|
Routes: map[dnsname.FQDN][]dnstype.Resolver{},
|
||||||
Hosts: map[dnsname.FQDN][]netaddr.IP{
|
Hosts: map[dnsname.FQDN][]netaddr.IP{
|
||||||
"b.net.": ips("fe75::2"),
|
"b.net.": ips("fe75::2"),
|
||||||
"myname.net.": ips("fe75::1"),
|
"myname.net.": ips("fe75::1"),
|
||||||
|
@ -1951,6 +1951,7 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Log
|
|||||||
// selfV6Only is whether we only have IPv6 addresses ourselves.
|
// selfV6Only is whether we only have IPv6 addresses ourselves.
|
||||||
selfV6Only := tsaddr.PrefixesContainsFunc(nm.Addresses, tsaddr.PrefixIs6) &&
|
selfV6Only := tsaddr.PrefixesContainsFunc(nm.Addresses, tsaddr.PrefixIs6) &&
|
||||||
!tsaddr.PrefixesContainsFunc(nm.Addresses, tsaddr.PrefixIs4)
|
!tsaddr.PrefixesContainsFunc(nm.Addresses, tsaddr.PrefixIs4)
|
||||||
|
dcfg.OnlyIPv6 = selfV6Only
|
||||||
|
|
||||||
// Populate MagicDNS records. We do this unconditionally so that
|
// Populate MagicDNS records. We do this unconditionally so that
|
||||||
// quad-100 can always respond to MagicDNS queries, even if the OS
|
// quad-100 can always respond to MagicDNS queries, even if the OS
|
||||||
@ -2382,7 +2383,9 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs) *router
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rs.Routes = append(rs.Routes, netaddr.IPPrefixFrom(tsaddr.TailscaleServiceIP(), 32))
|
if tsaddr.PrefixesContainsFunc(rs.LocalAddrs, tsaddr.PrefixIs4) {
|
||||||
|
rs.Routes = append(rs.Routes, netaddr.IPPrefixFrom(tsaddr.TailscaleServiceIP(), 32))
|
||||||
|
}
|
||||||
|
|
||||||
return rs
|
return rs
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/net/dns/resolver"
|
"tailscale.com/net/dns/resolver"
|
||||||
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
)
|
)
|
||||||
@ -40,6 +41,16 @@ type Config struct {
|
|||||||
// it to resolve, you also need to add appropriate routes to
|
// it to resolve, you also need to add appropriate routes to
|
||||||
// Routes.
|
// Routes.
|
||||||
Hosts map[dnsname.FQDN][]netaddr.IP
|
Hosts map[dnsname.FQDN][]netaddr.IP
|
||||||
|
// OnlyIPv6, if true, uses the IPv6 service IP (for MagicDNS)
|
||||||
|
// instead of the IPv4 version (100.100.100.100).
|
||||||
|
OnlyIPv6 bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) serviceIP() netaddr.IP {
|
||||||
|
if c.OnlyIPv6 {
|
||||||
|
return tsaddr.TailscaleServiceIPv6()
|
||||||
|
}
|
||||||
|
return tsaddr.TailscaleServiceIP()
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteToBufioWriter write a debug version of c for logs to w, omitting
|
// WriteToBufioWriter write a debug version of c for logs to w, omitting
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/net/dns/resolver"
|
"tailscale.com/net/dns/resolver"
|
||||||
"tailscale.com/net/tsaddr"
|
|
||||||
"tailscale.com/net/tsdial"
|
"tailscale.com/net/tsdial"
|
||||||
"tailscale.com/types/dnstype"
|
"tailscale.com/types/dnstype"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
@ -122,7 +121,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
|
|||||||
// through quad-100.
|
// through quad-100.
|
||||||
rcfg.Routes = routes
|
rcfg.Routes = routes
|
||||||
rcfg.Routes["."] = cfg.DefaultResolvers
|
rcfg.Routes["."] = cfg.DefaultResolvers
|
||||||
ocfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()}
|
ocfg.Nameservers = []netaddr.IP{cfg.serviceIP()}
|
||||||
return rcfg, ocfg, nil
|
return rcfg, ocfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +158,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
|
|||||||
// or routes + MagicDNS, or just MagicDNS, or on an OS that cannot
|
// or routes + MagicDNS, or just MagicDNS, or on an OS that cannot
|
||||||
// split-DNS. Install a split config pointing at quad-100.
|
// split-DNS. Install a split config pointing at quad-100.
|
||||||
rcfg.Routes = routes
|
rcfg.Routes = routes
|
||||||
ocfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()}
|
ocfg.Nameservers = []netaddr.IP{cfg.serviceIP()}
|
||||||
|
|
||||||
// If the OS can't do native split-dns, read out the underlying
|
// If the OS can't do native split-dns, read out the underlying
|
||||||
// resolver config and blend it into our config.
|
// resolver config and blend it into our config.
|
||||||
|
@ -377,7 +377,7 @@ func (r *Resolver) HandleExitNodeDNSQuery(ctx context.Context, q []byte, from ne
|
|||||||
// TODO: more than 1 resolver from /etc/resolv.conf?
|
// TODO: more than 1 resolver from /etc/resolv.conf?
|
||||||
|
|
||||||
var resolvers []resolverAndDelay
|
var resolvers []resolverAndDelay
|
||||||
if nameserver == tsaddr.TailscaleServiceIP() {
|
if nameserver == tsaddr.TailscaleServiceIP() || nameserver == tsaddr.TailscaleServiceIPv6() {
|
||||||
// If resolv.conf says 100.100.100.100, it's coming right back to us anyway
|
// If resolv.conf says 100.100.100.100, it's coming right back to us anyway
|
||||||
// so avoid the loop through the kernel and just do what we
|
// so avoid the loop through the kernel and just do what we
|
||||||
// would've done anyway. By not passing any resolvers, the forwarder
|
// would've done anyway. By not passing any resolvers, the forwarder
|
||||||
|
@ -36,14 +36,26 @@ func CGNATRange() netaddr.IPPrefix {
|
|||||||
tsUlaRange oncePrefix
|
tsUlaRange oncePrefix
|
||||||
ula4To6Range oncePrefix
|
ula4To6Range oncePrefix
|
||||||
ulaEph6Range oncePrefix
|
ulaEph6Range oncePrefix
|
||||||
|
serviceIPv6 oncePrefix
|
||||||
)
|
)
|
||||||
|
|
||||||
// TailscaleServiceIP returns the listen address of services
|
// TailscaleServiceIP returns the IPv4 listen address of services
|
||||||
// provided by Tailscale itself such as the MagicDNS proxy.
|
// provided by Tailscale itself such as the MagicDNS proxy.
|
||||||
|
//
|
||||||
|
// For IPv6, use TailscaleServiceIPv6.
|
||||||
func TailscaleServiceIP() netaddr.IP {
|
func TailscaleServiceIP() netaddr.IP {
|
||||||
return netaddr.IPv4(100, 100, 100, 100) // "100.100.100.100" for those grepping
|
return netaddr.IPv4(100, 100, 100, 100) // "100.100.100.100" for those grepping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TailscaleServiceIPv6 returns the IPv6 listen address of the services
|
||||||
|
// provided by Tailscale itself such as the MagicDNS proxy.
|
||||||
|
//
|
||||||
|
// For IPv4, use TailscaleServiceIP.
|
||||||
|
func TailscaleServiceIPv6() netaddr.IP {
|
||||||
|
serviceIPv6.Do(func() { mustPrefix(&serviceIPv6.v, "fd7a:115c:a1e0::53/128") })
|
||||||
|
return serviceIPv6.v.IP()
|
||||||
|
}
|
||||||
|
|
||||||
// IsTailscaleIP reports whether ip is an IP address in a range that
|
// IsTailscaleIP reports whether ip is an IP address in a range that
|
||||||
// Tailscale assigns from.
|
// Tailscale assigns from.
|
||||||
func IsTailscaleIP(ip netaddr.IP) bool {
|
func IsTailscaleIP(ip netaddr.IP) bool {
|
||||||
|
@ -31,6 +31,14 @@ func TestInCrostiniRange(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTailscaleServiceIPv6(t *testing.T) {
|
||||||
|
got := TailscaleServiceIPv6().String()
|
||||||
|
want := "fd7a:115c:a1e0::53"
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("got %q; want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestChromeOSVMRange(t *testing.T) {
|
func TestChromeOSVMRange(t *testing.T) {
|
||||||
if got, want := ChromeOSVMRange().String(), "100.115.92.0/23"; got != want {
|
if got, want := ChromeOSVMRange().String(), "100.115.92.0/23"; got != want {
|
||||||
t.Errorf("got %q; want %q", got, want)
|
t.Errorf("got %q; want %q", got, want)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/disco"
|
"tailscale.com/disco"
|
||||||
"tailscale.com/net/packet"
|
"tailscale.com/net/packet"
|
||||||
|
"tailscale.com/net/tsaddr"
|
||||||
"tailscale.com/tstime/mono"
|
"tailscale.com/tstime/mono"
|
||||||
"tailscale.com/types/ipproto"
|
"tailscale.com/types/ipproto"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
@ -407,17 +408,30 @@ func (t *Wrapper) sendOutbound(r tunReadResult) {
|
|||||||
t.outbound <- r
|
t.outbound <- r
|
||||||
}
|
}
|
||||||
|
|
||||||
var magicDNSIPPort = netaddr.MustParseIPPort("100.100.100.100:0")
|
var (
|
||||||
|
magicDNSIPPort = netaddr.IPPortFrom(tsaddr.TailscaleServiceIP(), 0) // 100.100.100.100:0
|
||||||
|
magicDNSIPPortv6 = netaddr.IPPortFrom(tsaddr.TailscaleServiceIPv6(), 0)
|
||||||
|
)
|
||||||
|
|
||||||
func (t *Wrapper) filterOut(p *packet.Parsed) filter.Response {
|
func (t *Wrapper) filterOut(p *packet.Parsed) filter.Response {
|
||||||
// Fake ICMP echo responses to MagicDNS (100.100.100.100).
|
// Fake ICMP echo responses to MagicDNS (100.100.100.100).
|
||||||
if p.IsEchoRequest() && p.Dst == magicDNSIPPort {
|
if p.IsEchoRequest() {
|
||||||
header := p.ICMP4Header()
|
switch p.Dst {
|
||||||
header.ToResponse()
|
case magicDNSIPPort:
|
||||||
outp := packet.Generate(&header, p.Payload())
|
header := p.ICMP4Header()
|
||||||
t.InjectInboundCopy(outp)
|
header.ToResponse()
|
||||||
return filter.DropSilently // don't pass on to OS; already handled
|
outp := packet.Generate(&header, p.Payload())
|
||||||
|
t.InjectInboundCopy(outp)
|
||||||
|
return filter.DropSilently // don't pass on to OS; already handled
|
||||||
|
case magicDNSIPPortv6:
|
||||||
|
header := p.ICMP6Header()
|
||||||
|
header.ToResponse()
|
||||||
|
outp := packet.Generate(&header, p.Payload())
|
||||||
|
t.InjectInboundCopy(outp)
|
||||||
|
return filter.DropSilently // don't pass on to OS; already handled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// TODO(bradfitz): support pinging TailscaleServiceIPv6 too.
|
||||||
|
|
||||||
// Issue 1526 workaround: if we sent disco packets over
|
// Issue 1526 workaround: if we sent disco packets over
|
||||||
// Tailscale from ourselves, then drop them, as that shouldn't
|
// Tailscale from ourselves, then drop them, as that shouldn't
|
||||||
|
@ -56,7 +56,10 @@
|
|||||||
|
|
||||||
const magicDNSPort = 53
|
const magicDNSPort = 53
|
||||||
|
|
||||||
var magicDNSIP = netaddr.IPv4(100, 100, 100, 100)
|
var (
|
||||||
|
magicDNSIP = tsaddr.TailscaleServiceIP()
|
||||||
|
magicDNSIPv6 = tsaddr.TailscaleServiceIPv6()
|
||||||
|
)
|
||||||
|
|
||||||
// Lazy wireguard-go configuration parameters.
|
// Lazy wireguard-go configuration parameters.
|
||||||
const (
|
const (
|
||||||
@ -486,12 +489,15 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
|
|||||||
|
|
||||||
// handleDNS is an outbound pre-filter resolving Tailscale domains.
|
// handleDNS is an outbound pre-filter resolving Tailscale domains.
|
||||||
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
|
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
|
||||||
if p.Dst.IP() == magicDNSIP && p.Dst.Port() == magicDNSPort && p.IPProto == ipproto.UDP {
|
if p.Dst.Port() == magicDNSPort && p.IPProto == ipproto.UDP {
|
||||||
err := e.dns.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src)
|
switch p.Dst.IP() {
|
||||||
if err != nil {
|
case magicDNSIP, magicDNSIPv6:
|
||||||
e.logf("dns: enqueue: %v", err)
|
err := e.dns.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src)
|
||||||
|
if err != nil {
|
||||||
|
e.logf("dns: enqueue: %v", err)
|
||||||
|
}
|
||||||
|
return filter.Drop
|
||||||
}
|
}
|
||||||
return filter.Drop
|
|
||||||
}
|
}
|
||||||
return filter.Accept
|
return filter.Accept
|
||||||
}
|
}
|
||||||
@ -508,22 +514,38 @@ func (e *userspaceEngine) pollResolver() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
h := packet.UDP4Header{
|
var buf []byte
|
||||||
IP4Header: packet.IP4Header{
|
|
||||||
Src: magicDNSIP,
|
|
||||||
Dst: to.IP(),
|
|
||||||
},
|
|
||||||
SrcPort: magicDNSPort,
|
|
||||||
DstPort: to.Port(),
|
|
||||||
}
|
|
||||||
hlen := h.Len()
|
|
||||||
|
|
||||||
// TODO(dmytro): avoid this allocation without importing tstun quirks into dns.
|
|
||||||
const offset = tstun.PacketStartOffset
|
const offset = tstun.PacketStartOffset
|
||||||
buf := make([]byte, offset+hlen+len(bs))
|
switch {
|
||||||
copy(buf[offset+hlen:], bs)
|
case to.IP().Is4():
|
||||||
h.Marshal(buf[offset:])
|
h := packet.UDP4Header{
|
||||||
|
IP4Header: packet.IP4Header{
|
||||||
|
Src: magicDNSIP,
|
||||||
|
Dst: to.IP(),
|
||||||
|
},
|
||||||
|
SrcPort: magicDNSPort,
|
||||||
|
DstPort: to.Port(),
|
||||||
|
}
|
||||||
|
hlen := h.Len()
|
||||||
|
// TODO(dmytro): avoid this allocation without importing tstun quirks into dns.
|
||||||
|
buf = make([]byte, offset+hlen+len(bs))
|
||||||
|
copy(buf[offset+hlen:], bs)
|
||||||
|
h.Marshal(buf[offset:])
|
||||||
|
case to.IP().Is6():
|
||||||
|
h := packet.UDP6Header{
|
||||||
|
IP6Header: packet.IP6Header{
|
||||||
|
Src: magicDNSIPv6,
|
||||||
|
Dst: to.IP(),
|
||||||
|
},
|
||||||
|
SrcPort: magicDNSPort,
|
||||||
|
DstPort: to.Port(),
|
||||||
|
}
|
||||||
|
hlen := h.Len()
|
||||||
|
// TODO(dmytro): avoid this allocation without importing tstun quirks into dns.
|
||||||
|
buf = make([]byte, offset+hlen+len(bs))
|
||||||
|
copy(buf[offset+hlen:], bs)
|
||||||
|
h.Marshal(buf[offset:])
|
||||||
|
}
|
||||||
e.tundev.InjectInboundDirect(buf, offset)
|
e.tundev.InjectInboundDirect(buf, offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user