NetworkConfiguration / dhcpcd

DHCP / IPv4LL / IPv6RA / DHCPv6 client.
https://roy.marples.name/projects/dhcpcd
BSD 2-Clause "Simplified" License
348 stars 112 forks source link

Prefix Exclude Option is malformed in DHCPv6-Request #246

Closed ctomahogh closed 1 year ago

ctomahogh commented 1 year ago

I am using dhcpcd-10.0.2 to obtain an IP address and a Prefix from a DHCPv6 server.

The server is not under my control, so I cannot change the config or test other configurations. My dhcpcd config file looks roughly like this

quiet
script /bin/dhcpc_helper
nogateway
nodev
noipv4ll
option domain_name_servers

hostname
noipv4
nodhcp
ipv6
dhcp6
interface eth0
  ia_na 0
  ia_pd 1 eth1/2 eth3/3 

In the advertise message the server sends not only a Prefix delegation, but also adds a Prefix exclude option. The provided prefix is i.e. 2001:db8:12:abc0:: /62 And the exclude option excludes the prefix 2001:db8:12:abc0:: /64

So the Prefix Exclude Option in the Advertise message has a length of 2 and the first byte of the option payload is 64 (prefix length) and the second byte (IPv6 subnet ID) is 0

advertise_dp_with_pe

Now the dhcpcd sends a DHCPv6-Request but with a malformed Prefix Exclude Option. The length of the Prefix Exclude option is 0

request_dp_with_malformed_pe

Obviously the DHCPv6 server I am using does not like this and therefor does not send a Reply.

As far as I understand RFC 6603 this is not correct. Actually the Request message should in this case reply with a length of 2 and the prefix length 64 and IPv6 subnet ID 0 (like in the advertise)

I looked into the code in dhcp6.c in the function dhcp6_makemessage where the request message is created. In my opinion the according source code (currently starting from line 1007 in master branch) is buggy and always sends a prefix exclude option with length 0

/* RFC6603 Section 4.2 */
if (ap->prefix_exclude_len) {
    uint8_t exb[16], *ep, u8;
    const uint8_t *pp;

    n = (size_t)((ap->prefix_exclude_len -
    ap->prefix_len - 1) / NBBY) + 1;
    ep = exb;
    *ep++ = (uint8_t)ap->prefix_exclude_len;
    pp = ap->prefix_exclude.s6_addr;
    pp += (size_t)
    ((ap->prefix_len - 1) / NBBY) +
    (n - 1);
    u8 = ap->prefix_len % NBBY;
    if (u8)
        n--;
    while (n-- > 0)
        *ep++ = *pp--;
    if (u8)
        *ep = (uint8_t)(*pp << u8);
    n++;
    COPYIN(D6_OPTION_PD_EXCLUDE, exb,
    (uint16_t)n);
    ia_na_len = (uint16_t)
    (ia_na_len + sizeof(o) + n);
}

The while (n-- > 0) loop in line 1023 will always decrement n to -1 which will be incremented to 0 in line 1027 in any case. So the COPYIN function will always use a length of 0 for the option payload.

As far as I understand the rest of the code, the buffer for the response (exb[16]) is correctly filled. So a simple fix could be to just calculate the amount of filled buffer.

...
while (n-- > 0)
    *ep++ = *pp--;
n = ep - exb;
if (u8) {
    *ep = (uint8_t)(*pp << u8);
    n++;
}
COPYIN(D6_OPTION_PD_EXCLUDE, exb,
    (uint16_t)n);
...

In my application this fix worked. But unfortunately I am struggling setting up a DHCPv6 server with prefix delegation and prefix exclude option locally to test other combinations. So I am not sure, whether this fix is working for other scenarios.

Therefor I did not submit this fix as patch or as merge request, because I would firstly appreciate feedback whether:

If I get feedback on this I will happily submit either as patch or as merge request (please advise, what is more appreciated).

Thank you in advance, Christian

rsmarples commented 1 year ago

Your server is faulty - it should only send a prefix exclude option if the client has it in their ORO option field. As you're requesting on eth0 but not delegating to it, dhcpcd should not put the exclude option in the ORO field in the solicit message.

Saying that, dhcpcd is clearly at fault for getting it wrong as well! I was able to setup a kea DHCPv6 server for prefix delegation with prefix exclusion to test this with and I can verify your patch works with a simple config. Here's my test case:

interface eth0
        ia_pd 1 eth0

By all means, submit a PR and take the credit for this. Event more kudos if you can test with odd length prefix delegation AND exclusion.

ctomahogh commented 1 year ago

Thank you for the feedback. I will do some more tests and submit a pull requests afterwards.

Yes, I agree with you, that the server should actually not send a prefix exclude option, as dhcpcd does not send it in the ORO field. What I don't understand is, why should dhcpcd not put the exclude option in the ORO field if the interface, the prefix is requested on is not the same as the one delegated to?

Maybe I should explain my use case a little bit further:

The device I am using dhcpcd on is a router. The router gets an IPv6 address on the WAN interface (eth0) via Router Advertisement (not via DHCPv6). Then it requests a prefix delegation via DHCPv6 on the WAN interface. The received prefixes will only be set on the local networks (eth1 and eth2). The WAN interface will not receive an IPv6 address from the delegated prefix. On the local interfaces a router advertiser is running which is used by the locally connected devices to obtain a reacheable and routeable IPv6 address.

Additionally the network ranges look like follows (slightly changed, but from overlapping point accurate): Router advertisement prefix: 2001:db8:12:abc0::/64 Delegated prefix: 2001:dba:12:abc0::/62 Excluded prefix: 2001:db8:12:abc0::/64

So the excluded prefix is the prefix used for the router advertisement on the WAN. In my opinion it makes sense to exclude it from the delegated prefix to avoid a prefix conflict on the router.

If I change my configuration in this case to (knowing that its excluded) use the offset 0 on the prefix delegation:

interface eth0
    ia_na 0
    ia_pd 1 eth1/0 eth2/3

I get the following (expected) error from dhcpcd: eth1: cannot delegate excluded prefix 2001:db8:12:abc0::/64

And from my perspective this looks totally valid.

So I would actually say, that dhcpcd should send the prefix exclude option in the ORO option field for this use case. But maybe I just misunderstand something.

rsmarples commented 1 year ago

Yes, I agree with you, that the server should actually not send a prefix exclude option, as dhcpcd does not send it in the ORO field. What I don't understand is, why should dhcpcd not put the exclude option in the ORO field if the interface, the prefix is requested on is not the same as the one delegated to?

Because there is no need for it and not many DHCPv6 servers support it anyway. The only one I know of is the ISC Kea one. I also know that some ISP DHCPv6 implementations don't even offer a lease if this option is present.

Maybe I should explain my use case a little bit further:

The device I am using dhcpcd on is a router. The router gets an IPv6 address on the WAN interface (eth0) via Router Advertisement (not via DHCPv6). Then it requests a prefix delegation via DHCPv6 on the WAN interface. The received prefixes will only be set on the local networks (eth1 and eth2). The WAN interface will not receive an IPv6 address from the delegated prefix. On the local interfaces a router advertiser is running which is used by the locally connected devices to obtain a reacheable and routeable IPv6 address.

Additionally the network ranges look like follows (slightly changed, but from overlapping point accurate): Router advertisement prefix: 2001:db8:12:abc0::/64 Delegated prefix: 2001:dba:12:abc0::/62 Excluded prefix: 2001:db8:12:abc0::/64

So the excluded prefix is the prefix used for the router advertisement on the WAN. In my opinion it makes sense to exclude it from the delegated prefix to avoid a prefix conflict on the router.

If I change my configuration in this case to (knowing that its excluded) use the offset 0 on the prefix delegation:

interface eth0
    ia_na 0
    ia_pd 1 eth1/0 eth2/3

I get the following (expected) error from dhcpcd: eth1: cannot delegate excluded prefix 2001:db8:12:abc0::/64

And from my perspective this looks totally valid.

So I would actually say, that dhcpcd should send the prefix exclude option in the ORO option field for this use case. But maybe I just misunderstand something.

dhcpcd is telling you that it cannot delegate the exclusion from DHCP6 because something already exists for it, whether static config or from a RA. This is important warning because if you remove the DHCPv6 lease then it would remove the RA assigned prefix which might annoy some people.

See this is just the kind of thing that annoys me - you don't want any cross over between managed and un-managed stuff. If we have seperate timings for the DHCPv6 exclusion and the RA prefix, which one takes precedence? If the RA changes the vltime to 0, should that also remove the prefix exclusion?

This kind of behavior we cannot possibly code for and will invariably annoy the end user such as yourself hence the polite warning dhcpcd emits.

rsmarples commented 1 year ago

Any status on this? I'd like to push out a new build soon and ideally I'd like this in.

ctomahogh commented 1 year ago

Sorry for the delay. I finally managed to setup a KEA DHCPv6 Server to do some tests. I will finish my tests until tomorrow and then submit a PR, if this is OK. If it is more "urgent" I could send the PR probably today

rsmarples commented 1 year ago

No,that is fine. Looking for a weekend release ideally.