Design doc for IPv6 support
Add a document describing a design for comprehensive IPv6 support. The ddclient maintainers agreed to this design, and it should be used to guide the implementation of full IPv6 support.
This commit is contained in:
parent
58c6570dde
commit
d31d9f8bde
1 changed files with 337 additions and 0 deletions
337
docs/ipv6-design-doc.md
Normal file
337
docs/ipv6-design-doc.md
Normal file
|
@ -0,0 +1,337 @@
|
||||||
|
# Design Doc: IPv6 Support
|
||||||
|
|
||||||
|
Author: [@rhansen](https://github.com/rhansen/)\
|
||||||
|
Date: 2020-06-09\
|
||||||
|
Signed off by:
|
||||||
|
[@SuperSandro2000](https://github.com/SuperSandro2000/)
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
|
||||||
|
Add full IPv6 support to ddclient, including support for dual-stack
|
||||||
|
systems.
|
||||||
|
|
||||||
|
## Background
|
||||||
|
|
||||||
|
ddclient's current IPv6 support is limited:
|
||||||
|
|
||||||
|
* Users can update either an IPv6 record or an IPv4 record for a
|
||||||
|
host, not both.
|
||||||
|
* If SSL is used for an HTTP request, IPv6 will be used if the
|
||||||
|
remote host has a AAAA record, even if the user would rather use
|
||||||
|
IPv4. This breaks `use=web` for IPv4 if the `web` URL's host has a
|
||||||
|
AAAA record.
|
||||||
|
* The `use=if` method only works if the user sets `if-skip` to
|
||||||
|
something that skips over all IPv4 addresses in the output of
|
||||||
|
`ifconfig` (or `ip`). If the output contains an IPv4 address after
|
||||||
|
the IPv6 address then `use=if` cannot be used for IPv6.
|
||||||
|
* There is no support for falling back to IPv4 if an IPv6 connection
|
||||||
|
fails.
|
||||||
|
* `use=if` does not filter out locally scoped or temporary IPv6
|
||||||
|
addresses.
|
||||||
|
|
||||||
|
Some attempts have been made to add more robust IPv6 support:
|
||||||
|
|
||||||
|
* Debian's ddclient package applies a
|
||||||
|
[patch](https://salsa.debian.org/debian/ddclient/-/blob/67a138aa3d98d70f01766123f58ef40e98693fd4/debian/patches/usev6.diff)
|
||||||
|
that adds a new `usev6` option. The `usev6` option can be set to
|
||||||
|
`ip` or `if`, but not any of the other strategies currently
|
||||||
|
available for the `use` option (`web`, `cmd`, `fw`, `cisco`,
|
||||||
|
`cisco-asa`). When set to `ip` or `if`, only IPv6 addresses are
|
||||||
|
considered; IPv4 addresses are ignored. The patch does not change
|
||||||
|
the behavior of the `use` option, so `use=web` or `use=cmd` can be
|
||||||
|
used for IPv6 if pointed at something that only outputs an IPv6
|
||||||
|
address.
|
||||||
|
* [ddclient-curl](https://github.com/astlinux-project/ddclient-curl)
|
||||||
|
is a fork of ddclient that uses curl as the HTTP client (instead
|
||||||
|
of ddclient's own homemade client) for more robust IPv6 support.
|
||||||
|
* PR #40 is perhaps the most comprehensive attempt at adding full
|
||||||
|
IPv6 support, but it was never merged and has since
|
||||||
|
bit-rotted. There is renewed effort to rebase the changes and get
|
||||||
|
them merged in. PR #40 adds new options and changes some existing
|
||||||
|
options. The approach taken is to completely isolate IPv4 address
|
||||||
|
detection from IPv6 address detection and require the update
|
||||||
|
protocol callbacks to handle each type of address appropriately.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* The mechanism for determining the current IPv4 address (the `use`
|
||||||
|
option) must be independently configurable from the mechanism used
|
||||||
|
to determine the current IPv6 address.
|
||||||
|
* The user must be able to disable IPv4 address updates without
|
||||||
|
affecting IPv6 updates.
|
||||||
|
* The user must be able to disable IPv6 address updates without
|
||||||
|
affecting IPv4 updates.
|
||||||
|
* If HTTP polling is used for both IPv4 and IPv6 address discovery,
|
||||||
|
the URL used to determine the IPv4 address (the `web` option) must
|
||||||
|
be independently configurable from the URL used to determine the
|
||||||
|
IPv6 address.
|
||||||
|
* The use of IPv4 or IPv6 to update a record must be independent of
|
||||||
|
the type of record being updated (IPv4 or IPv6).
|
||||||
|
* The callback for the update protocol must be given both addresses,
|
||||||
|
even if only one of the two addresses has changed.
|
||||||
|
* The callback for the update protocol must be told which addresses
|
||||||
|
have changed.
|
||||||
|
* There must be IPv6 equivalents to `use=ip`, `use=if`, `use=web`,
|
||||||
|
and `use=cmd`. For the IPv6 equivalent to `use=if`, it is
|
||||||
|
acceptable to ignore non-global and temporary addresses (the user
|
||||||
|
can always use the IPv6 equivalent to `use=cmd` to get non-global
|
||||||
|
or temporary addresses).
|
||||||
|
* Existing support for updating IPv6 records must not be lost.
|
||||||
|
* Some dynamic DNS service providers use separate credentials for
|
||||||
|
the IPv4 and IPv6 records. These providers must be supported,
|
||||||
|
either by accepting both sets of credentials in a single host's
|
||||||
|
configuration or by allowing the user to specify the same host
|
||||||
|
twice, once for IPv4 and once for IPv6.
|
||||||
|
|
||||||
|
### Nice-to-Haves
|
||||||
|
|
||||||
|
* The user should be able to force the update protocol to use IPv4
|
||||||
|
or IPv6.
|
||||||
|
* Unless configured otherwise, ddclient should first attempt to
|
||||||
|
update via IPv6 and fall back to IPv4 if the IPv6 connection
|
||||||
|
fails. This behavior can be added later; for now it is acceptable
|
||||||
|
to keep the current behavior (use IPv6 without IPv4 fallback if
|
||||||
|
there is a AAAA record, use IPv4 if there is no AAAA record).
|
||||||
|
* Full backwards compatibility with existing config files and
|
||||||
|
flags. The trade-offs between migration burden, long-term
|
||||||
|
usability, and code maintenance should be carefully considered.
|
||||||
|
* IPv6 equivalents to `use=fw`, `use=cisco`, and `use=cisco-asa`.
|
||||||
|
* Add IPv6 support in protocol callbacks where IPv6 support is
|
||||||
|
currently missing. (This can be done later.)
|
||||||
|
|
||||||
|
## Proposal
|
||||||
|
|
||||||
|
### Configuration changes
|
||||||
|
|
||||||
|
* Add new `usev4` and `usev6` settings that are like the current
|
||||||
|
`use` setting except they only apply to IPv4 and IPv6,
|
||||||
|
respectively.
|
||||||
|
* `usev4` can be set to one of the following values: `disabled`,
|
||||||
|
`ipv4`, `webv4`, `fwv4`, `ifv4`, `cmdv4`, `ciscov4`,
|
||||||
|
`cisco-asav4`
|
||||||
|
* `usev6` can be set to one of the following values: `disabled`,
|
||||||
|
`ipv6`, `webv6`, `fwv6`, `ifv6`, `cmdv6`, `ciscov6`,
|
||||||
|
`cisco-asav6`
|
||||||
|
* Add a new `use` strategy: `disabled`.
|
||||||
|
* The `disabled` value for `use`, `usev4`, and `usev6` causes
|
||||||
|
ddclient to act as if it was never set. This is useful for
|
||||||
|
overriding the global value for a particular host.
|
||||||
|
* For compatibility with ddclient-curl, `no` is a deprecated alias
|
||||||
|
of `disabled`.
|
||||||
|
* Add new `ipv4`, `ipv6`, `webv4`, `webv4-skip`, `webv6`,
|
||||||
|
`webv6-skip`, `ifv4`, `ifv6`, `cmdv4`, `cmdv6`, etc. settings that
|
||||||
|
behave like their versionless counterparts except they only apply
|
||||||
|
to IPv4 or IPv6. Deprecate the versionless counterparts, and
|
||||||
|
change their behavior so that they also influence the default
|
||||||
|
value of the versioned options. (Example: Suppose
|
||||||
|
`usev4=ifv4`. If `ifv4` is not set then `if` is used.) Special
|
||||||
|
notes:
|
||||||
|
* The value of `ip` will only serve as the default for `ipv4`
|
||||||
|
(or `ipv6`) if it contains an IPv4 (or IPv6) address.
|
||||||
|
* There is currently an `ipv6` boolean setting. To preserve
|
||||||
|
backward compatibility with existing configs, `ipv6` set to a
|
||||||
|
boolean value is ignored (other than a warning).
|
||||||
|
* There is no `ifv4-skip` or `ifv6-skip` because it's ddclient's
|
||||||
|
responsibility to properly parse the output of whatever tool
|
||||||
|
it uses to read the interface's addresses.
|
||||||
|
* For now there is no `cmdv4-skip` or `cmdv6-skip`. Anyone who
|
||||||
|
already knows how to write a regular expression can probably
|
||||||
|
write a wrapper script. These may be added in the future if
|
||||||
|
users request them, especially if it facilitates migration
|
||||||
|
away from the deprecated `cmd-skip` setting.
|
||||||
|
* For `usev6=ifv6`, interfaces are likely to have several IPv6
|
||||||
|
addresses (unlike IPv4). Choosing the "right" IPv6 address is
|
||||||
|
not trivial. Fortunately, we don't have to solve this
|
||||||
|
perfectly right now; we can choose something that mostly
|
||||||
|
works and let user bug reports guide future refinements. For
|
||||||
|
the first iteration, we will try the following:
|
||||||
|
* Ignore addresses that are not global unicast.
|
||||||
|
(Unfortunately, the `ip` command from iproute2 does not
|
||||||
|
provide a way to filter out ULA addresses so we will have
|
||||||
|
to do this ourselves.)
|
||||||
|
* Ignore temporary addresses.
|
||||||
|
* If no addresses remain, log a warning and don't update the
|
||||||
|
IPv6 record.
|
||||||
|
* Otherwise, if one of the remaining addresses matches the
|
||||||
|
previously selected address, continue to use it.
|
||||||
|
* Otherwise, select one arbitrarily.
|
||||||
|
* Deprecate the `use` setting (print a loud warning) but keep its
|
||||||
|
existing semantics with an exception: If there is a conflict with
|
||||||
|
`usev4` or `usev6` then those take priority:
|
||||||
|
* If `use`, `usev4`, and `usev6` are all set then a warning is
|
||||||
|
logged and the `use` setting is ignored.
|
||||||
|
* If `use` and `usev4` are both set and the `use` strategy
|
||||||
|
discovers an IPv4 address that differs from the address
|
||||||
|
discovered by the `usev4` strategy, then the address from
|
||||||
|
`usev4` is used and a warning is logged.
|
||||||
|
* If `use` and `usev6` are both set and the `use` strategy
|
||||||
|
discovers an IPv6 address that differs from the address
|
||||||
|
discovered by the `usev6` strategy, then the address from
|
||||||
|
`usev6` is used and a warning is logged.
|
||||||
|
* If `usev4` (`usev6`) is not set:
|
||||||
|
* If `ipv4` (`usev6`) is set, ddclient acts as if `usev4`
|
||||||
|
(`usev6`) was set to `ipv4` (`ipv6`).
|
||||||
|
* Otherwise, if `ifv4` (`ifv6`) is set, ddclient acts as if
|
||||||
|
`usev4` (`usev6`) was set to `ifv4` (`ifv6`).
|
||||||
|
* Otherwise, if `cmdv4` (`cmdv6`) is set, ddclient acts as if
|
||||||
|
`usev4` (`usev6`) was set to `cmdv4` (`cmdv6`).
|
||||||
|
* Otherwise, if `fwv4` (`fwv6`) is set, ddclient acts as if
|
||||||
|
`usev4` (`usev6`) was set to `fwv4` (`fwv6`).
|
||||||
|
* Otherwise, `usev4` (`usev6`) remains unset.
|
||||||
|
* To support separate credentials for IPv4 vs. IPv6 updates, users
|
||||||
|
can specify the same host multiple times, each time with different
|
||||||
|
options.
|
||||||
|
|
||||||
|
### Internal API changes
|
||||||
|
|
||||||
|
* Add two new entries to the `$config{$host}` hash:
|
||||||
|
* `$config{$host}{'wantipv4'}` is set to:
|
||||||
|
* If `usev4` is enabled, the IPv4 address discovered by the
|
||||||
|
`usev4` strategy.
|
||||||
|
* Otherwise, if `use` is enabled and the `use` strategy
|
||||||
|
discovered an IPv4 address, the IPv4 address discovered by
|
||||||
|
the `use` strategy.
|
||||||
|
* Otherwise, `undef`.
|
||||||
|
* `$config{$host}{'wantipv6'}` is set to:
|
||||||
|
* If `usev6` is enabled, the IPv6 address discovered by the
|
||||||
|
`usev6` strategy.
|
||||||
|
* Otherwise, if `use` is enabled and the `use` strategy
|
||||||
|
discovered an IPv6 address, the IPv6 address discovered by
|
||||||
|
the `use` strategy.
|
||||||
|
* Otherwise, `undef`.
|
||||||
|
* Deprecate the existing `$config{$host}{'wantip'}` entry, to be
|
||||||
|
removed after all update protocol callbacks have been updated to
|
||||||
|
use the above new entries. In the meantime, this entry's value
|
||||||
|
depends on which of `use`, `usev4`, and `usev6` is enabled, and
|
||||||
|
what type of IP address is discovered by the `use` strategy (if
|
||||||
|
enabled), according to the following table:
|
||||||
|
|
||||||
|
| `use` | `usev4` | `usev6` | resulting value |
|
||||||
|
| :---: | :---: | :---: | :--- |
|
||||||
|
| ✔(IPv4) | ✖ | ✖ | the IPv4 address discovered by the `use` strategy |
|
||||||
|
| ✔(IPv6) | ✖ | ✖ | the IPv6 address discovered by the `use` strategy |
|
||||||
|
| ✖ | ✔ | ✖ | the IPv4 address discovered by the `usev4` strategy |
|
||||||
|
| ✖ | ✖ | ✔ | the IPv6 address discovered by the `usev6` strategy |
|
||||||
|
| ✔(IPv4) | ✔ | ✖ | the IPv4 address discovered by the `usev4` strategy (and log another warning if it doesn't match the IPv4 address found by the `use` strategy) |
|
||||||
|
| ✔(IPv6) | ✔ | ✖ | the IPv6 address discovered by the `use` strategy |
|
||||||
|
| ✔(IPv4) | ✖ | ✔ | the IPv4 address discovered by the `use` strategy |
|
||||||
|
| ✔(IPv6) | ✖ | ✔ | the IPv6 address discovered by the `usev6` strategy (and log another warning if it doesn't match the IPv6 address found by the `use` strategy) |
|
||||||
|
|
||||||
|
* To support separate credentials for IPv4 vs. IPv6 updates, convert
|
||||||
|
the `%config` hash of host configs into a list of host configs. A
|
||||||
|
second definition for the same host adds a second entry rather
|
||||||
|
than overwrites the existing entry.
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
### Repurpose the existing settings for v4
|
||||||
|
|
||||||
|
Rather than create new `usev4`, `ifv4`, `cmdv4`, etc. settings,
|
||||||
|
repurpose the existing `use`, `if`, `cmd`, etc. settings for IPv4.
|
||||||
|
|
||||||
|
Why this was rejected:
|
||||||
|
* There is a usability advantage to the symmetry with the `v6`
|
||||||
|
settings.
|
||||||
|
* It is easier to remain compatible with existing configurations.
|
||||||
|
|
||||||
|
### Let `use` set the default for `usev4`
|
||||||
|
|
||||||
|
Rather than three separate IP discovery mechanisms (`use`, `usev4`,
|
||||||
|
and `usev6`), have just two (`usev4` and `usev6`) and let the old
|
||||||
|
`use` setting control the default for `usev4`: If `usev4` is not set,
|
||||||
|
then `use=foo` is equivalent to `usev4=foov4`.
|
||||||
|
|
||||||
|
Why this was rejected: Backwards incompatibility. Specifically,
|
||||||
|
configurations that previously updated an IPv6 record would instead
|
||||||
|
(attempt to) update an IPv4 record.
|
||||||
|
|
||||||
|
### Let `use` set the default for `usev4` and `usev6`
|
||||||
|
|
||||||
|
Rather than three separate IP discovery mechanisms (`use`, `usev4`,
|
||||||
|
and `usev6`), have just two (`usev4` and `usev6`) and let the old
|
||||||
|
`use` setting control the default for `usev4` and `usev6`:
|
||||||
|
|
||||||
|
* If neither `usev4` nor `usev6` is set, then `use=foo` is
|
||||||
|
equivalent to `usev4=foov4,usev6=foov6`.
|
||||||
|
* If `usev4` is set but not `usev6`, then `use=foo` is equivalent to
|
||||||
|
`usev6=foov6`.
|
||||||
|
* If `usev6` is set but not `usev4`, then `use=foo` is equivalent to
|
||||||
|
`usev4=foov4`.
|
||||||
|
* If both `usev4` and `usev6` are set, then `use=foo` is ignored.
|
||||||
|
|
||||||
|
Why this was rejected: The new design would cause existing
|
||||||
|
configurations to trigger surprising, and possibly undesired (e.g.,
|
||||||
|
timeouts or update errors), new behavior:
|
||||||
|
|
||||||
|
* Configurations that previously updated only an IPv4 record would
|
||||||
|
also update an IPv6 record.
|
||||||
|
* Similarly, configurations that previously updated only an IPv6
|
||||||
|
record would also update an IPv4 record.
|
||||||
|
|
||||||
|
### Replace uses of `'wantip'` with `'wantipv4'`
|
||||||
|
|
||||||
|
Rather than support `'wantip'`, `'wantipv4'`, and `'wantipv6'`, just
|
||||||
|
replace all `'wantip'` references to `'wantipv4'`.
|
||||||
|
|
||||||
|
Why this was rejected: This would break compatibility for users that
|
||||||
|
are currently updating IPv6 addresses. (Compatibility would be
|
||||||
|
restored once the update protocol callbacks are updated to honor
|
||||||
|
`'wantipv6'`.)
|
||||||
|
|
||||||
|
### Single `if` setting for both `usev4=if` and `usev6=if`
|
||||||
|
|
||||||
|
The proposed design calls for separate `ifv4` and `ifv6` settings. If
|
||||||
|
the user sets `usev4=if,usev6=if`, then the user most likely wants to
|
||||||
|
use the same interface for both IPv4 and IPv6. Rather than create
|
||||||
|
separate `ifv4` and `ifv6` settings, have a single `if` setting used
|
||||||
|
for both `usev4` and `usev6`.
|
||||||
|
|
||||||
|
Why this was rejected:
|
||||||
|
* Separate `v4` and `v6` settings adds consistency to the
|
||||||
|
configuration.
|
||||||
|
* There are cases where a user will want to use a different
|
||||||
|
interface. In particular, an IPv6 over IPv4 tunnel (e.g.,
|
||||||
|
https://tunnelbroker.net) involves creating a separate interface
|
||||||
|
that is used only for IPv6.
|
||||||
|
|
||||||
|
### Separate IPv4 and IPv6 credentials
|
||||||
|
|
||||||
|
In order to support providers that use separate credentials for IPv4
|
||||||
|
and IPv6 updates, the proposed design allows the user to define the
|
||||||
|
same host twice. We could instead add additional options so that the
|
||||||
|
user can provide both sets of credentials in a single host definition.
|
||||||
|
|
||||||
|
Why this was rejected:
|
||||||
|
* The proposed design is easier to implement, as it does not require
|
||||||
|
any modifications to existing protocol implementations.
|
||||||
|
* The proposed design is less likely to cause problems for users
|
||||||
|
that rely on globals instead of host-specific options. For
|
||||||
|
example, a configuration file like the following might not do what
|
||||||
|
the user expects:
|
||||||
|
|
||||||
|
```
|
||||||
|
ssl=true, use=if, if=eth0
|
||||||
|
|
||||||
|
protocol=foo
|
||||||
|
login=username-for-ipv4
|
||||||
|
password=password-for-ipv4
|
||||||
|
loginv6=username-for-ipv6
|
||||||
|
passwordv6=password-for-ipv6
|
||||||
|
myhost.example.com
|
||||||
|
|
||||||
|
protocol=bar
|
||||||
|
login=username
|
||||||
|
password=password
|
||||||
|
# This host definition will use loginv6, passwordv6 from above
|
||||||
|
# because the user didn't end each setting with a line
|
||||||
|
# continuation:
|
||||||
|
my-other-host.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
* The proposed design provides some bonus functionality:
|
||||||
|
* Users can smoothly transition between different providers by
|
||||||
|
updating both providers simultaneously until the domain
|
||||||
|
registration switches to the new registrar.
|
||||||
|
* Users can take advantage of providers that support multiple A
|
||||||
|
or multiple AAAA records for the same hostname, assuming each
|
||||||
|
record has independent credentials.
|
Loading…
Reference in a new issue