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

dhcp-server: also read sender MAC address from packet header #21368

Closed
ralfbiedert opened this issue Nov 14, 2021 · 24 comments · Fixed by #27313
Closed

dhcp-server: also read sender MAC address from packet header #21368

ralfbiedert opened this issue Nov 14, 2021 · 24 comments · Fixed by #27313
Labels
dhcp network RFE 🎁 Request for Enhancement, i.e. a feature request

Comments

@ralfbiedert
Copy link

ralfbiedert commented Nov 14, 2021

systemd version the issue has been seen with

systemd 249 (249.4)
+PAM +AUDIT -SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL > -ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB -ZSTD -XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified

Used distribution

NixOS - Unstable

Linux kernel version used (uname -a)

Linux central 5.10.78 #1-NixOS SMP Sat Nov 6 13:10:10 UTC 2021 x86_64 GNU/Linux

CPU architecture issue was seen on

x86_64

Expected behaviour you didn't see

IPs assigned via DHCPServerStaticLease were not handed out to clients.

Unexpected behaviour you saw

Random IPs were assigned to clients instead.

Steps to reproduce the problem

When using a network configuration like so:

[root@central:/etc/nixos]# cat /etc/systemd/network/br_internal.network

[Match]
Name=br_internal

[Network]
Address=192.168.0.1/24
ConfigureWithoutCarrier=yes
DHCPServer=yes
DHCPv6PrefixDelegation=yes
IPv6SendRA=yes

[DHCPServer]
DNS=192.168.0.1
EmitDNS=true
PoolOffset=100
PoolSize=99

[DHCPServerStaticLease]
MACAddress=04:d4:c4:52:8e:ce
Address=192.168.0.101

I would expect a client with MAC 04:d4:c4:52:8e:ce to be assigned the IP 192.168.0.101. However, the client is instead assigned a random IP from the DHCP pool:

image

Things I've tried that didn't work:

  • Using a Linux client instead (still assigned random pool IP)
  • Upper / lower casing 04:d4:c4:52:8e:ce
  • Specifying Address=192.168.0.101/24 instead (which I've seen somewhere, but gave warning in journal)
  • Rebooting all machines
  • Omitting PoolOffset and PoolSize

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

Journal output around a random time I applied that config.

Nov 14 23:11:47 central nixos[95798]: switching to system configuration /nix/store/cf6356pngbvi0w255mn1s0pivvy01q5j-nixos-system-central-21.11pre329879.c5ed8beb478
Nov 14 23:11:47 central systemd[1]: Stopped target Local File Systems.
Nov 14 23:11:47 central systemd[1]: Stopped target All Network Interfaces (deprecated).
Nov 14 23:11:47 central systemd[1]: Stopped target Remote File Systems.
Nov 14 23:11:47 central systemd[1]: systemd-networkd-wait-online.service: Deactivated successfully.
Nov 14 23:11:47 central systemd[1]: Stopped Wait for Network to be Configured.
Nov 14 23:11:47 central systemd[1]: Stopping Network Configuration...
Nov 14 23:11:47 central systemd-networkd[94579]: enp4s0: DHCP lease lost
Nov 14 23:11:47 central systemd-networkd[94579]: enp4s0: DHCPv6 lease lost
Nov 14 23:11:47 central systemd-networkd[94579]: br_internal: DHCPv6 lease lost
Nov 14 23:11:47 central systemd-resolved[1894]: enp2s0f2: Failed to read DNSSEC negative trust anchors for the interface, ignoring: No data available
Nov 14 23:11:47 central systemd[1]: systemd-networkd.service: Deactivated successfully.
Nov 14 23:11:47 central systemd[1]: Stopped Network Configuration.
Nov 14 23:11:47 central systemd[1]: systemd-networkd.service: Consumed 47ms CPU time, received 1.4K IP traffic, sent 1.6K IP traffic.
Nov 14 23:11:47 central systemd[1]: Starting Network Configuration...
Nov 14 23:11:47 central systemd-networkd[98487]: tap_drone: TUNSETIFF failed on tun dev: Device or resource busy
Nov 14 23:11:47 central systemd-networkd[98487]: Failed to load /etc/systemd/network/tap_drone.netdev, ignoring: Device or resource busy
Nov 14 23:11:47 central systemd-networkd[98487]: tap_drone: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: tap_drone: Gained carrier
Nov 14 23:11:47 central systemd-networkd[98487]: br_internal: netdev ready
Nov 14 23:11:47 central systemd-networkd[98487]: br_internal: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: br_internal: Gained carrier
Nov 14 23:11:47 central systemd-networkd[98487]: enp2s0f3: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: enp2s0f2: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: enp2s0f1: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: enp2s0f1: Gained carrier
Nov 14 23:11:47 central systemd-networkd[98487]: enp2s0f0: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: enp2s0f0: Gained carrier
Nov 14 23:11:47 central systemd-networkd[98487]: enp4s0: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: enp4s0: Gained carrier
Nov 14 23:11:47 central systemd-networkd[98487]: lo: Link UP
Nov 14 23:11:47 central systemd-networkd[98487]: lo: Gained carrier
Nov 14 23:11:47 central systemd-networkd[98487]: br_internal: Gained IPv6LL
Nov 14 23:11:47 central systemd-networkd[98487]: enp4s0: Gained IPv6LL
Nov 14 23:11:47 central systemd-networkd[98487]: Enumeration completed
Nov 14 23:11:47 central systemd[1]: Started Network Configuration.
Nov 14 23:11:47 central systemd-networkd[98487]: br_internal: netdev exists, using existing without changing its parameters

Actual bridge configuration for br_internal:

[root@central:/etc/nixos]# cat /etc/systemd/network/br_internal.netdev

[NetDev]
Kind=bridge
Name=br_internal
[root@central:/etc/nixos]# cat /etc/systemd/network/internal.network

[Match]
Name=enp2s0f* tap_drone

[Network]
Bridge=br_internal
@yuwata
Copy link
Member

yuwata commented Nov 14, 2021

Could you test with the current git HEAD? We recently fixes several issues on DHCP server's address assignment (#21175).

@yuwata yuwata added the needs-reporter-feedback ❓ There's an unanswered question, the reporter needs to answer label Nov 14, 2021
@yuwata
Copy link
Member

yuwata commented Nov 14, 2021

Also, please provide debugging logs of networkd both server and client sides. Debugging logs can be generate by creating the following drop-in config by systemctl edit systemd-networkd.service:

# /etc/systemd/system/systemd-networkd.service.d/override.conf
[Service]
Environment=SYSTEMD_LOG_LEVEL=debug

@yuwata
Copy link
Member

yuwata commented Nov 14, 2021

Hm? I guess the MAC address of the physical interface and the bridge interface are different.
Please try to specify the MAC address of br_internal in [DHCPServerStaticLease] section.

@ralfbiedert
Copy link
Author

Hm? I guess the MAC address of the physical interface and the bridge interface are different.
Please try to specify the MAC address of br_internal in [DHCPServerStaticLease] section.

Thanks for the hint! Maybe I misunderstand, but isn't that the MAC of the bridge, while the dhcp server has issues finding / assigning MACs of the clients?

Or are you saying that in order to hand out the MAC / IP for my windows machine as above I'll also have to set the MAC of the bridge?

Also, please provide debugging logs of networkd both server and client sides.

Yes, I'll try tomorrow once I'm back at the machine.

@yuwata
Copy link
Member

yuwata commented Nov 15, 2021

Hm? I guess the MAC address of the physical interface and the bridge interface are different.
Please try to specify the MAC address of br_internal in [DHCPServerStaticLease] section.

Thanks for the hint! Maybe I misunderstand, but isn't that the MAC of the bridge, while the dhcp server has issues finding / assigning MACs of the clients?

Or are you saying that in order to hand out the MAC / IP for my windows machine as above I'll also have to set the MAC of the bridge?

Ah, sorry, I misunderstand the report. br_internal is the server side. Please ignore my previous comment.

@ralfbiedert
Copy link
Author

Ok, so I had trouble building and using the latest HEAD, but I managed to enable debug logs on both machines. Since I now use a Linux client the server lease config slightly changed:

[DHCPServerStaticLease]
MACAddress = 52:55:00:d1:55:01
Address = 192.168.0.166

For reference, the client now is a VM connected via a TAP interface with an ip link like so:

2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 52:55:00:d1:55:01 brd ff:ff:ff:ff:ff:ff

Enabling debug logs on both both ends, and requesting a DHCP on the client I receive the following logs on the server:

Nov 15 20:31:09 central systemd-networkd[566544]: rtnl: received non-static neighbor, ignoring.
Nov 15 20:31:13 central systemd-networkd[566544]: br_internal: RADV: Next Router Advertisement in 9s
Nov 15 20:31:16 central systemd-networkd[566544]: br_internal: DHCPv4 server: DISCOVER (0x1ace8584)
Nov 15 20:31:16 central systemd-networkd[566544]: br_internal: DHCPv4 server: OFFER (0x1ace8584)
Nov 15 20:31:16 central systemd-networkd[566544]: br_internal: RADV: Sent solicited Router Advertisement to fe80::5055:ff:fed1:5501
Nov 15 20:31:17 central systemd-networkd[566544]: br_internal: DHCPv4 server: REQUEST (selecting) (0x1ace8584)
Nov 15 20:31:17 central systemd-networkd[566544]: br_internal: DHCPv4 server: ACK (0x1ace8584)
Nov 15 20:31:17 central systemd-networkd[566544]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/network1/link/_37 interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=41 reply_cookie=0 signature=sa{sv}as error-name=n/a e>
Nov 15 20:31:18 central systemd-networkd[566544]: enp2s0f1: LLDP: Invoking callback for 'refreshed' event.
Nov 15 20:31:18 central systemd-networkd[566544]: enp2s0f1: LLDP: Successfully processed LLDP datagram.
Nov 15 20:31:22 central systemd-networkd[566544]: rtnl: received non-static neighbor, ignoring.
Nov 15 20:31:22 central systemd-networkd[566544]: rtnl: received non-static neighbor, ignoring.
Nov 15 20:31:22 central systemd-networkd[566544]: br_internal: NDISC: Sent Router Solicitation, next solicitation in 31s
Nov 15 20:31:23 central systemd-networkd[566544]: br_internal: RADV: Next Router Advertisement in 8min 6s
Nov 15 20:31:25 central systemd-networkd[566544]: rtnl: received non-static neighbor, ignoring.
Nov 15 20:31:28 central systemd-networkd[566544]: enp2s0f1: LLDP: Invoking callback for 'refreshed' event.
Nov 15 20:31:28 central systemd-networkd[566544]: enp2s0f1: LLDP: Successfully processed LLDP datagram.

Meanwhile, the client printed something like:

Nov 15 19:31:16 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): STARTED on ifindex 2
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Discovering IPv6 routers
Nov 15 19:31:16 drone systemd-networkd[1772]: NDISC: Started IPv6 Router Solicitation client
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: State changed: pending -> configuring
Nov 15 19:31:16 drone systemd-networkd[1772]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/network1/link/_32 interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=20 reply_cookie=0 signature=sa{sv}as error-name=>
Nov 15 19:31:16 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): DISCOVER
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting address: 2001:9b1:40cc:100:5055:ff:fed1:5501/64 (valid for 21h 39min 30s)
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: 2001:9b1:40cc:100:5055:ff:fed1:5501/128, src: n/a, gw: n/a, prefsrc: n/a, scope: global, table: local, proto: kernel, type: local
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting address: 192.168.0.111/24 (valid for 57min 23s)
Nov 15 19:31:16 drone systemd-networkd[1772]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/network1/link/_32 interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=21 reply_cookie=0 signature=sa{sv}as error-name=>
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: 192.168.0.0/24, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: link, table: main, proto: kernel, type: unicast
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: 192.168.0.255/32, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: link, table: local, proto: kernel, type: broadcast
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: 192.168.0.0/32, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: link, table: local, proto: kernel, type: broadcast
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: 192.168.0.111/32, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: host, table: local, proto: kernel, type: local
Nov 15 19:31:16 drone systemd-networkd[1772]: rtnl: received non-static neighbor, ignoring.
Nov 15 19:31:16 drone systemd-networkd[1772]: rtnl: received non-static neighbor, ignoring.
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: n/a, src: n/a, gw: fe80::60ee:bff:fe2c:56be, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
Nov 15 19:31:16 drone systemd-networkd[1772]: ens3: Forgetting route: dst: 2001:9b1:40cc:100::/64, src: n/a, gw: n/a, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
Nov 15 19:31:16 drone systemd-networkd[1772]: NDISC: Sent Router Solicitation, next solicitation in 3s
Nov 15 19:31:17 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): OFFER
Nov 15 19:31:17 drone systemd-networkd[1772]: NDISC: Received Router Advertisement: flags none preference medium lifetime 1800 sec
Nov 15 19:31:17 drone systemd-networkd[1772]: NDISC: Invoking callback for 'router' event.
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Configuring route: dst: n/a, src: n/a, gw: fe80::60ee:bff:fe2c:56be, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Configuring route: dst: 2001:9b1:40cc:100::/64, src: n/a, gw: n/a, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
Nov 15 19:31:17 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): REQUEST (requesting)
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Received remembered route: dst: n/a, src: n/a, gw: fe80::60ee:bff:fe2c:56be, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Received remembered route: dst: 2001:9b1:40cc:100::/64, src: n/a, gw: n/a, prefsrc: n/a, scope: global, table: main, proto: ra, type: unicast
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Remembering updated address: 2001:9b1:40cc:100:5055:ff:fed1:5501/64 (valid for 21h 39min 29s)
Nov 15 19:31:17 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): ACK
Nov 15 19:31:17 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): lease expires in 59min 59s
Nov 15 19:31:17 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): T2 expires in 52min 29s
Nov 15 19:31:17 drone systemd-networkd[1772]: DHCP CLIENT (0x1ace8584): T1 expires in 30min
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: DHCPv4 address 192.168.0.111/24 via 192.168.0.1
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Remembering updated address: 192.168.0.111/24 (valid for 1h)
Nov 15 19:31:17 drone systemd-networkd[1772]: Sent message type=signal sender=n/a destination=n/a path=/org/freedesktop/network1/link/_32 interface=org.freedesktop.DBus.Properties member=PropertiesChanged cookie=22 reply_cookie=0 signature=sa{sv}as error-name=>
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Remembering route: dst: 192.168.0.111/32, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: host, table: local, proto: kernel, type: local
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Remembering route: dst: 192.168.0.255/32, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: link, table: local, proto: kernel, type: broadcast
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Remembering route: dst: 192.168.0.0/24, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: link, table: main, proto: kernel, type: unicast
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: Remembering route: dst: 192.168.0.0/32, src: n/a, gw: n/a, prefsrc: 192.168.0.111, scope: link, table: local, proto: kernel, type: broadcast
Nov 15 19:31:17 drone systemd-networkd[1772]: ens3: DHCP: No routes received from DHCP server: No data available

After this transaction, the client has received an ip addr like so:

2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:55:00:d1:55:01 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.111/24 brd 192.168.0.255 scope global dynamic ens3
       valid_lft 3013sec preferred_lft 3013sec

@yuwata
Copy link
Member

yuwata commented Nov 30, 2021

I noticed that the client needs to send the MAC address. If the client is systemd-networkd, then you need to specify the following:

[DHCPv4]
ClientIdentifier=mac

We have tested the static lease feature in our CIs. You can find the example configuration at dhcp-server-static-lease.network and dhcp-client-static-lease.network.

@yuwata
Copy link
Member

yuwata commented Nov 30, 2021

Closing. As the issue seems a kind of mis-configuration.

Feel free to request to reopen this page, if the above setting does not fix your issue. Thank you.

@yuwata yuwata closed this as completed Nov 30, 2021
@veehaitch
Copy link
Contributor

I noticed that the client needs to send MAC address. If the client is systemd-networkd, then you need to specify the following:

[DHCPv4]
ClientIdentifier=mac

I can confirm that this works. Thanks!

@ralfbiedert
Copy link
Author

Hm, maybe I misunderstand, but how are Windows / Android clients supposed to get a DCHP address? I previously used dnsmasq which gave all clients an address, without any special client configuration.

@yuwata
Copy link
Member

yuwata commented Nov 30, 2021

@ralfbiedert You mean Windows or Android clients get the static leases? Could you show an example configuration for dnsmasq? I guess the static leases are configured with other client identifiers instead of MAC address?

@ralfbiedert
Copy link
Author

ralfbiedert commented Nov 30, 2021

Sure. This is the (NixOS-dnsmasq) config I previously used which gave all my clients the IPs below:

  services.dnsmasq = {
    enable = true;
    extraConfig = ''
      interface=wg0
      interface=br0
      dhcp-range=192.168.0.10,192.168.0.99,12h
      dhcp-host=04:D4:C4:52:8E:CE,192.168.0.101
      dhcp-host=08:62:66:95:A6:90,192.168.0.102
      dhcp-host=62:2E:C5:64:9B:A4,192.168.0.103
      dhcp-host=36:B1:AE:D7:F0:8D,192.168.0.104
      dhcp-host=b8:27:eb:41:31:a2,192.168.0.105
    '';
  };

Before that I used asuswrt with pretty much used the same MAC / configs, but just entered in a web interface.

@yuwata yuwata reopened this Nov 30, 2021
@yuwata yuwata removed the needs-reporter-feedback ❓ There's an unanswered question, the reporter needs to answer label Nov 30, 2021
@yuwata
Copy link
Member

yuwata commented Dec 1, 2021

Could you try to dump DHCP packets and check the Windows or Android DHCP clients actually sends client identifier with their MAC address?

If not, I guess dnsmasq also uses the source MAC address of a DHCP request if the client identifier in the request is not MAC.

@ralfbiedert
Copy link
Author

ralfbiedert commented Dec 1, 2021

[DHCPServerStaticLease]
MACAddress = 98:00:c6:77:72:5e
Address = 192.168.0.106

My networks skills are a bit rusty, but using the systemd-networkd setting above and an iPhone 7, capturing the resulting traffic via tcpdump -i enp2s0f0 -w cap2 and opening the results in WireShark I get this communication:

DHCP Request

Frame 11: 342 bytes on wire (2736 bits), 342 bytes captured (2736 bits)
    Encapsulation type: Ethernet (1)
    Arrival Time: Dec  1, 2021 20:19:36.167396000 W. Europe Standard Time
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1638386376.167396000 seconds
    [Time delta from previous captured frame: 0.005578000 seconds]
    [Time delta from previous displayed frame: 0.005578000 seconds]
    [Time since reference or first frame: 2.862768000 seconds]
    Frame Number: 11
    Frame Length: 342 bytes (2736 bits)
    Capture Length: 342 bytes (2736 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: eth:ethertype:ip:udp:dhcp]
    [Coloring Rule Name: UDP]
    [Coloring Rule String: udp]
Ethernet II, Src: Apple_77:72:5e (98:00:c6:77:72:5e), Dst: Broadcast (ff:ff:ff:ff:ff:ff)
Internet Protocol Version 4, Src: 0.0.0.0, Dst: 255.255.255.255
User Datagram Protocol, Src Port: 68, Dst Port: 67
Dynamic Host Configuration Protocol (Request)
    Message type: Boot Request (1)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0xfa97a626
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 0.0.0.0
    Next server IP address: 0.0.0.0
    Relay agent IP address: 0.0.0.0
    Client MAC address: Apple_77:72:5e (98:00:c6:77:72:5e)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (Request)
    Option: (55) Parameter Request List
    Option: (57) Maximum DHCP Message Size
    Option: (61) Client identifier
    Option: (50) Requested IP Address (192.168.0.157)
    Option: (51) IP Address Lease Time
    Option: (12) Host Name
    Option: (255) End
    Padding: 0000000000000000000000

DHCP ACK

Frame 12: 331 bytes on wire (2648 bits), 331 bytes captured (2648 bits)
    Encapsulation type: Ethernet (1)
    Arrival Time: Dec  1, 2021 20:19:36.167496000 W. Europe Standard Time
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1638386376.167496000 seconds
    [Time delta from previous captured frame: 0.000100000 seconds]
    [Time delta from previous displayed frame: 0.000100000 seconds]
    [Time since reference or first frame: 2.862868000 seconds]
    Frame Number: 12
    Frame Length: 331 bytes (2648 bits)
    Capture Length: 331 bytes (2648 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    [Protocols in frame: eth:ethertype:ip:udp:dhcp]
    [Coloring Rule Name: UDP]
    [Coloring Rule String: udp]
Ethernet II, Src: 62:ee:0b:2c:56:be (62:ee:0b:2c:56:be), Dst: Apple_77:72:5e (98:00:c6:77:72:5e)
Internet Protocol Version 4, Src: 192.168.0.1, Dst: 192.168.0.157
User Datagram Protocol, Src Port: 67, Dst Port: 68
Dynamic Host Configuration Protocol (ACK)
    Message type: Boot Reply (2)
    Hardware type: Ethernet (0x01)
    Hardware address length: 6
    Hops: 0
    Transaction ID: 0xfa97a626
    Seconds elapsed: 0
    Bootp flags: 0x0000 (Unicast)
    Client IP address: 0.0.0.0
    Your (client) IP address: 192.168.0.157
    Next server IP address: 0.0.0.0
    Relay agent IP address: 0.0.0.0
    Client MAC address: Apple_77:72:5e (98:00:c6:77:72:5e)
    Client hardware address padding: 00000000000000000000
    Server host name not given
    Boot file name not given
    Magic cookie: DHCP
    Option: (53) DHCP Message Type (ACK)
        Length: 1
        DHCP: ACK (5)
    Option: (51) IP Address Lease Time
        Length: 4
        IP Address Lease Time: (43200s) 12 hours
    Option: (1) Subnet Mask (255.255.255.0)
        Length: 4
        Subnet Mask: 255.255.255.0
    Option: (3) Router
        Length: 4
        Router: 192.168.0.1
    Option: (6) Domain Name Server
        Length: 4
        Domain Name Server: 192.168.0.1
    Option: (101) TCode
        Length: 13
        TZ TCode: Europe/Berlin
    Option: (54) DHCP Server Identifier (192.168.0.1)
        Length: 4
        DHCP Server Identifier: 192.168.0.1
    Option: (255) End
        Option End: 255

I hope this works. Also here is a raw pcap export for the DHCP exchange: exported2.zip. I cropped the recording to just a few packets, but let me know if you need more of the exchange or anything else.


Update, weird. Although I disconnected form the network and also renewed the lease (just for good measure) I got a pool address at first. However, after coming back to the phone I can now see it actually got .106. I'll rerun this with another device ...

Update 2 ... this gets weirder by the minute. Suddenly the Windows PC I originally created the ticket with also receives its assigned IP. It's as if running tcpdump magically made it work.

@ralfbiedert
Copy link
Author

Update 3 ... omg ... it all makes sense now. Apparently with installing tcpdump (or at least very recently) NixOS must have also updated systemd, as I now have systemd 249 (249.5). And that seems to have the bug fixes you mentioned. Oh well, so much for reproducible builds ...

Anyhow, bug seems to be fixed and sorry for the late confusion!

@ammarzuberi
Copy link

I have this issue running systemd 249 (249.7-1) on Debian bookworm (testing). I have four leases set:

[DHCPServerStaticLease]
MACAddress=xx
Address=10.30.0.10
[DHCPServerStaticLease]
MACAddress=xx
Address=10.30.0.20
[DHCPServerStaticLease]
MACAddress=xx
Address=10.30.0.21
[DHCPServerStaticLease]
MACAddress=xx
Address=10.30.0.25

When I don't have the static leases set, the clients will get IPs from the pool as expected, but when I add the leases they fail DHCP entirely. Looking at a tcpdump I see the following in a loop:

23:28:54.091791 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from xx (oui Unknown), length 300
23:28:54.092410 IP 10.30.0.1.bootps > 10.30.0.21.bootpc: BOOTP/DHCP, Reply, length 268
23:28:54.092949 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from xx (oui Unknown), length 300
23:28:56.716816 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from xx (oui Unknown), length 300
23:28:59.649365 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from xx (oui Unknown), length 300

The reply contains the correct IP for the static lease and looks like this:

23:31:06.981003 IP (tos 0xc0, ttl 64, id 0, offset 0, flags [none], proto UDP (17), length 296)
    10.30.0.1.bootps > 10.30.0.21.bootpc: [udp sum ok] BOOTP/DHCP, Reply, length 268, xid 0x686ce172, Flags [none] (0x0000)
	  Your-IP 10.30.0.21
	  Client-Ethernet-Address xx (oui Unknown)
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: Offer
	    Lease-Time (51), length 4: 3600
	    Subnet-Mask (1), length 4: 255.255.0.0
	    Default-Gateway (3), length 4: 10.30.0.1
	    Server-ID (54), length 4: 10.30.0.1
	    END (255), length 0

Then the client responds correctly with a Request with the IP in it:

23:35:16.343142 IP (tos 0x0, ttl 64, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.bootpc > 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from xx (oui Unknown), length 300, xid 0x2b8848a2, secs 4235, Flags [none] (0x0000)
	  Client-Ethernet-Address xx (oui Unknown)
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message (53), length 1: Request
	    Client-ID (61), length 7: ether xx
	    Requested-IP (50), length 4: 10.30.0.21
	    Server-ID (54), length 4: 10.30.0.1
	    MSZ (57), length 2: 576
	    Parameter-Request (55), length 8:
	      Subnet-Mask (1), Default-Gateway (3), Domain-Name-Server (6), Hostname (12)
	      Domain-Name (15), BR (28), NTP (42), Vendor-Option (43)
	    Vendor-Class (60), length 4: "ubnt"
	    END (255), length 0
	    PAD (0), length 0, occurs 15

Then after that there is no acknowledgement from systemd-networkd.

@yuwata
Copy link
Member

yuwata commented Dec 30, 2021

@ammarzuberi I guess the addresses specified in the static leases are not in the address pool. If so, the issue is hopefully fixed in v250 by b713a99 (#20423).

Could you test v250 or the PR? Or, try to extend the address pool to make it contain the static addresses.

At least, please provide [DHCPServer] section.

@ammarzuberi
Copy link

Expanding the pool to include the reserved addresses worked. I'll upgrade to v250 when it hits debian-testing. Thanks!

@meklonets
Copy link

Hello.
OS Archlinux
systemd 251.2-1.
DHCPServerStaticLease doesn't work until client has

[DHCPv4]
ClientIdentifier=mac

Expanding PoolSize to:

[Match]                                                                                        
Name=br0

[Network]
Address=192.168.77.1/24
DHCPServer=yes

[DHCPServer]
PoolOffset=1
PoolSize=254
EmitDNS=yes
DNS=8.8.8.8
[DHCPServerStaticLease]
MACAddress=9a:4d:a5:eb:e4:50
Address=192.168.77.2

of the DHCPServer section didn't help either.

@maxpoulin64
Copy link

I'm also experiencing this issue:

ArchLinux - systemd 251.2-1

Server:

[Match]
Name=qemu

[Network]
DHCP=no
Address=192.168.1.1/24
DHCPServer=yes

[DHCPServer]
ServerAddress=192.168.1.1/24
PoolOffset=100
PoolSize=100
UplinkInterface=:none
EmitDNS=yes
DNS=192.168.2.1
EmitRouter=yes
Router=192.168.1.1
BindToInterface=yes

[DHCPServerStaticLease]
MACAddress=52:54:00:00:55:55
Address=192.168.1.55

It works fine with ClientIdentifier=mac as suggested above, but if I use dhcpcd instead it doesn't work and gets a random IP from the pool. Using dhclient however works correctly.

Looking into Wireshark, it appears that when ClientIdentifier=mac is set, systemd-networkd reports its client identifier as just the MAC address and nothing else. Meanwhile, dhcpcd also sends out an IAID, a DUID and even a timestamp in addition to the MAC address.

dhcpcd:

Option: (61) Client identifier
    Length: 19
    IAID: 00005555
    DUID Type: link-layer address plus time (1)
    Hardware type: Ethernet (1)
    Time: 710302326
    Link layer address: 52:54:00:00:55:55

systemd-networkd:

Option: (61) Client identifier
    Length: 7
    Hardware type: Ethernet (0x01)
    Client MAC address: RealtekU_00:55:55 (52:54:00:00:55:55)

This all seems to work just fine with dnsmasq. Thankfully, I do control all the machines on this network so I can make it work with the mentioned client configuration, but it seems odd to me that it doesn't work the same way as pretty much every other DHCP server out there. Every other DHCP server I've tried can assign a static lease no problem without ClientIdentifier=mac, and part of the point of DHCP is that it doesn't require client configurations, even with some fairly broken DHCP clients...

The client MAC address is also provided outside of the Client Identifier packet as part of the DHCP Discover, maybe that one could be used instead as a fallback to make it more robust?

@PaulGrandperrin
Copy link

PaulGrandperrin commented Sep 5, 2022

@yuwata , similarly to @maxpoulin64 I'm in a situation where I can't change my clients networking conf. This is why I need to use DHCP in the first place, or else I would just give them static IPs.

Other DHCP servers seem to work as expected even when ClientIdentifier=mac is not set on the clients.

Would it be possible to reopen this issue or open a new one to:

  • make systemd's dhcp server static leases work without needing to modify client's conf
    or
  • set ClientIdentifier=mac by default on clients (I personally don't like this idea)

@yuwata yuwata reopened this Sep 5, 2022
@yuwata yuwata added the RFE 🎁 Request for Enhancement, i.e. a feature request label Sep 5, 2022
@PaulGrandperrin
Copy link

PaulGrandperrin commented Sep 5, 2022

Thanks @yuwata!
FWIW, I tried replacing systemd's dhcp server with Kea, and I can confirm that it's able to assign static leases based on MAC addresses.

EDIT: in the same environment, meaning without ClientIdentifier=mac on the clients.

@yuwata yuwata changed the title DHCPServerStaticLease does not work dhcp-server: also read sender MAC address from packet header Sep 5, 2022
@yuwata
Copy link
Member

yuwata commented Sep 5, 2022

Yeah, MAC address should be also read from header, not only from client identifier.

@RobertMe
Copy link
Contributor

I'm affected by this issue as well.

I've looked into the (server) code for a bit and read a small part of the RFC (2131). For the RFC this states that the client identifier is an opaque key and it shouldn't be interpreted by the server. But my guess is it would be fine to use it as lookup for static leases (as it isn't really interpreted). But I do believe that the implementation is quite odd in this regard. This as the configuration option explicitly is named MACAddress and it also validates that the provided value actually is a mac address (and thus isn't opaque, nor will accept any of the examples of the RFC like a hostname, nor the identifier generated based on RFC 4361).

So to me it seems like there are two things going on:

  1. The current configuration is 100% targetted for matching on MAC address, but the server implementation doesn't use the MAC address (but client identifier), which to me seems like a bug
  2. A possible feature (request) where the configuration is extended so you can set up static leases based on ClientIdentifier (in which case it would be mandatory to provide either MACAddress= or ClientIdentifier=) which would me in line with the current server implementation

Taking into account that the current configuration only accepts actual MAC addresses, and is named MACAddress, I do believe that it would make sense to change the server implementation to (only) look at the mac address ("chaddr") as well. This as it was already impossible to configure an opaque key, and I do think that it would be very uncommon for the client identifier to look like a mac address (which would be accepted in the configuration), but differ from the actual mac address / "chaddr".
So I would like to write a pull request for this change, updating the server code to only look at chaddr (and renaming variables like static_leases_by_client_id as well to reflect it only contains MAC addresses). I don't believe this will lead to any backwards incomapitibilities as the configuration always only accepted MAC addresses. Furthermore I also looked into the implementation of DNSMasq and this (also) only uses the chaddr for static leases, and based on the other comments in this thread I do beieve that the same would apply to other DHCP servers (at least looking at chaddr, possibly looking at the client id).
At a later stage someone might then make the changes to allow static addresses based on the client identifier.

So would such a PR be accepted? Because I'm feeling comfortable enough to make such a small change to only use chaddr when checking whether a static lease is configured and renaming some variables (like the lookup table). Extending the current implentation so it does accept a ClientIdentifier option as well, and doing a lookup on either of them, would be uncomfortable for me as I'm not a C programmer and this being the first time I'm reading systemd code, so unfamiliar with how the configurarion is parsed etc. And I do believe there is an argument to only change the implentation to use chaddr instead of the client id (based on the fact that's the only thing currently accepted in the configuration).
An alternative might be to drop the validation that MACAddress is a MAC address and allowing any value. This way we could use the (random/unique) client identifier in the configuration. But IMO that would still be confusing based on both naming and my believe that no other DHCP server would provide such feature (providing an opaque string as "MAC address" and supporting this value when looking up whether there is a static address required).

RobertMe added a commit to RobertMe/systemd that referenced this issue Apr 17, 2023
DHCP static leases are looked up by the client identifier as send by
the client, while configured based on MAC. As RFC 2131 states the client
identifier is an opaque key and must not be interpreted by the server
this means that DHCP clients can (/will) also use a client identifier
which is not a MAC address. One of these clients actually is
systemd-networkd which uses an RFC 4361 by default to generate the
client identifier. For these kind of DHCP clients static leases thus
don't work because of this mismatch between configuring a MAC address
but the server matching based on client identifier. This adds a fallback
to try to look up a configured static lease based on the "chaddr" of the
DHCP message as this will always contain the MAC address of the client.

Fixes systemd#21368
RobertMe added a commit to RobertMe/systemd that referenced this issue Apr 18, 2023
DHCP static leases are looked up by the client identifier as send by
the client, while configured based on MAC. As RFC 2131 states the client
identifier is an opaque key and must not be interpreted by the server
this means that DHCP clients can (/will) also use a client identifier
which is not a MAC address. One of these clients actually is
systemd-networkd which uses an RFC 4361 by default to generate the
client identifier. For these kind of DHCP clients static leases thus
don't work because of this mismatch between configuring a MAC address
but the server matching based on client identifier. This adds a fallback
to try to look up a configured static lease based on the "chaddr" of the
DHCP message as this will always contain the MAC address of the client.

Fixes systemd#21368
RobertMe added a commit to RobertMe/systemd that referenced this issue Apr 19, 2023
DHCP static leases are looked up by the client identifier as send by
the client, while configured based on MAC. As RFC 2131 states the client
identifier is an opaque key and must not be interpreted by the server
this means that DHCP clients can (/will) also use a client identifier
which is not a MAC address. One of these clients actually is
systemd-networkd which uses an RFC 4361 by default to generate the
client identifier. For these kind of DHCP clients static leases thus
don't work because of this mismatch between configuring a MAC address
but the server matching based on client identifier. This adds a fallback
to try to look up a configured static lease based on the "chaddr" of the
DHCP message as this will always contain the MAC address of the client.

Fixes systemd#21368
RobertMe added a commit to RobertMe/systemd that referenced this issue Apr 20, 2023
DHCP static leases are looked up by the client identifier as send by
the client, while configured based on MAC. As RFC 2131 states the client
identifier is an opaque key and must not be interpreted by the server
this means that DHCP clients can (/will) also use a client identifier
which is not a MAC address. One of these clients actually is
systemd-networkd which uses an RFC 4361 by default to generate the
client identifier. For these kind of DHCP clients static leases thus
don't work because of this mismatch between configuring a MAC address
but the server matching based on client identifier. This adds a fallback
to try to look up a configured static lease based on the "chaddr" of the
DHCP message as this will always contain the MAC address of the client.

Fixes systemd#21368
yuwata pushed a commit that referenced this issue Apr 20, 2023
DHCP static leases are looked up by the client identifier as send by
the client, while configured based on MAC. As RFC 2131 states the client
identifier is an opaque key and must not be interpreted by the server
this means that DHCP clients can (/will) also use a client identifier
which is not a MAC address. One of these clients actually is
systemd-networkd which uses an RFC 4361 by default to generate the
client identifier. For these kind of DHCP clients static leases thus
don't work because of this mismatch between configuring a MAC address
but the server matching based on client identifier. This adds a fallback
to try to look up a configured static lease based on the "chaddr" of the
DHCP message as this will always contain the MAC address of the client.

Fixes #21368
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dhcp network RFE 🎁 Request for Enhancement, i.e. a feature request
Development

Successfully merging a pull request may close this issue.

8 participants