diff mbox series

[ovs-dev,RFC] controller: Always populate prefix-length for IPv6 PD.

Message ID 20240318211252.62591-1-fnordahl@ubuntu.com
State RFC
Headers show
Series [ovs-dev,RFC] controller: Always populate prefix-length for IPv6 PD. | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Frode Nordahl March 18, 2024, 9:12 p.m. UTC
The prefix we obtain is used to fill the ``ipv6_ra_prefixes``
option for configuration of instances through SLAAC.

As discussed in RFC 7421 the interface identifier is 64 bits long,
and client implementations refrain from performing SLAAC with any
other prefix length.

TODO: I'm pretty sure the origin of this behavior is tied to an
      older RFC, attempt to track it down.

The DHCPv6 server is at liberty to offer whichever prefix length it
wants, so make sure new requests are for a prefix length of 64 so
that it is suitable.

TODO: We may need to make the default prefix-length to request
      configurable?  Setting this to 64 fixes an issue I see in my
      network where the server will delegate a /62 by default,
      which does not work well for SLAAC.

A future improvement might be to adjust the requested prefix length
to the number of downstream LRPs we have and then allocate to the
downstream LRPs from that larger delegated prefix.

TODO: We are free to request whatever prefix-length we want, and
      the server is equally free to ignore our request and give us
      something else.  So we need some generic handling of
      allocations where prefix-length < 64.  Perhaps just split
      the prefix up in /64 chunks and add them all to
      ``ipv6_ra_prefixes`` ?

TODO: tests

QUESTION: From cursory view there appears to be an issue with
          routing inside OVN after successful IPv6 Prefix
          Delegation, it may be an issue with my setup, but
          thought I might as well ask.  Is the prefix delegation
          code supposed to automatically handle internal routing?

Signed-off-by: Frode Nordahl <fnordahl@ubuntu.com>
---
 controller/pinctrl.c | 35 ++++++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 11 deletions(-)

Comments

Frode Nordahl March 19, 2024, 9:14 a.m. UTC | #1
On Mon, Mar 18, 2024 at 10:12 PM Frode Nordahl <fnordahl@ubuntu.com> wrote:
>
> The prefix we obtain is used to fill the ``ipv6_ra_prefixes``
> option for configuration of instances through SLAAC.
>
> As discussed in RFC 7421 the interface identifier is 64 bits long,
> and client implementations refrain from performing SLAAC with any
> other prefix length.
>
> TODO: I'm pretty sure the origin of this behavior is tied to an
>       older RFC, attempt to track it down.
>
> The DHCPv6 server is at liberty to offer whichever prefix length it
> wants, so make sure new requests are for a prefix length of 64 so
> that it is suitable.
>
> TODO: We may need to make the default prefix-length to request
>       configurable?  Setting this to 64 fixes an issue I see in my
>       network where the server will delegate a /62 by default,
>       which does not work well for SLAAC.
>
> A future improvement might be to adjust the requested prefix length
> to the number of downstream LRPs we have and then allocate to the
> downstream LRPs from that larger delegated prefix.
>
> TODO: We are free to request whatever prefix-length we want, and
>       the server is equally free to ignore our request and give us
>       something else.  So we need some generic handling of
>       allocations where prefix-length < 64.  Perhaps just split
>       the prefix up in /64 chunks and add them all to
>       ``ipv6_ra_prefixes`` ?
>
> TODO: tests
>
> QUESTION: From cursory view there appears to be an issue with
>           routing inside OVN after successful IPv6 Prefix
>           Delegation, it may be an issue with my setup, but
>           thought I might as well ask.  Is the prefix delegation
>           code supposed to automatically handle internal routing?

I apparently can't quite let go of this itch :)

Responding to my own question, there does appear to be missing
functionality in a few more areas to make IPv6 Prefix Delegation
support complete:
1) We need to include the learned ipv6_prefix when building
lr_in_ip_routing logical flows, something like this does it:

diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index ee5cbcdc3..94ebf6488 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -344,6 +344,22 @@ extract_lrp_networks(const struct
nbrec_logical_router_port *lrp,
         }
     }

+    for (int i = 0; i < lrp->n_ipv6_prefix; i++) {
+        struct in6_addr ip6;
+        unsigned int plen;
+        char *error;
+
+        error = ipv6_parse_cidr(lrp->ipv6_prefix[i], &ip6, &plen);
+        if (!error) {
+            add_ipv6_netaddr(laddrs, ip6, plen);
+        } else {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_INFO_RL(&rl, "invalid syntax '%s' in ipv6_prefix",
+                         lrp->networks[i]);
+            free(error);
+        }
+    }
+

---

I just piggy backed on extract_lrp_networks() here, but it might
deserve its own extract_lrp_delegated_ipv6_prefix() or something like
that for clarity?

2) Traffic is currently allowed to pass through port security,
presumably due to ND/MAC learning, but we should probably let IPAM
know about the learned prefixes so that dynamic addresses can be
properly populated?

3) I need to manually add an external v6 default route, but as a user
I would expect this to be learned through RA when IPv6 Prefix
Delegation is enabled. Maybe we should make this automatic?

> Signed-off-by: Frode Nordahl <fnordahl@ubuntu.com>
> ---
>  controller/pinctrl.c | 35 ++++++++++++++++++++++++-----------
>  1 file changed, 24 insertions(+), 11 deletions(-)
>
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index 721a737de..8c4425a2e 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -1120,9 +1120,7 @@ compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
>      if (pfd->uuid.len) {
>          payload += pfd->uuid.len + sizeof(struct dhcpv6_opt_header);
>      }
> -    if (ipv6_addr_is_set(&pfd->prefix)) {
> -        payload += sizeof(struct dhcpv6_opt_ia_prefix);
> -    }
> +    payload += sizeof(struct dhcpv6_opt_ia_prefix);
>
>      eth_compose(b, (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02), pfd->ea,
>                  ETH_TYPE_IPV6, IPV6_HEADER_LEN);
> @@ -1174,23 +1172,38 @@ compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
>      ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
>      int opt_len = sizeof(struct dhcpv6_opt_ia_na) -
>                    sizeof(struct dhcpv6_opt_header);
> -    if (ipv6_addr_is_set(&pfd->prefix)) {
> -        opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
> -    }
> +    opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
>      ia_pd->opt.len = htons(opt_len);
>      ia_pd->iaid = htonl(pfd->aid);
>      ia_pd->t1 = OVS_BE32_MAX;
>      ia_pd->t2 = OVS_BE32_MAX;
> +    struct dhcpv6_opt_ia_prefix *ia_prefix =
> +        (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
> +    ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
> +    ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
> +                               sizeof(struct dhcpv6_opt_header));
>      if (ipv6_addr_is_set(&pfd->prefix)) {
> -        struct dhcpv6_opt_ia_prefix *ia_prefix =
> -            (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
> -        ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
> -        ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
> -                                   sizeof(struct dhcpv6_opt_header));
>          ia_prefix->plife_time = OVS_BE32_MAX;
>          ia_prefix->vlife_time = OVS_BE32_MAX;
>          ia_prefix->plen = pfd->plen;
>          ia_prefix->ipv6 = pfd->prefix;
> +    } else {
> +        /* The prefix we obtain is used to fill the ``ipv6_ra_prefixes``
> +         * option for configuration of instances through SLAAC.
> +         *
> +         * As discussed in RFC 7421 the interface identifier is 64 bits long,
> +         * and client implementations refrain from performing SLAAC with any
> +         * other prefix length.
> +         *
> +         * The DHCPv6 server is at liberty to offer whichever prefix length it
> +         * wants, so make sure new requests are for a prefix length of 64 so
> +         * that it is suitable.
> +         *
> +         * A future improvement might be to adjust the requested prefix length
> +         * to the number of downstream LRPs we have and then allocate to the
> +         * downstream LRPs from that larger delegated prefix. */
> +        ia_prefix->ipv6 = in6addr_any;
> +        ia_prefix->plen = 64;
>      }
>
>      uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(b));
> --
> 2.43.0
>
Dumitru Ceara April 12, 2024, 11:29 a.m. UTC | #2
On 3/19/24 10:14, Frode Nordahl wrote:
> On Mon, Mar 18, 2024 at 10:12 PM Frode Nordahl <fnordahl@ubuntu.com> wrote:
>>

Hi Frode,

Sorry for taking so long to reply.

>> The prefix we obtain is used to fill the ``ipv6_ra_prefixes``
>> option for configuration of instances through SLAAC.
>>
>> As discussed in RFC 7421 the interface identifier is 64 bits long,
>> and client implementations refrain from performing SLAAC with any
>> other prefix length.
>>
>> TODO: I'm pretty sure the origin of this behavior is tied to an
>>       older RFC, attempt to track it down.
>>
>> The DHCPv6 server is at liberty to offer whichever prefix length it
>> wants, so make sure new requests are for a prefix length of 64 so
>> that it is suitable.
>>
>> TODO: We may need to make the default prefix-length to request
>>       configurable?  Setting this to 64 fixes an issue I see in my
>>       network where the server will delegate a /62 by default,
>>       which does not work well for SLAAC.
>>
>> A future improvement might be to adjust the requested prefix length
>> to the number of downstream LRPs we have and then allocate to the
>> downstream LRPs from that larger delegated prefix.
>>
>> TODO: We are free to request whatever prefix-length we want, and
>>       the server is equally free to ignore our request and give us
>>       something else.  So we need some generic handling of
>>       allocations where prefix-length < 64.  Perhaps just split
>>       the prefix up in /64 chunks and add them all to
>>       ``ipv6_ra_prefixes`` ?
>>
>> TODO: tests
>>
>> QUESTION: From cursory view there appears to be an issue with
>>           routing inside OVN after successful IPv6 Prefix
>>           Delegation, it may be an issue with my setup, but
>>           thought I might as well ask.  Is the prefix delegation
>>           code supposed to automatically handle internal routing?
> 
> I apparently can't quite let go of this itch :)
> 

I'm not aware of this feature being used actively in production (I might
be wrong of course) so maybe that's why there are these loose ends.  We
should probably fix them though, especially if it's interesting for your
own usage.

> Responding to my own question, there does appear to be missing
> functionality in a few more areas to make IPv6 Prefix Delegation
> support complete:
> 1) We need to include the learned ipv6_prefix when building
> lr_in_ip_routing logical flows, something like this does it:
> 
> diff --git a/lib/ovn-util.c b/lib/ovn-util.c
> index ee5cbcdc3..94ebf6488 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -344,6 +344,22 @@ extract_lrp_networks(const struct
> nbrec_logical_router_port *lrp,
>          }
>      }
> 
> +    for (int i = 0; i < lrp->n_ipv6_prefix; i++) {
> +        struct in6_addr ip6;
> +        unsigned int plen;
> +        char *error;
> +
> +        error = ipv6_parse_cidr(lrp->ipv6_prefix[i], &ip6, &plen);
> +        if (!error) {
> +            add_ipv6_netaddr(laddrs, ip6, plen);
> +        } else {
> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> +            VLOG_INFO_RL(&rl, "invalid syntax '%s' in ipv6_prefix",
> +                         lrp->networks[i]);
> +            free(error);
> +        }
> +    }
> +
> 
> ---
> 
> I just piggy backed on extract_lrp_networks() here, but it might
> deserve its own extract_lrp_delegated_ipv6_prefix() or something like
> that for clarity?
> 

Maybe, given that the rest of the network configuration doesn't change.

> 2) Traffic is currently allowed to pass through port security,
> presumably due to ND/MAC learning, but we should probably let IPAM
> know about the learned prefixes so that dynamic addresses can be
> properly populated?
> 

Do you mean the DHCPv6 traffic or traffic using those IPs?  If it's the
latter it sounds a bit weird to me.  I'd expect indeed that we need to
inform IPAM about the learned prefixes.

> 3) I need to manually add an external v6 default route, but as a user
> I would expect this to be learned through RA when IPv6 Prefix
> Delegation is enabled. Maybe we should make this automatic?
> 

Sounds reasonable to me.

>> Signed-off-by: Frode Nordahl <fnordahl@ubuntu.com>
>> ---
>>  controller/pinctrl.c | 35 ++++++++++++++++++++++++-----------
>>  1 file changed, 24 insertions(+), 11 deletions(-)
>>
>> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
>> index 721a737de..8c4425a2e 100644
>> --- a/controller/pinctrl.c
>> +++ b/controller/pinctrl.c
>> @@ -1120,9 +1120,7 @@ compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
>>      if (pfd->uuid.len) {
>>          payload += pfd->uuid.len + sizeof(struct dhcpv6_opt_header);
>>      }
>> -    if (ipv6_addr_is_set(&pfd->prefix)) {
>> -        payload += sizeof(struct dhcpv6_opt_ia_prefix);
>> -    }
>> +    payload += sizeof(struct dhcpv6_opt_ia_prefix);
>>
>>      eth_compose(b, (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02), pfd->ea,
>>                  ETH_TYPE_IPV6, IPV6_HEADER_LEN);
>> @@ -1174,23 +1172,38 @@ compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
>>      ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
>>      int opt_len = sizeof(struct dhcpv6_opt_ia_na) -
>>                    sizeof(struct dhcpv6_opt_header);
>> -    if (ipv6_addr_is_set(&pfd->prefix)) {
>> -        opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
>> -    }
>> +    opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
>>      ia_pd->opt.len = htons(opt_len);
>>      ia_pd->iaid = htonl(pfd->aid);
>>      ia_pd->t1 = OVS_BE32_MAX;
>>      ia_pd->t2 = OVS_BE32_MAX;
>> +    struct dhcpv6_opt_ia_prefix *ia_prefix =
>> +        (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
>> +    ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
>> +    ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
>> +                               sizeof(struct dhcpv6_opt_header));
>>      if (ipv6_addr_is_set(&pfd->prefix)) {
>> -        struct dhcpv6_opt_ia_prefix *ia_prefix =
>> -            (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
>> -        ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
>> -        ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
>> -                                   sizeof(struct dhcpv6_opt_header));
>>          ia_prefix->plife_time = OVS_BE32_MAX;
>>          ia_prefix->vlife_time = OVS_BE32_MAX;
>>          ia_prefix->plen = pfd->plen;
>>          ia_prefix->ipv6 = pfd->prefix;
>> +    } else {
>> +        /* The prefix we obtain is used to fill the ``ipv6_ra_prefixes``
>> +         * option for configuration of instances through SLAAC.
>> +         *
>> +         * As discussed in RFC 7421 the interface identifier is 64 bits long,
>> +         * and client implementations refrain from performing SLAAC with any
>> +         * other prefix length.
>> +         *
>> +         * The DHCPv6 server is at liberty to offer whichever prefix length it
>> +         * wants, so make sure new requests are for a prefix length of 64 so
>> +         * that it is suitable.
>> +         *
>> +         * A future improvement might be to adjust the requested prefix length
>> +         * to the number of downstream LRPs we have and then allocate to the
>> +         * downstream LRPs from that larger delegated prefix. */
>> +        ia_prefix->ipv6 = in6addr_any;
>> +        ia_prefix->plen = 64;
>>      }
>>
>>      uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(b));

Regards,
Dumitru
diff mbox series

Patch

diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 721a737de..8c4425a2e 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -1120,9 +1120,7 @@  compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
     if (pfd->uuid.len) {
         payload += pfd->uuid.len + sizeof(struct dhcpv6_opt_header);
     }
-    if (ipv6_addr_is_set(&pfd->prefix)) {
-        payload += sizeof(struct dhcpv6_opt_ia_prefix);
-    }
+    payload += sizeof(struct dhcpv6_opt_ia_prefix);
 
     eth_compose(b, (struct eth_addr) ETH_ADDR_C(33,33,00,01,00,02), pfd->ea,
                 ETH_TYPE_IPV6, IPV6_HEADER_LEN);
@@ -1174,23 +1172,38 @@  compose_prefixd_packet(struct dp_packet *b, struct ipv6_prefixd_state *pfd)
     ia_pd->opt.code = htons(DHCPV6_OPT_IA_PD);
     int opt_len = sizeof(struct dhcpv6_opt_ia_na) -
                   sizeof(struct dhcpv6_opt_header);
-    if (ipv6_addr_is_set(&pfd->prefix)) {
-        opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
-    }
+    opt_len += sizeof(struct dhcpv6_opt_ia_prefix);
     ia_pd->opt.len = htons(opt_len);
     ia_pd->iaid = htonl(pfd->aid);
     ia_pd->t1 = OVS_BE32_MAX;
     ia_pd->t2 = OVS_BE32_MAX;
+    struct dhcpv6_opt_ia_prefix *ia_prefix =
+        (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
+    ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
+    ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
+                               sizeof(struct dhcpv6_opt_header));
     if (ipv6_addr_is_set(&pfd->prefix)) {
-        struct dhcpv6_opt_ia_prefix *ia_prefix =
-            (struct dhcpv6_opt_ia_prefix *)(ia_pd + 1);
-        ia_prefix->opt.code = htons(DHCPV6_OPT_IA_PREFIX);
-        ia_prefix->opt.len = htons(sizeof(struct dhcpv6_opt_ia_prefix) -
-                                   sizeof(struct dhcpv6_opt_header));
         ia_prefix->plife_time = OVS_BE32_MAX;
         ia_prefix->vlife_time = OVS_BE32_MAX;
         ia_prefix->plen = pfd->plen;
         ia_prefix->ipv6 = pfd->prefix;
+    } else {
+        /* The prefix we obtain is used to fill the ``ipv6_ra_prefixes``
+         * option for configuration of instances through SLAAC.
+         *
+         * As discussed in RFC 7421 the interface identifier is 64 bits long,
+         * and client implementations refrain from performing SLAAC with any
+         * other prefix length.
+         *
+         * The DHCPv6 server is at liberty to offer whichever prefix length it
+         * wants, so make sure new requests are for a prefix length of 64 so
+         * that it is suitable.
+         *
+         * A future improvement might be to adjust the requested prefix length
+         * to the number of downstream LRPs we have and then allocate to the
+         * downstream LRPs from that larger delegated prefix. */
+        ia_prefix->ipv6 = in6addr_any;
+        ia_prefix->plen = 64;
     }
 
     uint32_t csum = packet_csum_pseudoheader6(dp_packet_l3(b));