]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ipv6] Expose router address for DHCPv6 leased addresses 1245/head
authorMichael Brown <mcb30@ipxe.org>
Thu, 27 Jun 2024 12:26:39 +0000 (13:26 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 27 Jun 2024 12:43:37 +0000 (13:43 +0100)
The DHCPv6 protocol does not itself provide a router address or a
prefix length.  This information is instead obtained from the router
advertisements.

Our IPv6 minirouting table construction logic will first construct an
entry for each advertised prefix, and later update the entry to
include an address assigned within that prefix via stateful DHCPv6 (if
applicable).

This logic fails if the address assigned via stateful DHCPv6 does not
fall within any of the advertised prefixes (e.g. if the network is
configured to use DHCPv6-assigned /128 addresses with no advertised
on-link prefixes).  We will currently treat this situation as
equivalent to having a manually assigned address with no corresponding
router address or prefix length: the routing table entry will use the
default /64 prefix length and will not include the router address.

DHCPv6 is triggered only in response to a router advertisement with
the "Managed Address Configuration (M)" or "Other Configuration (O)"
flags set, and a router address is therefore available at the point
that we initiate DHCPv6.

Record the router address when initiating DHCPv6, and expose this
router address as part of the DHCPv6 settings block.  This allows the
routing table entry for any address assigned via stateful DHCPv6 to
correctly include the router address, even if the assigned address
does not fall within an advertised prefix.

Also provide a fixed /128 prefix length as part of the DHCPv6 settings
block.  When an address assigned via stateful DHCPv6 does not fall
within an advertised prefix, this will cause the routing table entry
to have a /128 prefix length as expected.  (When such an address does
fall within an advertised prefix, it will continue to use the
advertised prefix length.)

Originally-fixed-by: Guvenc Gulce <guevenc.guelce@sap.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/dhcpv6.h
src/net/ndp.c
src/net/udp/dhcpv6.c

index 6e70f7e6391345f6ac9158087b7a677701dd6363..065e9c376dcca0b6ffe93886efa62e58ff0a1e6d 100644 (file)
@@ -276,6 +276,6 @@ static inline void ipv6_all_dhcp_relay_and_servers ( struct in6_addr *addr ) {
 }
 
 extern int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
-                         int stateful );
+                         struct in6_addr *router, int stateful );
 
 #endif /* _IPXE_DHCPV6_H */
index 373a9360b1ed419c2d62700f7374959f8d46bfc8..3c555f4a327e3b8e96485dc32a05857ac2f70927 100644 (file)
@@ -1221,7 +1221,7 @@ ipv6conf_rx_router_advertisement ( struct net_device *netdev,
        /* Start DHCPv6 if required */
        if ( radv->flags & ( NDP_ROUTER_MANAGED | NDP_ROUTER_OTHER ) ) {
                stateful = ( radv->flags & NDP_ROUTER_MANAGED );
-               if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev,
+               if ( ( rc = start_dhcpv6 ( &ipv6conf->dhcp, netdev, router,
                                           stateful ) ) != 0 ) {
                        DBGC ( netdev, "NDP %s could not start state%s DHCPv6: "
                               "%s\n", netdev->name,
index 9e27dec6fa1a802eae6f91cdc9df01febf0d8403..a49109894184287cbbbf6d048ed33057490f4903 100644 (file)
@@ -268,6 +268,8 @@ struct dhcpv6_settings {
        struct settings settings;
        /** Leased address */
        struct in6_addr lease;
+       /** Router address */
+       struct in6_addr router;
        /** Option list */
        struct dhcpv6_option_list options;
 };
@@ -283,25 +285,21 @@ static int dhcpv6_applies ( struct settings *settings __unused,
                            const struct setting *setting ) {
 
        return ( ( setting->scope == &dhcpv6_scope ) ||
-                ( setting_cmp ( setting, &ip6_setting ) == 0 ) );
+                ( setting->scope == &ipv6_settings_scope ) );
 }
 
 /**
  * Fetch value of DHCPv6 leased address
  *
- * @v dhcpset          DHCPv6 settings
+ * @v dhcpv6set                DHCPv6 settings
  * @v data             Buffer to fill with setting data
  * @v len              Length of buffer
  * @ret len            Length of setting data, or negative error
  */
-static int dhcpv6_fetch_lease ( struct dhcpv6_settings *dhcpv6set,
-                               void *data, size_t len ) {
+static int dhcpv6_fetch_ip6 ( struct dhcpv6_settings *dhcpv6set,
+                             void *data, size_t len ) {
        struct in6_addr *lease = &dhcpv6set->lease;
 
-       /* Do nothing unless a leased address exists */
-       if ( IN6_IS_ADDR_UNSPECIFIED ( lease ) )
-               return -ENOENT;
-
        /* Copy leased address */
        if ( len > sizeof ( *lease ) )
                len = sizeof ( *lease );
@@ -310,6 +308,72 @@ static int dhcpv6_fetch_lease ( struct dhcpv6_settings *dhcpv6set,
        return sizeof ( *lease );
 }
 
+/**
+ * Fetch value of DHCPv6 implicit address prefix length
+ *
+ * @v dhcpv6set                DHCPv6 settings
+ * @v data             Buffer to fill with setting data
+ * @v len              Length of buffer
+ * @ret len            Length of setting data, or negative error
+ */
+static int dhcpv6_fetch_len6 ( struct dhcpv6_settings *dhcpv6set __unused,
+                              void *data, size_t len ) {
+       uint8_t *len6 = data;
+
+       /* Default to assuming this is the only address on the link.
+        * If the address falls within a known prefix, then the IPv6
+        * routing table construction logic will match it against that
+        * prefix.
+        */
+       if ( len )
+               *len6 = IPV6_MAX_PREFIX_LEN;
+
+       return sizeof ( *len6 );
+}
+
+/**
+ * Fetch value of DHCPv6 router address
+ *
+ * @v dhcpv6set                DHCPv6 settings
+ * @v data             Buffer to fill with setting data
+ * @v len              Length of buffer
+ * @ret len            Length of setting data, or negative error
+ */
+static int dhcpv6_fetch_gateway6 ( struct dhcpv6_settings *dhcpv6set,
+                                  void *data, size_t len ) {
+       struct in6_addr *router = &dhcpv6set->router;
+
+       /* Copy router address */
+       if ( len > sizeof ( *router ) )
+               len = sizeof ( *router );
+       memcpy ( data, router, len );
+
+       return sizeof ( *router );
+}
+
+/** A DHCPv6 address setting operation */
+struct dhcpv6_address_operation {
+       /** Generic setting */
+       const struct setting *setting;
+       /**
+        * Fetch value of setting
+        *
+        * @v dhcpv6set         DHCPv6 settings
+        * @v data              Buffer to fill with setting data
+        * @v len               Length of buffer
+        * @ret len             Length of setting data, or negative error
+        */
+       int ( * fetch ) ( struct dhcpv6_settings *dhcpv6set,
+                         void *data, size_t len );
+};
+
+/** DHCPv6 address settings operations */
+static struct dhcpv6_address_operation dhcpv6_address_operations[] = {
+       { &ip6_setting, dhcpv6_fetch_ip6 },
+       { &len6_setting, dhcpv6_fetch_len6 },
+       { &gateway6_setting, dhcpv6_fetch_gateway6 },
+};
+
 /**
  * Fetch value of DHCPv6 setting
  *
@@ -325,11 +389,20 @@ static int dhcpv6_fetch ( struct settings *settings,
        struct dhcpv6_settings *dhcpv6set =
                container_of ( settings, struct dhcpv6_settings, settings );
        const union dhcpv6_any_option *option;
+       struct dhcpv6_address_operation *op;
        size_t option_len;
-
-       /* Handle leased address */
-       if ( setting_cmp ( setting, &ip6_setting ) == 0 )
-               return dhcpv6_fetch_lease ( dhcpv6set, data, len );
+       unsigned int i;
+
+       /* Handle address settings */
+       for ( i = 0 ; i < ( sizeof ( dhcpv6_address_operations ) /
+                           sizeof ( dhcpv6_address_operations[0] ) ) ; i++ ) {
+               op = &dhcpv6_address_operations[i];
+               if ( setting_cmp ( setting, op->setting ) != 0 )
+                       continue;
+               if ( IN6_IS_ADDR_UNSPECIFIED ( &dhcpv6set->lease ) )
+                       return -ENOENT;
+               return op->fetch ( dhcpv6set, data, len );
+       }
 
        /* Find option */
        option = dhcpv6_option ( &dhcpv6set->options, setting->tag );
@@ -354,11 +427,12 @@ static struct settings_operations dhcpv6_settings_operations = {
  * Register DHCPv6 options as network device settings
  *
  * @v lease            DHCPv6 leased address
+ * @v router           DHCPv6 router address
  * @v options          DHCPv6 option list
  * @v parent           Parent settings block
  * @ret rc             Return status code
  */
-static int dhcpv6_register ( struct in6_addr *lease,
+static int dhcpv6_register ( struct in6_addr *lease, struct in6_addr *router,
                             struct dhcpv6_option_list *options,
                             struct settings *parent ) {
        struct dhcpv6_settings *dhcpv6set;
@@ -382,6 +456,7 @@ static int dhcpv6_register ( struct in6_addr *lease,
        dhcpv6set->options.data = data;
        dhcpv6set->options.len = len;
        memcpy ( &dhcpv6set->lease, lease, sizeof ( dhcpv6set->lease ) );
+       memcpy ( &dhcpv6set->router, router, sizeof ( dhcpv6set->router ) );
 
        /* Register settings */
        if ( ( rc = register_settings ( &dhcpv6set->settings, parent,
@@ -501,6 +576,8 @@ struct dhcpv6_session {
 
        /** Network device being configured */
        struct net_device *netdev;
+       /** Router address */
+       struct in6_addr router;
        /** Transaction ID */
        uint8_t xid[3];
        /** Identity association ID */
@@ -876,8 +953,8 @@ static int dhcpv6_rx ( struct dhcpv6_session *dhcpv6,
        }
 
        /* Register settings */
-       if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &options,
-                                     parent ) ) != 0 ) {
+       if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &dhcpv6->router,
+                                     &options, parent ) ) != 0 ) {
                DBGC ( dhcpv6, "DHCPv6 %s could not register settings: %s\n",
                       dhcpv6->netdev->name, strerror ( rc ) );
                goto done;
@@ -915,11 +992,12 @@ static struct interface_descriptor dhcpv6_xfer_desc =
  *
  * @v job              Job control interface
  * @v netdev           Network device
+ * @v router           Router address
  * @v stateful         Perform stateful address autoconfiguration
  * @ret rc             Return status code
  */
 int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
-                  int stateful ) {
+                  struct in6_addr *router, int stateful ) {
        struct ll_protocol *ll_protocol = netdev->ll_protocol;
        struct dhcpv6_session *dhcpv6;
        struct {
@@ -944,6 +1022,7 @@ int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
        intf_init ( &dhcpv6->job, &dhcpv6_job_desc, &dhcpv6->refcnt );
        intf_init ( &dhcpv6->xfer, &dhcpv6_xfer_desc, &dhcpv6->refcnt );
        dhcpv6->netdev = netdev_get ( netdev );
+       memcpy ( &dhcpv6->router, router, sizeof ( dhcpv6->router ) );
        xid = random();
        memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) );
        dhcpv6->start = currticks();