]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipv6] Expose NDP-provided settings (including the DNS server)
authorMichael Brown <mcb30@ipxe.org>
Thu, 5 Dec 2013 16:44:50 +0000 (16:44 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 5 Dec 2013 16:44:50 +0000 (16:44 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/ndp.h
src/net/ndp.c

index 46083b53c774aa9abcd93df5be9b6f30c7ba04a1..464dab333139a86270f36add88873fd99e27f6d6 100644 (file)
@@ -67,6 +67,36 @@ struct ndp_prefix_information_option {
 /** NDP autonomous address configuration flag */
 #define NDP_PREFIX_AUTONOMOUS 0x40
 
+/** NDP recursive DNS server option */
+#define NDP_OPT_RDNSS 25
+
+/** NDP recursive DNS server */
+struct ndp_rdnss_option {
+       /** NDP option header */
+       struct ndp_option_header header;
+       /** Reserved */
+       uint16_t reserved;
+       /** Lifetime */
+       uint32_t lifetime;
+       /** Addresses */
+       struct in6_addr addresses[0];
+} __attribute__ (( packed ));
+
+/** NDP DNS search list option */
+#define NDP_OPT_DNSSL 31
+
+/** NDP DNS search list */
+struct ndp_dnssl_option {
+       /** NDP option header */
+       struct ndp_option_header header;
+       /** Reserved */
+       uint16_t reserved;
+       /** Lifetime */
+       uint32_t lifetime;
+       /** Domain names */
+       uint8_t names[0];
+} __attribute__ (( packed ));
+
 /** An NDP option */
 union ndp_option {
        /** Option header */
@@ -75,6 +105,10 @@ union ndp_option {
        struct ndp_ll_addr_option ll_addr;
        /** Prefix information option */
        struct ndp_prefix_information_option prefix;
+       /** Recursive DNS server option */
+       struct ndp_rdnss_option rdnss;
+       /** DNS search list option */
+       struct ndp_dnssl_option dnssl;
 } __attribute__ (( packed ));
 
 /** An NDP neighbour solicitation or advertisement header */
@@ -166,4 +200,7 @@ static inline int ndp_tx ( struct io_buffer *iobuf, struct net_device *netdev,
                              &ndp_discovery, net_source, ll_source );
 }
 
+/** NDP settings block name */
+#define NDP_SETTINGS_NAME "ndp"
+
 #endif /* _IPXE_NDP_H */
index 862e31ef40843c4b88ff982b7cf1a43ac1ce1619..8141c8deb5c400bbf4df5f8dbcbcdf76401d3fe9 100644 (file)
@@ -38,8 +38,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
  *
  */
 
-static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
-                                             unsigned int flags );
+static int
+ipv6conf_rx_router_advertisement ( struct net_device *netdev,
+                                  struct ndp_router_advertisement_header *radv,
+                                  size_t len );
 
 /**
  * Transmit NDP packet with link-layer address option
@@ -585,8 +587,8 @@ ndp_rx_router_advertisement ( struct io_buffer *iobuf,
                goto err_options;
 
        /* Pass to IPv6 autoconfiguration */
-       if ( ( rc = ipv6conf_rx_router_advertisement ( netdev,
-                                                      radv->flags ) ) != 0 )
+       if ( ( rc = ipv6conf_rx_router_advertisement ( netdev, radv,
+                                                      len ) ) != 0 )
                goto err_ipv6conf;
 
  err_ipv6conf:
@@ -611,6 +613,179 @@ struct icmpv6_handler ndp_handlers[] __icmpv6_handler = {
        },
 };
 
+/****************************************************************************
+ *
+ * NDP settings
+ *
+ */
+
+/** An NDP settings block */
+struct ndp_settings {
+       /** Reference counter */
+       struct refcnt refcnt;
+       /** Settings interface */
+       struct settings settings;
+       /** Length of NDP options */
+       size_t len;
+       /** NDP options */
+       union ndp_option option[0];
+};
+
+/** NDP settings scope */
+static const struct settings_scope ndp_settings_scope;
+
+/**
+ * Construct NDP tag
+ *
+ * @v type             NDP option type
+ * @v offset           Starting offset of data
+ * @ret tag            NDP tag
+ */
+#define NDP_TAG( type, offset )        ( ( (offset) << 8 ) | (type) )
+
+/**
+ * Extract NDP tag type
+ *
+ * @v tag              NDP tag
+ * @ret type           NDP option type
+ */
+#define NDP_TAG_TYPE( tag ) ( (tag) & 0xff )
+
+/**
+ * Extract NDP tag offset
+ *
+ * @v tag              NDP tag
+ * @ret offset         Starting offset of data
+ */
+#define NDP_TAG_OFFSET( tag ) ( (tag) >> 8 )
+
+/**
+ * Check applicability of NDP setting
+ *
+ * @v settings         Settings block
+ * @v setting          Setting to fetch
+ * @ret applies                Setting applies within this settings block
+ */
+static int ndp_applies ( struct settings *settings __unused,
+                        const struct setting *setting ) {
+
+       return ( setting->scope == &ndp_settings_scope );
+}
+
+/**
+ * Fetch value of NDP setting
+ *
+ * @v settings         Settings block
+ * @v setting          Setting to fetch
+ * @v data             Buffer to fill with setting data
+ * @v len              Length of buffer
+ * @ret len            Length of setting data, or negative error
+ */
+static int ndp_fetch ( struct settings *settings,
+                      struct setting *setting,
+                      void *data, size_t len ) {
+       struct ndp_settings *ndpset =
+               container_of ( settings, struct ndp_settings, settings );
+       struct net_device *netdev =
+               container_of ( settings->parent, struct net_device,
+                              settings.settings );
+       union ndp_option *option;
+       unsigned int type = NDP_TAG_TYPE ( setting->tag );
+       unsigned int offset = NDP_TAG_OFFSET ( setting->tag );
+       size_t remaining;
+       size_t option_len;
+       size_t payload_len;
+
+       /* Scan through NDP options for requested type.  We can assume
+        * that the options are well-formed, otherwise they would have
+        * been rejected prior to being stored.
+        */
+       option = ndpset->option;
+       remaining = ndpset->len;
+       while ( remaining ) {
+
+               /* Calculate option length */
+               option_len = ( option->header.blocks * NDP_OPTION_BLKSZ );
+
+               /* If this is the requested option, return it */
+               if ( option->header.type == type ) {
+
+                       /* Sanity check */
+                       if ( offset > option_len ) {
+                               DBGC ( netdev, "NDP %s option %d too short\n",
+                                      netdev->name, type );
+                               return -EINVAL;
+                       }
+                       payload_len = ( option_len - offset );
+
+                       /* Copy data to output buffer */
+                       if ( len > payload_len )
+                               len = payload_len;
+                       memcpy ( data, ( ( ( void * ) option ) + offset ), len);
+                       return payload_len;
+               }
+
+               /* Move to next option */
+               option = ( ( ( void * ) option ) + option_len );
+               remaining -= option_len;
+       }
+
+       return -ENOENT;
+}
+
+/** NDP settings operations */
+static struct settings_operations ndp_settings_operations = {
+       .applies = ndp_applies,
+       .fetch = ndp_fetch,
+};
+
+/**
+ * Register NDP settings
+ *
+ * @v netdev           Network device
+ * @v option           NDP options
+ * @v len              Length of options
+ * @ret rc             Return status code
+ */
+static int ndp_register_settings ( struct net_device *netdev,
+                                  union ndp_option *option, size_t len ) {
+       struct settings *parent = netdev_settings ( netdev );
+       struct ndp_settings *ndpset;
+       int rc;
+
+       /* Allocate and initialise structure */
+       ndpset = zalloc ( sizeof ( *ndpset ) + len );
+       if ( ! ndpset ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       ref_init ( &ndpset->refcnt, NULL );
+       settings_init ( &ndpset->settings, &ndp_settings_operations,
+                       &ndpset->refcnt, &ndp_settings_scope );
+       ndpset->len = len;
+       memcpy ( ndpset->option, option, len );
+
+       /* Register settings */
+       if ( ( rc = register_settings ( &ndpset->settings, parent,
+                                       NDP_SETTINGS_NAME ) ) != 0 )
+               goto err_register;
+
+ err_register:
+       ref_put ( &ndpset->refcnt );
+ err_alloc:
+       return rc;
+}
+
+/** DNS server setting */
+const struct setting ndp_dns6_setting __setting ( SETTING_IP_EXTRA, dns6 ) = {
+       .name = "dns6",
+       .description = "DNS server",
+       .tag = NDP_TAG ( NDP_OPT_RDNSS,
+                        offsetof ( struct ndp_rdnss_option, addresses ) ),
+       .type = &setting_type_ipv6,
+       .scope = &ndp_settings_scope,
+};
+
 /****************************************************************************
  *
  * IPv6 autoconfiguration
@@ -713,12 +888,19 @@ static void ipv6conf_expired ( struct retry_timer *timer, int fail ) {
  * Handle router advertisement during IPv6 autoconfiguration
  *
  * @v netdev           Network device
- * @v flags            Router flags
+ * @v radv             Router advertisement
+ * @v len              Length of router advertisement
  * @ret rc             Return status code
+ *
+ * This function assumes that the router advertisement is well-formed,
+ * since it must have already passed through option processing.
  */
-static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
-                                             unsigned int flags ) {
+static int
+ipv6conf_rx_router_advertisement ( struct net_device *netdev,
+                                  struct ndp_router_advertisement_header *radv,
+                                  size_t len ) {
        struct ipv6conf *ipv6conf;
+       size_t option_len;
        int stateful;
        int rc;
 
@@ -739,9 +921,15 @@ static int ipv6conf_rx_router_advertisement ( struct net_device *netdev,
        /* Stop router solicitation timer */
        stop_timer ( &ipv6conf->timer );
 
+       /* Register NDP settings */
+       option_len = ( len - offsetof ( typeof ( *radv ), option ) );
+       if ( ( rc = ndp_register_settings ( netdev, radv->option,
+                                           option_len ) ) != 0 )
+               return rc;
+
        /* Start DHCPv6 if required */
-       if ( flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
-               stateful = ( flags & NDP_ROUTER_MANAGED );
+       if ( radv->flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
+               stateful = ( radv->flags & NDP_ROUTER_MANAGED );
                if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
                                           stateful ) ) != 0 ) {
                        DBGC ( netdev, "NDP %s could not start state%s DHCPv6: "