¿Cómo agregar el indicador AAAA (IPv6) a la configuración de resolución de DNS en Sierra?

Tengo una interfaz IPv6 en funcionamiento con el prefijo /64. Significa que puedo conectarme a direcciones IPv6 directas a través del navegador, pero no puedo conectarme a hosts IPv6 como testv6.madore.org.

El problema está en la configuración de DNS donde no tengo bandera "Request AAAA records":

$ scutil --dns

resolver #1
  search domain[0] : xxxxxxxx
  nameserver[0] : xxxxxxxx
  nameserver[1] : xxxxxxxx
  nameserver[2] : xxxxxxxx
  if_index : 4 (en0)
  flags    : Scoped, Request A records
  reach    : Reachable

¿Puede alguien decirme cómo agregar/habilitar manualmente ese indicador también?

flags    : Scoped, Request A records, Request AAAA records

Respuestas (2)

Fue una gran molestia resolverlo, así que escribí una pequeña guía con la esperanza de que otros la encontraran útil:

Cómo convencer a macOS para que realice búsquedas de DNS IPv6 cuando su única dirección IPv6 es a través de una VPN o algún tipo de túnel

El problema

El solucionador de nombres de dominio de macOS solo devolverá direcciones IPv6 (de registros AAAA) cuando crea que tiene una dirección IPv6 enrutable válida. Para las interfaces físicas como Ethernet o Wi-Fi, es suficiente establecer o que se le asigne una dirección IPv6, pero para los túneles (como los que usan utuninterfaces) hay algunos pasos extra molestos que deben tomarse para convencer al sistema de que sí, en efecto. tiene una dirección IPv6 y sí, le gustaría recuperar las direcciones IPv6 para las búsquedas de DNS.

Lo uso wg-quickpara establecer un túnel WireGuard entre mi computadora portátil y un servidor virtual Linode. WireGuard utiliza un utundispositivo de túnel de espacio de usuario para realizar la conexión. Así es como se configura ese dispositivo:

utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1420
    inet 10.75.131.2 --> 10.75.131.2 netmask 0xffffff00
    inet6 fe80::a65e:60ff:fee1:b1bf%utun1 prefixlen 64 scopeid 0xc
    inet6 2600:3c03::de:d002 prefixlen 116
    nd6 options=201<PERFORMNUD,DAD>

Y aquí hay algunas líneas relevantes de mi tabla de enrutamiento:

Internet:
Destination        Gateway            Flags        Refs      Use   Netif Expire
0/1                utun1              USc             0        0   utun1
default            10.20.4.4          UGSc            0        0     en3
10.20.4/24         link#14            UCS             3        0     en3      !
10.75.131.2        10.75.131.2        UH              0        0   utun1
50.116.51.30       10.20.4.4          UGHS            7  2629464     en3
128.0/1            utun1              USc             5        0   utun1

Internet6:
Destination                             Gateway                         Flags         Netif Expire
::/1                                    utun1                           USc           utun1
2600:3c03::de:d000/116                  fe80::a65e:60ff:fee1:b1bf%utun1 Uc            utun1
8000::/1                                utun1                           USc           utun1
  • 10.20.4/24es mi red ethernet local.
  • 10.20.4.5es la dirección IP LAN de mi computadora portátil.
  • 10.20.4.4es la dirección IP de LAN de mi puerta de enlace.
  • 10.75.131.2es la dirección IPv4 de mi extremo del túnel punto a punto de WireGuard.
  • 2600:3c03::de:d002es la dirección IPv6 de mi extremo del túnel punto a punto de WireGuard.
  • 50.116.51.30es la dirección pública de mi servidor Linode.

Esto debería ser suficiente para tener conectividad IPv6, ¿verdad? Bueno, la resolución de nombres funciona cuando hosthabla directamente con mi servidor de nombres:

sam@shiny ~> host ipv6.whatismyv6.com
ipv6.whatismyv6.com has IPv6 address 2607:f0d0:3802:84::128

Hacer ping por dirección IPv6 funciona:

sam@shiny ~> ping6 -c1 2607:f0d0:3802:84::128
PING6(56=40+8+8 bytes) 2600:3c03::de:d002 --> 2607:f0d0:3802:84::128
16 bytes from 2607:f0d0:3802:84::128, icmp_seq=0 hlim=55 time=80.991 ms

--- 2607:f0d0:3802:84::128 ping6 statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 80.991/80.991/80.991/0.000 ms

Y las conexiones HTTP por dirección IPv6 funcionan:

sam@shiny ~> curl -s 'http://[2607:f0d0:3802:84::128]' -H 'Host: ipv6.whatismyv6.com' | html2text | head -3
                 This page shows your IPv6 and/or IPv4 address
                          You are connecting with an IPv6 Address of:
                                             2600:3c03::de:d002

Sin embargo, las conexiones HTTP por nombre de host solo IPv6 no funcionan:

sam@shiny ~> curl 'http://ipv6.whatismyv6.com'
curl: (6) Could not resolve host: ipv6.whatismyv6.com

El resultado es el mismo en wgetaplicaciones GUI como Firefox: la conexión mediante una dirección IPv6 literal funciona bien, pero la conexión mediante un nombre de host que solo tiene un registro AAAA (y ningún registro A) asociado no funciona.

Curiosamente, ping6 puede realizar una búsqueda de DNS y recuperar una dirección IPv6:

sam@shiny ~ [6]> ping6 -c1 ipv6.whatismyv6.com
PING6(56=40+8+8 bytes) 2600:3c03::de:d002 --> 2607:f0d0:3802:84::128
16 bytes from 2607:f0d0:3802:84::128, icmp_seq=0 hlim=55 time=49.513 ms

--- ipv6.whatismyv6.com ping6 statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 49.513/49.513/49.513/0.000 ms

¿ Por qué puede ping6hacer esto cuando nada más puede? Resulta que cuando lo ping6llama getaddrinfosobrescribe las banderas predeterminadas. Uno de los indicadores predeterminados es AI_ADDRCONFIG, que le dice al resolutor que solo devuelva direcciones en familias de direcciones para las que el sistema tiene una dirección IP. (Es decir, no devuelva direcciones IPv6 a menos que el sistema tenga una dirección IPv6 (no de enlace local).) La mayoría de los otros programas agregan a las banderas predeterminadas en lugar de aplastarlas, lo que supongo que es sensato.

Si lo ejecuta, scutil --dnsle dirá cómo está configurado el resolver. Aquí está la salida en mi sistema (menos un montón de cosas de mdns que no importan):

DNS configuration

resolver #1
  search domain[0] : home.munkynet.org
  nameserver[0] : 10.20.4.4
  if_index : 14 (en3)
  flags    : Request A records
  reach    : 0x00020002 (Reachable,Directly Reachable Address)

DNS configuration (for scoped queries)

resolver #1
  search domain[0] : home.munkynet.org
  nameserver[0] : 10.20.4.4
  if_index : 14 (en3)
  flags    : Scoped, Request A records
  reach    : 0x00020002 (Reachable,Directly Reachable Address)

Tenga en cuenta que en flags, dice Request A recordspero no Request AAAA records. Por lo tanto, nos corresponde a nosotros tratar de convencer al resolutor de macOS de que, de hecho, tenemos una dirección IPv6 válida, aunque esté en una interfaz de túnel.

Configuración del sistema

La forma "correcta" de que esto suceda es que cualquier programa configure el túnel para usar la SystemConfigurationAPI extraña y en gran parte indocumentada para registrar el "servicio" de la red y sus propiedades IPv6. La aplicación Viscosity hace esto. Tunnelblick no lo hace, el OpenVPN Client oficial no lo hace, y wg-quickseguro que tampoco.

el scutilchapucero

Podemos crear las mismas estructuras de "servicio" de SystemConfiguration manualmente usando el scutilcomando:

Primero creamos la parte IPv4 del servicio:

sam@shiny ~> sudo scutil
> d.init
> d.add Addresses * 10.75.131.2
> d.add DestAddresses * 10.75.131.2
> d.add InterfaceName utun1
> set State:/Network/Service/my_ipv6_tunnel_service/IPv4
> set Setup:/Network/Service/my_ipv6_tunnel_service/IPv4

Y luego creamos la parte IPv6:

> d.init
> d.add Addresses * fe80::a65e:60ff:fee1:b1bf 2600:3c03::de:d002
> d.add DestAddresses * ::ffff:ffff:ffff:ffff:0:0 ::
> d.add Flags * 0 0
> d.add InterfaceName utun1
> d.add PrefixLength * 64 116
> set State:/Network/Service/my_ipv6_tunnel_service/IPv6
> set Setup:/Network/Service/my_ipv6_tunnel_service/IPv6
> quit

Una vez hecho esto, la salida de scutil --dns(de nuevo módulo mdns cosas) cambia:

DNS configuration

resolver #1
  search domain[0] : home.munkynet.org
  nameserver[0] : 10.20.4.4
  if_index : 14 (en3)
  flags    : Request A records, Request AAAA records
  reach    : 0x00020002 (Reachable,Directly Reachable Address)

DNS configuration (for scoped queries)

resolver #1
  search domain[0] : home.munkynet.org
  nameserver[0] : 10.20.4.4
  if_index : 14 (en3)
  flags    : Scoped, Request A records
  reach    : 0x00020002 (Reachable,Directly Reachable Address)

Ahora lo vemos Request AAAA recordsen las banderas! No estoy realmente seguro de qué son las "consultas de alcance" o por qué la configuración de DNS para ellas no cambió, pero parece que las cosas funcionan ahora, así que sea lo que sea:

sam@shiny ~> curl -s 'http://ipv6.whatismyv6.com' | html2text | head -3
                 This page shows your IPv6 and/or IPv4 address
                          You are connecting with an IPv6 Address of:
                                             2600:3c03::de:d002

Al desconectarse del túnel, todo lo que tiene que hacer es eliminar las claves de configuración del sistema que agregó:

sam@shiny ~> sudo scutil
> remove State:/Network/Service/my_ipv6_tunnel_service/IPv4
> remove Setup:/Network/Service/my_ipv6_tunnel_service/IPv4
> remove State:/Network/Service/my_ipv6_tunnel_service/IPv6
> remove Setup:/Network/Service/my_ipv6_tunnel_service/IPv6
> quit

Un par de cosas a tener en cuenta:

  • El nombre my_ipv6_tunnel_servicees totalmente arbitrario.
  • De acuerdo con la información que obtuve de los scripts arriba/abajo en el .ovpnperfil de Mullvad, debe crear las teclas Setup:y . State:No verifiqué esto porque soy perezoso.
  • No tengo ni idea de dónde DestAddressesviene el IPv6. Los copié de Viscosity porque parecían funcionar allí. ::ffff:ffff:ffff:ffff:0:0para la dirección local de enlace y ::para el público
  • Ni siquiera sé realmente qué DestAddressessignifica o para qué se usa.

un buen guion

Escribí un script de python que obtiene direcciones y longitudes de prefijos de ifconfigla salida. Requiere Python 3.6 o posterior, así que asegúrese de tenerlo en su camino. Se llama wg-updowny llama a su servicio SystemConfiguration wg-updown-utun#, pero en realidad no es específico de WireGuard. Puede llamarlo como un script de subida/bajada previa para cualquier túnel VPN antiguo o ejecutarlo manualmente. Llámalo así:

# After tunnel comes up
wg-updown up IFACE

# Before tunnel goes down
wg-updown down IFACE

reemplácelo IFACEcon el nombre de la interfaz que está utilizando su túnel/cliente VPN, por ejemplo utun1, . Imprimirá los comandos que está enviando scutilpara que pueda ver lo que está haciendo en detalle.

#!/usr/bin/env python3

import re
import subprocess
import sys

def service_name_for_interface(interface):
    return 'wg-updown-' + interface

v4pat = re.compile(r'^\s*inet\s+(\S+)\s+-->\s+(\S+)\s+netmask\s+\S+')
v6pat = re.compile(r'^\s*inet6\s+(\S+?)(?:%\S+)?\s+prefixlen\s+(\S+)')
def get_tunnel_info(interface):
    ipv4s = dict(Addresses=[], DestAddresses=[])
    ipv6s = dict(Addresses=[], DestAddresses=[], Flags=[], PrefixLength=[])
    ifconfig = subprocess.run(["ifconfig", interface], capture_output=True,
                              check=True, text=True)
    for line in ifconfig.stdout.splitlines():
        v6match = v6pat.match(line)
        if v6match:
            ipv6s['Addresses'].append(v6match[1])
            # This is cribbed from Viscosity and probably wrong.
            if v6match[1].startswith('fe80'):
                ipv6s['DestAddresses'].append('::ffff:ffff:ffff:ffff:0:0')
            else:
                ipv6s['DestAddresses'].append('::')
            ipv6s['Flags'].append('0')
            ipv6s['PrefixLength'].append(v6match[2])
            continue
        v4match = v4pat.match(line)
        if v4match:
            ipv4s['Addresses'].append(v4match[1])
            ipv4s['DestAddresses'].append(v4match[2])
            continue
    return (ipv4s, ipv6s)

def run_scutil(commands):
    print(commands)
    subprocess.run(['scutil'], input=commands, check=True, text=True)

def up(interface):
    service_name = service_name_for_interface(interface)
    (ipv4s, ipv6s) = get_tunnel_info(interface)
    run_scutil('\n'.join([
        f"d.init",
        f"d.add Addresses * {' '.join(ipv4s['Addresses'])}",
        f"d.add DestAddresses * {' '.join(ipv4s['DestAddresses'])}",
        f"d.add InterfaceName {interface}",
        f"set State:/Network/Service/{service_name}/IPv4",
        f"set Setup:/Network/Service/{service_name}/IPv4",
        f"d.init",
        f"d.add Addresses * {' '.join(ipv6s['Addresses'])}",
        f"d.add DestAddresses * {' '.join(ipv6s['DestAddresses'])}",
        f"d.add Flags * {' '.join(ipv6s['Flags'])}",
        f"d.add InterfaceName {interface}",
        f"d.add PrefixLength * {' '.join(ipv6s['PrefixLength'])}",
        f"set State:/Network/Service/{service_name}/IPv6",
        f"set Setup:/Network/Service/{service_name}/IPv6",
    ]))

def down(interface):
    service_name = service_name_for_interface(interface)
    run_scutil('\n'.join([
        f"remove State:/Network/Service/{service_name}/IPv4",
        f"remove Setup:/Network/Service/{service_name}/IPv4",
        f"remove State:/Network/Service/{service_name}/IPv6",
        f"remove Setup:/Network/Service/{service_name}/IPv6",
    ]))

def main():
    operation = sys.argv[1]
    interface = sys.argv[2]
    if operation == 'up':
        up(interface)
    elif operation == 'down':
        down(interface)
    else:
        raise NotImplementedError()

if __name__ == "__main__":
    main()
gracias por hacer esto! Noté que funcionó para todo menos para Chrome. Alguien descubrí que agregar una Ruta convenció a Chrome:d.add Router <one-of-the-addresses>

He encontrado cómo arreglar esto. El servicio IPv6 debe agregarse al dispositivo de túnel (en caso de que sea una VPN, utun1por ejemplo) usando scutil.

$ scutil

show State:/Network/Service/utun1/IPv6
<dictionary> {
  Addresses : <array> {
    0 : "your IPv6 address here"
  }
  InterfaceName : utun1
  PrefixLength : <array> {
    0 : 64
  }
  Router : "your IPv6 address here"
}
interesante hallazgo Estoy tratando de aprender más detalles sobre esto. ¿Encontraste esto documentado en alguna parte?