]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[dns] Add support for resolving IPv6 addresses via AAAA records
authorMichael Brown <mcb30@ipxe.org>
Wed, 4 Dec 2013 22:21:47 +0000 (22:21 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 5 Dec 2013 00:41:49 +0000 (00:41 +0000)
Our policy is to prefer IPv6 addreses to IPv4 addresses, but to
request IPv6 addresses only if we have an IPv6 address for the name
server itself.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/dhcpv6.h
src/include/ipxe/dns.h
src/net/udp/dhcpv6.c
src/net/udp/dns.c

index 7a1a2b07f90573a43df5107f581f1e0be57089d7..a0a8d05b02044f377db8dcc124546f364cc933c1 100644 (file)
@@ -147,10 +147,10 @@ struct dhcpv6_user_class_option {
 #define DHCPV6_USER_CLASS 15
 
 /** DHCPv6 DNS recursive name server option */
-#define DHCPV6_DNS_SERVER 23
+#define DHCPV6_DNS_SERVERS 23
 
 /** DHCPv6 domain search list option */
-#define DHCPV6_DOMAIN_SEARCH 24
+#define DHCPV6_DOMAIN_LIST 24
 
 /**
  * Any DHCPv6 option
index 1c427601a2847896197620fe66fd93fae1816f64..164c16aec8686d014d50a85f7f95f75a96e01c02 100644 (file)
@@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 
 #define DNS_TYPE_A             1
 #define DNS_TYPE_CNAME         5
+#define DNS_TYPE_AAAA          28
 #define DNS_TYPE_ANY           255
 
 #define DNS_CLASS_IN           1
@@ -78,6 +79,11 @@ struct dns_rr_info_a {
        struct in_addr in_addr;
 } __attribute__ (( packed ));
 
+struct dns_rr_info_aaaa {
+       struct dns_rr_info_common common;
+       struct in6_addr in6_addr;
+} __attribute__ (( packed ));
+
 struct dns_rr_info_cname {
        struct dns_rr_info_common common;
        char cname[0];
@@ -86,6 +92,7 @@ struct dns_rr_info_cname {
 union dns_rr_info {
        struct dns_rr_info_common common;
        struct dns_rr_info_a a;
+       struct dns_rr_info_aaaa aaaa;
        struct dns_rr_info_cname cname;
 };
 
index 668974caec31829650108dc3d736164a9767cf2b..d38c5d94608da1f9adeab1da6db107d630d6985b 100644 (file)
@@ -362,7 +362,7 @@ static int dhcpv6_register ( struct dhcpv6_option_list *options,
 
 /** Options to be requested */
 static uint16_t dhcpv6_requested_options[] = {
-       htons ( DHCPV6_DNS_SERVER ), htons ( DHCPV6_DOMAIN_SEARCH ),
+       htons ( DHCPV6_DNS_SERVERS ), htons ( DHCPV6_DOMAIN_LIST ),
 };
 
 /**
index 447da8afd3ea7b52ba22af10f6d4db6f06074837..7e65efd4da4fd9f07e1b4e785b36bb65433d73f5 100644 (file)
@@ -37,6 +37,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/tcpip.h>
 #include <ipxe/settings.h>
 #include <ipxe/features.h>
+#include <ipxe/dhcp.h>
+#include <ipxe/dhcpv6.h>
 #include <ipxe/dns.h>
 
 /** @file
@@ -56,8 +58,15 @@ FEATURE ( FEATURE_PROTOCOL, "DNS", DHCP_EB_FEATURE_DNS, 1 );
        __einfo_uniqify ( EINFO_ENXIO, 0x02, "No DNS servers available" )
 
 /** The DNS server */
-static struct sockaddr_tcpip nameserver = {
-       .st_port = htons ( DNS_PORT ),
+static union {
+       struct sockaddr sa;
+       struct sockaddr_tcpip st;
+       struct sockaddr_in sin;
+       struct sockaddr_in6 sin6;
+} nameserver = {
+       .st = {
+               .st_port = htons ( DNS_PORT ),
+       },
 };
 
 /** The local domain */
@@ -75,7 +84,13 @@ struct dns_request {
        struct retry_timer timer;
 
        /** Socket address to fill in with resolved address */
-       struct sockaddr sa;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_in sin;
+               struct sockaddr_in6 sin6;
+       } address;
+       /** Initial query type */
+       uint16_t qtype;
        /** Current query packet */
        struct dns_query query;
        /** Location of query info structure within current packet
@@ -104,6 +119,24 @@ static void dns_done ( struct dns_request *dns, int rc ) {
        intf_shutdown ( &dns->resolv, rc );
 }
 
+/**
+ * Mark DNS request as resolved and complete
+ *
+ * @v dns              DNS request
+ * @v rc               Return status code
+ */
+static void dns_resolved ( struct dns_request *dns ) {
+
+       DBGC ( dns, "DNS %p found address %s\n",
+              dns, sock_ntoa ( &dns->address.sa ) );
+
+       /* Return resolved address */
+       resolv_done ( &dns->resolv, &dns->address.sa );
+
+       /* Mark operation as complete */
+       dns_done ( dns, 0 );
+}
+
 /**
  * Compare DNS reply name against the query name from the original request
  *
@@ -345,7 +378,6 @@ static int dns_xfer_deliver ( struct dns_request *dns,
                              struct xfer_metadata *meta __unused ) {
        const struct dns_header *reply = iobuf->data;
        union dns_rr_info *rr_info;
-       struct sockaddr_in *sin;
        unsigned int qtype = dns->qinfo->qtype;
        int rc;
 
@@ -383,20 +415,23 @@ static int dns_xfer_deliver ( struct dns_request *dns,
        while ( ( rr_info = dns_find_rr ( dns, reply ) ) ) {
                switch ( rr_info->common.type ) {
 
-               case htons ( DNS_TYPE_A ):
+               case htons ( DNS_TYPE_AAAA ):
 
-                       /* Found the target A record */
-                       DBGC ( dns, "DNS %p found address %s\n",
-                              dns, inet_ntoa ( rr_info->a.in_addr ) );
-                       sin = ( struct sockaddr_in * ) &dns->sa;
-                       sin->sin_family = AF_INET;
-                       sin->sin_addr = rr_info->a.in_addr;
+                       /* Found the target AAAA record */
+                       dns->address.sin6.sin6_family = AF_INET6;
+                       memcpy ( &dns->address.sin6.sin6_addr,
+                                &rr_info->aaaa.in6_addr,
+                                sizeof ( dns->address.sin6.sin6_addr ) );
+                       dns_resolved ( dns );
+                       rc = 0;
+                       goto done;
 
-                       /* Return resolved address */
-                       resolv_done ( &dns->resolv, &dns->sa );
+               case htons ( DNS_TYPE_A ):
 
-                       /* Mark operation as complete */
-                       dns_done ( dns, 0 );
+                       /* Found the target A record */
+                       dns->address.sin.sin_family = AF_INET;
+                       dns->address.sin.sin_addr = rr_info->a.in_addr;
+                       dns_resolved ( dns );
                        rc = 0;
                        goto done;
 
@@ -407,7 +442,7 @@ static int dns_xfer_deliver ( struct dns_request *dns,
                        dns->qinfo = ( void * ) dns_decompress_name ( reply,
                                                         rr_info->cname.cname,
                                                         dns->query.payload );
-                       dns->qinfo->qtype = htons ( DNS_TYPE_A );
+                       dns->qinfo->qtype = dns->qtype;
                        dns->qinfo->qclass = htons ( DNS_CLASS_IN );
                        
                        /* Terminate the operation if we recurse too far */
@@ -432,6 +467,16 @@ static int dns_xfer_deliver ( struct dns_request *dns,
         */
        switch ( qtype ) {
 
+       case htons ( DNS_TYPE_AAAA ):
+               /* We asked for an AAAA record and got nothing; try
+                * the A.
+                */
+               DBGC ( dns, "DNS %p found no AAAA record; trying A\n", dns );
+               dns->qinfo->qtype = htons ( DNS_TYPE_A );
+               dns_send_packet ( dns );
+               rc = 0;
+               goto done;
+
        case htons ( DNS_TYPE_A ):
                /* We asked for an A record and got nothing;
                 * try the CNAME.
@@ -447,7 +492,7 @@ static int dns_xfer_deliver ( struct dns_request *dns,
                 * (i.e. if the next A query is already set up), then
                 * issue it, otherwise abort.
                 */
-               if ( dns->qinfo->qtype == htons ( DNS_TYPE_A ) ) {
+               if ( dns->qinfo->qtype == dns->qtype ) {
                        dns_send_packet ( dns );
                        rc = 0;
                        goto done;
@@ -519,7 +564,7 @@ static int dns_resolv ( struct interface *resolv,
        int rc;
 
        /* Fail immediately if no DNS servers */
-       if ( ! nameserver.st_family ) {
+       if ( ! nameserver.sa.sa_family ) {
                DBG ( "DNS not attempting to resolve \"%s\": "
                      "no DNS servers\n", name );
                rc = -ENXIO_NO_NAMESERVER;
@@ -543,20 +588,32 @@ static int dns_resolv ( struct interface *resolv,
        intf_init ( &dns->resolv, &dns_resolv_desc, &dns->refcnt );
        intf_init ( &dns->socket, &dns_socket_desc, &dns->refcnt );
        timer_init ( &dns->timer, dns_timer_expired, &dns->refcnt );
-       memcpy ( &dns->sa, sa, sizeof ( dns->sa ) );
+       memcpy ( &dns->address.sa, sa, sizeof ( dns->address.sa ) );
+
+       /* Determine initial query type */
+       switch ( nameserver.sa.sa_family ) {
+       case AF_INET:
+               dns->qtype = htons ( DNS_TYPE_A );
+               break;
+       case AF_INET6:
+               dns->qtype = htons ( DNS_TYPE_AAAA );
+               break;
+       default:
+               rc = -ENOTSUP;
+               goto err_qtype;
+       }
 
        /* Create query */
        dns->query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
                                       DNS_FLAG_RD );
        dns->query.dns.qdcount = htons ( 1 );
        dns->qinfo = ( void * ) dns_make_name ( fqdn, dns->query.payload );
-       dns->qinfo->qtype = htons ( DNS_TYPE_A );
+       dns->qinfo->qtype = dns->qtype;
        dns->qinfo->qclass = htons ( DNS_CLASS_IN );
 
        /* Open UDP connection */
        if ( ( rc = xfer_open_socket ( &dns->socket, SOCK_DGRAM,
-                                      ( struct sockaddr * ) &nameserver,
-                                      NULL ) ) != 0 ) {
+                                      &nameserver.sa, NULL ) ) != 0 ) {
                DBGC ( dns, "DNS %p could not open socket: %s\n",
                       dns, strerror ( rc ) );
                goto err_open_socket;
@@ -572,10 +629,11 @@ static int dns_resolv ( struct interface *resolv,
        return 0;       
 
  err_open_socket:
- err_alloc_dns:
+ err_qtype:
        ref_put ( &dns->refcnt );
- err_qualify_name:
+ err_alloc_dns:
        free ( fqdn );
+ err_qualify_name:
  err_no_nameserver:
        return rc;
 }
@@ -593,7 +651,7 @@ struct resolver dns_resolver __resolver ( RESOLV_NORMAL ) = {
  ******************************************************************************
  */
 
-/** DNS server setting */
+/** IPv4 DNS server setting */
 const struct setting dns_setting __setting ( SETTING_IPv4_EXTRA ) = {
        .name = "dns",
        .description = "DNS server",
@@ -601,23 +659,34 @@ const struct setting dns_setting __setting ( SETTING_IPv4_EXTRA ) = {
        .type = &setting_type_ipv4,
 };
 
+/** IPv6 DNS server setting */
+const struct setting dns6_setting __setting ( SETTING_IPv6_EXTRA ) = {
+       .name = "dns6",
+       .description = "DNS server",
+       .tag = DHCPV6_DNS_SERVERS,
+       .type = &setting_type_ipv6,
+       .scope = &ipv6_scope,
+};
+
 /**
  * Apply DNS settings
  *
  * @ret rc             Return status code
  */
 static int apply_dns_settings ( void ) {
-       struct sockaddr_in *sin_nameserver =
-               ( struct sockaddr_in * ) &nameserver;
-       int len;
 
        /* Fetch DNS server address */
-       nameserver.st_family = 0;
-       if ( ( len = fetch_ipv4_setting ( NULL, &dns_setting,
-                                         &sin_nameserver->sin_addr ) ) >= 0 ){
-               nameserver.st_family = AF_INET;
+       nameserver.sa.sa_family = 0;
+       if ( fetch_ipv6_setting ( NULL, &dns6_setting,
+                                 &nameserver.sin6.sin6_addr ) >= 0 ) {
+               nameserver.sin6.sin6_family = AF_INET6;
+       } else if ( fetch_ipv4_setting ( NULL, &dns_setting,
+                                        &nameserver.sin.sin_addr ) >= 0 ) {
+               nameserver.sin.sin_family = AF_INET;
+       }
+       if ( nameserver.sa.sa_family ) {
                DBG ( "DNS using nameserver %s\n",
-                     inet_ntoa ( sin_nameserver->sin_addr ) );
+                     sock_ntoa ( &nameserver.sa ) );
        }
 
        /* Get local domain DHCP option */