Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DuplicateAddressDetection settings for systemd-networkd (LP: #1959190) #525

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sanecz
Copy link

@sanecz sanecz commented Oct 19, 2024

Description

Adding DuplicateAddressDetection parameter to be able to configure the DAD as by default it is enabled for ipv6 and ipv4ll.

It should kinda close LP#1959190 as it would provide a way to configure it, but not setting any default value to avoid breaking network configurations.

Checklist

  • Runs make check successfully.
  • Retains code coverage (make check-coverage).
  • New/changed keys in YAML format are documented.
  • (Optional) Adds example YAML for new feature.
  • (Optional) Closes an open bug in Launchpad.

  * implement configuration key duplicate-address-detection
  * documentation of the parameter
  * adding a few tests
@slyon slyon added community This PR has been proposed by somebody outside of the Netplan team and roadmap commitments. schema change This PR includes a change to netplan's YAML schema, which needs sign-off labels Oct 23, 2024
@slyon slyon self-requested a review October 23, 2024 14:47
@slyon slyon changed the title Add DuplicateAddressDetection settings for systemd-networkd (LP#1959190) Add DuplicateAddressDetection settings for systemd-networkd (LP: #1959190) Oct 24, 2024
Copy link
Collaborator

@slyon slyon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @sanecz Thank you very much for your high quality contribution to Netplan and for tackling this long-standing feature request in LP#1959190!

This PR is already pretty good, as you adopted many of the required parts of Netplan to make it work, like:

  • networkd.c backend renderer
  • netplan.c YAML emitter
  • unit tests
  • Python bindings
  • Fuzzer schemas
  • passes all CI checks

As a non-blocking requirement, we should also consider adding:

  • nm.c (NetworkManager) backend renderer
  • Integration test(s), e.g. in tests/integration/ethernets.py

Before we get into the implementation details, I'd like to talk about the new parsing logic and interfaces that we will expose to our users. And also the data structures for storage. I don't think the DAD-per-address is the optimal solution here. Also, we should probably choose something more specific than a string (char*) to store the data internally.

Generally, I like the duplicate-address-detection naming. There's also the notion of "IPv4 Address Conflict Detection" (ACD) [RFC 5227], but I have the impression that "Duplicate Address Detection" (DAD) is used commonly across IPv4 & IPv6 these days. See:

Besides that, I think the new duplicate-address-detection setting should move to the per-NetDef/interface level. As an "ipv6" or "both" setting doesn't make sense for a specific IPv4 address and vice versa. Furthermore, NetworkManager apparently only allows defining this per-connection, not per-address via its ipv4/6.dad-timeout settings.

So I'd suggest the following schema:

ethernets:
  eth0:
    addresses: [169.254.0.13/16]
    dhcp6: true
    duplicate-address-detection: [ipv4-ll, ipv6] # could be [ipv4], [ipv6], [ipv4, ipv6], or the empty list "[]"

The default could be implicitly represented as [ipv4-ll, ipv6], as that is what networkd uses today.

What do you think about those suggestions?
I'll also ask for feedback from our architect @vorlonofportland about this.

const mapping_entry_handler address_option_handlers[] = {
{"lifetime", YAML_SCALAR_NODE, {.generic=handle_address_option_lifetime}, addr_option_offset(lifetime)},
{"label", YAML_SCALAR_NODE, {.generic=handle_address_option_label}, addr_option_offset(label)},
{"duplicate-address-detection", YAML_SCALAR_NODE, {.generic=handle_address_option_duplicate_address_detection}, addr_option_offset(duplicate_address_detection)},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this setting should move to COMMON_LINK_HANDLERS.

For network-manager, we can only control it on a per ipv4/ipv6 level:

Also, when defining it directly on a IPv4 address only none or ipv4 makes sense (boolean effectively, maybe "ipv4-ll" would be a special case), while when defining it directly on a IPv6 address, only none (false) or ipv6(true) makes sense. So I think it should be a per NetDef/interface setting, that way it could also describe dynamic addresses, like DHCP or IPv6RA.

g_ascii_strcasecmp(scalar(node), "none") != 0) {
return yaml_error(npp, node, error, "invalid duplicate-address-detection value '%s'", scalar(node));
}
return handle_generic_str(npp, node, npp->current.addr_options, data, error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should rather store this in a guint and represent it as an enum... Maybe something along those lines?

typedef enum {
    NETPLAN_DAD_UNSET = 0,
    NETPLAN_DAD_BOTH = 1<<0,
    NETPLAN_DAD_NONE = 1<<1,
    NETPLAN_DAD_IPV4LL = 1<<2,
    NETPLAN_DAD_IPV4 = 1<<3,
    NETPLAN_DAD_IPV6 = 1<<4,
    NETPLAN_DAD_MAX_,
} NetplanDadFlag;

The current (networkd) default could then be reflected as NETPLAN_DAD_IPV4LL | NETPLAN_DAD_IPV6

More flags could be added as needed in the future and the backend renderers (networkd.c / nm.c) would be able to handle only the flags that they actually understand individually, writing out the corresponding per-address or per-connection settings.

@@ -422,6 +422,12 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
> In addition to the addresses themselves one can specify configuration
> parameters as mappings. Current supported options are:

- **`duplicate-address-detection`** (scalar)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the duplicate-address-detection naming. There's also the notion of "IPv4 Address Conflict Detection" (ACD) [RFC 5227], but I have the impression that "Duplicate Address Detection" (DAD) is used commonly across IPV4 & IPv6 these days.

See

Comment on lines +427 to +429
> Configure the duplicate address detection (DAD). Valid options
> are `ipv4`, `ipv6`, `both` or `none`. Currently supported on the
> networkd back end only.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently a direct copy of the networkd settings. I'd rather like to represent this as a sequence/list of flags, though. Maybe on the global level instead of per-address.

e.g.

ethernets:
  eth0:
    addresses: [169.254.0.13/16]
    dhcp6: true
    duplicate-address-detection: [ipv4-ll, ipv6]

The current default could be represented as [ipv4-ll, ipv6], we could have "both" [ipv4, ipv6] or "none" []. ipv4 should probably include ipv4-ll implicitly.

On a per-IP level it does not alway make sense, or is there any use case for this? E.g:

ethernets:
  eth0:
    addresses:
    - 169.254.0.13/16:
      duplicate-address-detection: "ipv6"
    dhcp6: true

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a per-IP level it does not alway make sense, or is there any use case for this? E.g: [...]

I don't think this it makes sense too to have this configuration

But thinking of weird corner cases, I don't know why someone would do that, but it still is a valid configuration for networkd, that cannot be reproduced with nm afaik.

ethernets:
  eth0:
    addresses:
    - 172.16.0.1/24:
      duplicate-address-detection: "ipv4"
    - 169.254.0.13/16:
      duplicate-address-detection: "none"
    dhcp6: true

@slyon
Copy link
Collaborator

slyon commented Oct 24, 2024

We might also consider enabling this feature by default, similarly as it was done in NetworkManager by Fedora 40: https://fedoraproject.org/wiki/Changes/Enable_IPv4_Address_Conflict_Detection

E.g. defaulting to duplicate-address-detection: [ipv4, ipv6] (instead of [ipv4-ll, ipv6]). This would be a breaking change that we'd need to announce in the release notes and maybe relax for backports of the new version. But I still think it might be a feasible approach, as not having DAD enable could lead to broken network configurations, which isn't any better.

@@ -422,6 +422,12 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
> In addition to the addresses themselves one can specify configuration
> parameters as mappings. Current supported options are:

- **`duplicate-address-detection`** (scalar)
Copy link
Collaborator

@slyon slyon Oct 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (non-blocking):

Suggested change
- **`duplicate-address-detection`** (scalar)
- **`duplicate-address-detection`** (scalar) – since 1.2

@sanecz
Copy link
Author

sanecz commented Nov 7, 2024

Hi @slyon

Thank you for your feedback, really appreciate it !

As a non-blocking requirement, we should also consider adding:
nm.c (NetworkManager) backend renderer

For the NetworkManager render, as I do not have much knowledge on it, I'll take a little time to explore and experiment configurations for it.

Integration test(s), e.g. in tests/integration/ethernets.py

Sure I'll take a look and implement it.

[...] Also, we should probably choose something more specific than a string (char*) to store the data internally.

Agreed, as you mentioned on a later comment, an enum would do perfectly the job.

As an "ipv6" or "both" setting doesn't make sense for a specific IPv4 address and vice versa.

I entirely agree.
What is the scope of netplan when dealing with configuration that is syntactically correct but does not makes sense ? Should it be the responsibility of the choosen network backend to check, or should netplan do some coherence checks ? [1]
Also 'both' might come handy for automation if you don't want to check the type of the IP when writing the configuration (clearly what I did too on the network configurations of my servers without thinking too much ;D)

[...] I don't think the DAD-per-address is the optimal solution here.
[...] Besides that, I think the new duplicate-address-detection setting should move to the per-NetDef/interface level. [..]
Furthermore, NetworkManager apparently only allows defining this per-connection, not per-address via its ipv4/6.dad-timeout settings.

It seems that systemd-networkd and NetworkManager do really differs on the way of configuring it, as networkd mentions DAD configuration only on the address section option and there is only one address declared by section, and not much more, while with networkmanager as you previously mentioned, to enable the dad per interfaces it is enabled by setting the dad-timeout to a value > 0 for ipv4 and ipv6. So it's not easy to think about a one-fit-both configuration. [2]

This also means that another (optional) configuration key only available for nm should exists to set the timeout instead of a fixed value. The networkd configuration does not seem to have an equivalence. (I think only the number of probes sent can be changed)

duplicate-address-detection: [ipv4-ll, ipv6] # could be [ipv4], [ipv6], [ipv4, ipv6], or the empty list "[]"

True, this schema seems clearer than setting directly the networkd keywords, especially if nm is supported too !

We might also consider enabling this feature by default, similarly as it was done in NetworkManager by Fedora 40
E.g. defaulting to duplicate-address-detection: [ipv4, ipv6] (instead of [ipv4-ll, ipv6]). This would be a breaking change that we'd need to announce in the release notes and maybe relax for backports of the new version. But I still think it might be a feasible approach, as not having DAD enable could lead to broken network configurations, which isn't any better.

On purpose I didn't set a default value to use the default behavior of the network backend thus avoiding issues for some configurations too (and some people that might ask why it takes few seconds longer to up the network too).

--

Howerver, as mentioned on [1], if netplan does coherence check, we can manage to check the family of the ip and set the only proper configuration keys per address/interface or throw an error or a warning.

About the setting [2], I'm still not sure what could be the best idea:

  • if we set the configuration per netdef/interface, we loose some of the flexibility of systemd-networkd (probably only for unusual configurations or some cases I can't really think about ?)
  • if we set the configuration per address, someone using nm could set on an addres "duplicate-address-detection: none" and on another address "duplicate-address-detection: ipv4" and I don't really see how can we generate a configuration for this case.
  • support both models, disable the per address for nm

I'm looking forward to hearing your ideas !

@slyon
Copy link
Collaborator

slyon commented Nov 11, 2024

Hi @slyon

Thank you for your feedback, really appreciate it !

As a non-blocking requirement, we should also consider adding:
nm.c (NetworkManager) backend renderer

For the NetworkManager render, as I do not have much knowledge on it, I'll take a little time to explore and experiment configurations for it.

As mentioned, I see this as non-blocking. It's fine to focus on systemd-networkd for now. The implementation for NetworkManager could then be a follow-up PR.

Integration test(s), e.g. in tests/integration/ethernets.py

Sure I'll take a look and implement it.

Thanks!

[...] Also, we should probably choose something more specific than a string (char*) to store the data internally.

Agreed, as you mentioned on a later comment, an enum would do perfectly the job.

As an "ipv6" or "both" setting doesn't make sense for a specific IPv4 address and vice versa.

I entirely agree. What is the scope of netplan when dealing with configuration that is syntactically correct but does not makes sense ? Should it be the responsibility of the choosen network backend to check, or should netplan do some coherence checks ? [1]

IMO coherence checks should happen on the highest layer (e.g. inside Netplan), we don't want our users to dig down into the stack, to understand that they described an invalid configuration. We should make it obvious from the very start.
In Netplan we have the src/validation.c stage which can be used for this. When some configuration is invalid, but not harmful, it might just log a warning (probably the case here). OTOH if Netplan can already tell that an invalid configuration will not work, it should error out from the validation stage.

Also 'both' might come handy for automation if you don't want to check the type of the IP when writing the configuration (clearly what I did too on the network configurations of my servers without thinking too much ;D)

Sure, but I think we could cover this with the schema I suggested above, e.g.: duplicate-address-detection: [ipv4, ipv6]

[...] I don't think the DAD-per-address is the optimal solution here.
[...] Besides that, I think the new duplicate-address-detection setting should move to the per-NetDef/interface level. [..]
Furthermore, NetworkManager apparently only allows defining this per-connection, not per-address via its ipv4/6.dad-timeout settings.

It seems that systemd-networkd and NetworkManager do really differs on the way of configuring it, as networkd mentions DAD configuration only on the address section option and there is only one address declared by section, and not much more, while with networkmanager as you previously mentioned, to enable the dad per interfaces it is enabled by setting the dad-timeout to a value > 0 for ipv4 and ipv6. So it's not easy to think about a one-fit-both configuration. [2]

This also means that another (optional) configuration key only available for nm should exists to set the timeout instead of a fixed value. The networkd configuration does not seem to have an equivalence. (I think only the number of probes sent can be changed)

Yes, unfortunately there are many such nuanced difference between networking backends. With Netplan we try unify them in a best effort approach. For this DAD case, I could think of setting NM's ipv4/6.dad-timeout to something like 200ms when [ipv4/6] gets enabled through Netplan. Logging a warning when ipv4-ll is select, which we cannot clearly map to NetworkManager. The users would then still have the ability to override this default, using networkmanager.passthrough.ipv4.data-timeout settings.

duplicate-address-detection: [ipv4-ll, ipv6] # could be [ipv4], [ipv6], [ipv4, ipv6], or the empty list "[]"

True, this schema seems clearer than setting directly the networkd keywords, especially if nm is supported too !

Thanks! I've also got +1 from Steve, our architect, about this (after some back channel discussions). So let's go with that.
We just need to make sure all the options are clearly documented in doc/netplan-yaml.md (e.g. ipv4-ll is also valid by itself, ipv4 is a superset, including "ipv4-ll").

We might also consider enabling this feature by default, similarly as it was done in NetworkManager by Fedora 40
E.g. defaulting to duplicate-address-detection: [ipv4, ipv6] (instead of [ipv4-ll, ipv6]). This would be a breaking change that we'd need to announce in the release notes and maybe relax for backports of the new version. But I still think it might be a feasible approach, as not having DAD enable could lead to broken network configurations, which isn't any better.

On purpose I didn't set a default value to use the default behavior of the network backend thus avoiding issues for some configurations too (and some people that might ask why it takes few seconds longer to up the network too).

I agree. Let's try not to change any default for now. I'd suggest going with [ipv4-ll, ipv6] still, as that reflects the current behaviour. On NM this might potentially translate to ipv4.dad-timeout=-1 (as we cannot reflect "ipv4-ll" there) & ipv6.dad-timeout=200 (or maybe a better default timeout).

--

Howerver, as mentioned on [1], if netplan does coherence check, we can manage to check the family of the ip and set the only proper configuration keys per address/interface or throw an error or a warning.

About the setting [2], I'm still not sure what could be the best idea:

  • if we set the configuration per netdef/interface, we loose some of the flexibility of systemd-networkd (probably only for unusual configurations or some cases I can't really think about ?)
  • if we set the configuration per address, someone using nm could set on an addres "duplicate-address-detection: none" and on another address "duplicate-address-detection: ipv4" and I don't really see how can we generate a configuration for this case.
  • support both models, disable the per address for nm

I'm looking forward to hearing your ideas !

Right, there's a balance to strike here. With the proposed duplicate-address-detection: [ipv4-ll, ipv6] # could also be [ipv4], [ipv6], [ipv4, ipv6], or the empty list "[]" schema we're doing pretty good, IMO. It allows for some granularity on IP-address-type/range and we would also be able to extend it with more values in the future, e.g. "ipv6-ll", "ipv4-no-ll", ... (if needed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
community This PR has been proposed by somebody outside of the Netplan team and roadmap commitments. schema change This PR includes a change to netplan's YAML schema, which needs sign-off schema ok
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants