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

systemd-networkd [DHCPServer] should send requested options also in DHCPOFFER #27471

Closed
mwetterw opened this issue May 1, 2023 · 3 comments · Fixed by #27607
Closed

systemd-networkd [DHCPServer] should send requested options also in DHCPOFFER #27471

mwetterw opened this issue May 1, 2023 · 3 comments · Fixed by #27607
Labels
bug 🐛 Programming errors, that need preferential fixing dhcp network
Milestone

Comments

@mwetterw
Copy link

mwetterw commented May 1, 2023

systemd version the issue has been seen with

253

Used distribution

Arch Linux

Linux kernel version used

6.2.10-arch1-1

CPU architectures issue was seen on

x86_64

Component

systemd-networkd

Expected behaviour you didn't see

According to RFC2131, when a DHCP server is answering to a DHCPDISCOVER with a DHCPOFFER, it should:

[...] The
server MUST return to the client:

o The client's network address, as determined by the rules given
earlier in this section,

o The expiration time for the client's lease, as determined by the
rules given earlier in this section,

o Parameters requested by the client, according to the following
rules:

    -- IF the server has been explicitly configured with a default
       value for the parameter, the server MUST include that value
       in an appropriate option in the 'option' field, ELSE

So if a client requested DNS option in the DHCPDISCOVER for example, we should see this in the DHCPOFFER if systemd-networkd has a specific value to give for that parameter.

Unexpected behaviour you saw

There is a discrepancy between options provided in the DHCPOFFER and DHCPACK.
The DHCPACK lists much more options than its DHCPOFFER counterpart:
https://github.com/systemd/systemd/blob/main/src/libsystemd-network/sd-dhcp-server.c#L622-L630

So for example, Linux kernel PNP DHCP (cmdline with ip=dhcp) fails to get the correct DNS server.
This is because in Linux implementation, they only consider the DNS Server of the DHCPOFFER.
If the DHCPOFFER contains no DNS option, Linux will fall back to choosing the same IP as the DHCP Server.
https://github.com/torvalds/linux/blob/v6.2/net/ipv4/ipconfig.c#L1161-L1162

Steps to reproduce the problem

Configure systemd-networkd to be a DHCPServer and to distribute a DNS server different from the server itself.

[Match]
Name=eth0

[Link]
RequiredForOnline=no
ActivationPolicy=manual

[Network]
Address=10.10.10.1/24
DHCPServer=yes

[DHCPServer]
EmitRouter=yes
EmitNTP=yes
NTP=192.168.0.27
EmitDNS=yes
DNS=192.168.0.27

[DHCPServerStaticLease]
MACAddress=aa:aa:aa:aa:aa:aa
Address=10.10.10.251

Use a DHCP client which for at least one option, only cares if it's in the DHCPOFFER.
For example, the Linux kernel built-in DHCP client during boot:

CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y

And with ip=dhcp in the command line.

For DNS option, it will only care if it's present in the DHCPOFFER. If it was not present, it will set it to be equal to the IP of the DHCP Server, even if the value comes later in the DHCPACK.

Additional program output to the terminal or log subsystem illustrating the issue

# tcpdump -vvvnei eth0 port 68
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
23:55:26.948929 aa:aa:aa:aa:aa:aa > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 590: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 576)
    0.0.0.0.68 > 255.255.255.255.67: [no cksum] BOOTP/DHCP, Request from aa:aa:aa:aa:aa:aa, length 548, xid 0xaaaaaaaa, secs 2, Flags [none] (0x0000)
	  Client-Ethernet-Address aa:aa:aa:aa:aa:aa
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x22222222
	    DHCP-Message (53), length 1: Discover
	    Parameter-Request (55), length 9: 
	      Subnet-Mask (1), Default-Gateway (3), Domain-Name-Server (6), Hostname (12)
	      Domain-Name (15), RP (17), MTU (26), YD (40)
	      NTP (42)
	    END (255), length 0
	    PAD (0), length 0, occurs 293
23:55:26.949070 bb:bb:bb:bb:bb:bb > aa:aa:aa:aa:aa:aa, ethertype IPv4 (0x0800), length 310: (tos 0xc0, ttl 64, id 0, offset 0, flags [none], proto UDP (17), length 296)
    10.10.10.1.67 > 10.10.10.251.68: [udp sum ok] BOOTP/DHCP, Reply, length 268, xid 0xaaaaaaaa, Flags [none] (0x0000)
	  Your-IP 10.10.10.251
	  Client-Ethernet-Address aa:aa:aa:aa:aa:aa
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x22222222
	    DHCP-Message (53), length 1: Offer
	    Lease-Time (51), length 4: 3600
	    Subnet-Mask (1), length 4: 255.255.255.0
	    Default-Gateway (3), length 4: 10.10.10.1
	    Server-ID (54), length 4: 10.10.10.1
	    END (255), length 0
23:55:26.968926 aa:aa:aa:aa:aa:aa > ff:ff:ff:ff:ff:ff, ethertype IPv4 (0x0800), length 590: (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto UDP (17), length 576)
    0.0.0.0.68 > 255.255.255.255.67: [no cksum] BOOTP/DHCP, Request from aa:aa:aa:aa:aa:aa, length 548, xid 0xaaaaaaaa, secs 2, Flags [none] (0x0000)
	  Client-Ethernet-Address aa:aa:aa:aa:aa:aa
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x22222222
	    DHCP-Message (53), length 1: Request
	    Server-ID (54), length 4: 10.10.10.1
	    Requested-IP (50), length 4: 10.10.10.251
	    Parameter-Request (55), length 9: 
	      Subnet-Mask (1), Default-Gateway (3), Domain-Name-Server (6), Hostname (12)
	      Domain-Name (15), RP (17), MTU (26), YD (40)
	      NTP (42)
	    END (255), length 0
	    PAD (0), length 0, occurs 281
23:55:26.969054 bb:bb:bb:bb:bb:bb > aa:aa:aa:aa:aa:aa, ethertype IPv4 (0x0800), length 336: (tos 0xc0, ttl 64, id 0, offset 0, flags [none], proto UDP (17), length 322)
    10.10.10.1.67 > 10.10.10.251.68: [udp sum ok] BOOTP/DHCP, Reply, length 294, xid 0xaaaaaaaa, Flags [none] (0x0000)
	  Your-IP 10.10.10.251
	  Client-Ethernet-Address aa:aa:aa:aa:aa:aa
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x22222222
	    DHCP-Message (53), length 1: ACK
	    Lease-Time (51), length 4: 3600
	    Subnet-Mask (1), length 4: 255.255.255.0
	    Default-Gateway (3), length 4: 10.10.10.1
	    Domain-Name-Server (6), length 4: 192.168.0.27
	    NTP (42), length 4: 192.168.0.27
	    TZ-Name (101), length 12: "Europe/Paris"
	    Server-ID (54), length 4: 10.10.10.1
	    END (255), length 0


Linux kernel gets the wrong DNS (falls back to 10.10.10.1 instead of using 192.168.0.27).
But it gets the correct NTP server though.

[    4.808940] IP-Config: Got DHCP answer from 10.10.10.1, my address is 10.10.10.251
[    4.816578] IP-Config: Complete:
[    4.819830]      device=eth0, hwaddr=aa:aa:aa:aa:aa:aa, ipaddr=10.10.10.251, mask=255.255.255.0, gw=10.10.10.1
[    4.829887]      host=10.10.10.251, domain=, nis-domain=(none)
[    4.835781]      bootserver=0.0.0.0, rootserver=0.0.0.0, rootpath=
[    4.835802]      nameserver0=10.10.10.1
[    4.846152]      ntpserver0=192.168.0.27
[    4.922446] uart-pl011 20201000.serial: no DMA platform data
[    5.017119] Freeing unused kernel image (initmem) memory: 29696K
[    5.023568] Run /init as init process
@mwetterw mwetterw added the bug 🐛 Programming errors, that need preferential fixing label May 1, 2023
@mwetterw
Copy link
Author

mwetterw commented May 1, 2023

(It's possible Linux has an issue by only looking at the DHCPOFFER for the DNS, but I think systemd-networkd also has an issue here, and then the 2 issues get intertwined together and create a "protocol misunderstanding".)

@YHNdnzj YHNdnzj added the dhcp label May 1, 2023
@yuwata yuwata added this to the v254 milestone May 1, 2023
intel-lab-lkp pushed a commit to intel-lab-lkp/linux that referenced this issue May 8, 2023
Some DHCP server implementations only send the important requested DHCP
options in the final BOOTP reply (DHCPACK).
One example is systemd-networkd.
However, RFC2131, in section 4.3.1 states:

> The server MUST return to the client:
> [...]
> o Parameters requested by the client, according to the following
>   rules:
>
>      -- IF the server has been explicitly configured with a default
>         value for the parameter, the server MUST include that value
>         in an appropriate option in the 'option' field, ELSE

I've reported the issue here:
systemd/systemd#27471

Linux PNP DHCP client implementation only takes into account the DNS
servers received in the first BOOTP reply (DHCPOFFER).
This usually isn't an issue as servers are required to put the same
values in the DHCPOFFER and DHCPACK.
However, RFC2131, in section 4.3.2 states:

> Any configuration parameters in the DHCPACK message SHOULD NOT
> conflict with those in the earlier DHCPOFFER message to which the
> client is responding.  The client SHOULD use the parameters in the
> DHCPACK message for configuration.

When making Linux PNP DHCP client (cmdline ip=dhcp) interact with
systemd-networkd DHCP server, an interesting "protocol misunderstanding"
happens:
Because DNS servers were only specified in the DHCPACK and not in the
DHCPOFFER, Linux will not catch the correct DNS servers: in the first
BOOTP reply (DHCPOFFER), it sees that there is no DNS, and sets as
fallback the IP of the DHCP server itself. When the second BOOTP reply
comes (DHCPACK), it's already too late: the kernel will not overwrite
the fallback setting it has set previously.

This patch makes the kernel overwrite its DNS fallback by DNS servers
specified in the DHCPACK if any.

Signed-off-by: Martin Wetterwald <martin@wetterwald.eu>
intel-lab-lkp pushed a commit to intel-lab-lkp/linux that referenced this issue May 10, 2023
Some DHCP server implementations only send the important requested DHCP
options in the final BOOTP reply (DHCPACK).
One example is systemd-networkd.
However, RFC2131, in section 4.3.1 states:

> The server MUST return to the client:
> [...]
> o Parameters requested by the client, according to the following
>   rules:
>
>      -- IF the server has been explicitly configured with a default
>         value for the parameter, the server MUST include that value
>         in an appropriate option in the 'option' field, ELSE

I've reported the issue here:
systemd/systemd#27471

Linux PNP DHCP client implementation only takes into account the DNS
servers received in the first BOOTP reply (DHCPOFFER).
This usually isn't an issue as servers are required to put the same
values in the DHCPOFFER and DHCPACK.
However, RFC2131, in section 4.3.2 states:

> Any configuration parameters in the DHCPACK message SHOULD NOT
> conflict with those in the earlier DHCPOFFER message to which the
> client is responding.  The client SHOULD use the parameters in the
> DHCPACK message for configuration.

When making Linux PNP DHCP client (cmdline ip=dhcp) interact with
systemd-networkd DHCP server, an interesting "protocol misunderstanding"
happens:
Because DNS servers were only specified in the DHCPACK and not in the
DHCPOFFER, Linux will not catch the correct DNS servers: in the first
BOOTP reply (DHCPOFFER), it sees that there is no DNS, and sets as
fallback the IP of the DHCP server itself. When the second BOOTP reply
comes (DHCPACK), it's already too late: the kernel will not overwrite
the fallback setting it has set previously.

This patch makes the kernel overwrite its DNS fallback by DNS servers
specified in the DHCPACK if any.

Signed-off-by: Martin Wetterwald <martin@wetterwald.eu>
Signed-off-by: David S. Miller <davem@davemloft.net>
@mwetterw
Copy link
Author

I've changed the behavior on Linux side in net-next.
https://git.kernel.org/netdev/net-next/c/81ac2722fa19

The patch should probably land on Linux 6.5-rc1.

yuwata added a commit to yuwata/systemd that referenced this issue May 11, 2023
These options may not be necessary in DHCPOFFER message, but RFC allows
to send them, and the kernel's DHCP client parses DNS servers only from
DHCPOFFER message. Let's always send same options on offer and ack.

See RFC 2131 section 4.3.1 (https://www.rfc-editor.org/rfc/rfc2131#section-4.3.1).

Fixes systemd#27471.
@yuwata
Copy link
Member

yuwata commented May 11, 2023

Fix is waiting in #27607.

yuwata added a commit to yuwata/systemd that referenced this issue May 11, 2023
From RFC 2131 section 4.3.1 (https://www.rfc-editor.org/rfc/rfc2131#section-4.3.1):
----
The server MUST return to the client:
- Parameters requested by the client, according to the following rules:
  -- IF the server has been explicitly configured with a default
     value for the parameter, the server MUST include that value
     in an appropriate option in the 'option' field,
----
The sentence is not only for ACK, but for all (positive) responses, that
is DHCPOFFER and DHCPACK.

Fixes systemd#27471.
bluca pushed a commit that referenced this issue May 13, 2023
From RFC 2131 section 4.3.1 (https://www.rfc-editor.org/rfc/rfc2131#section-4.3.1):
----
The server MUST return to the client:
- Parameters requested by the client, according to the following rules:
  -- IF the server has been explicitly configured with a default
     value for the parameter, the server MUST include that value
     in an appropriate option in the 'option' field,
----
The sentence is not only for ACK, but for all (positive) responses, that
is DHCPOFFER and DHCPACK.

Fixes #27471.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Programming errors, that need preferential fixing dhcp network
Development

Successfully merging a pull request may close this issue.

3 participants