Resolución IPv6 dns en macOS High Sierra

Mi ISP me proporciona solo una dirección IPv4.

Después de configurar una conexión con el servidor OpenVPN de mi universidad, obtengo una dirección IPv6. Con una dirección IPv6 de ejemplo www6.fit.vutbr.cz

Puedo:

Pero no puedo:

  • abra www6.fit.vutbr.cz en un navegador web

es decir, el navegador web no resuelve una dirección dns ipv6.

Si agrego la siguiente asignación a /etc/hosts

2001:67c:1220:809::93e5:916 www6.fit.vutbr.cz

el navegador web finalmente funciona.

Creo que tiene algo que ver con scutil --dns mostrando:

DNS configuration

resolver #1
  search domain[0] : lan
  nameserver[0] : 192.168.1.1
  nameserver[1] : 8.8.8.8
  nameserver[2] : 8.8.4.4
  flags    : Request A records
  reach    : 0x00020002 (Reachable,Directly Reachable Address)

Creo que debería ver Solicitar registros AAAA. ¿Por qué host, traceroute6 y ping6 resuelven el DNS, pero los navegadores web no? ¿Alguna sugerencia? Gracias

Comentario rápido: hasta ahora no he podido resolver el problema usando OpenVPN ni Tunnelblick, pero usando Viscosity lo resuelve. Es decir, Viscosity maneja el DNS IPv6 a través de VPN.

Respuestas (3)

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 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()

Al menos una interfaz de red debe incluir un servidor DNS con dirección IPv6.

Eso establece el indicador AAAA y luego Mac OS resolverá las direcciones IPv6.

El problema parece surgir porque IPv6 está deshabilitado en la interfaz Ethernet (al menos así fue conmigo).

Estoy accediendo a Internet IPv6 a través de tunnelbroker.net. Acabo de agregar mis direcciones local y de puerta de enlace de Tunnelbroker en la página Preferencias del sistema - Red en la página de red.

Una solución más limpia sería ajustar las banderas para Solicitar registros AAAA, pero no tengo idea de cómo hacerlo.