A DHCP server listen on a network interface (e.g: eth0
)
$ cat /etc/default/isc-dhcp-server
INTERFACESv4="eth0"
INTERFACESv6=""
shall use a unique static IP address:
$ cat /etc/network/interfaces
auto eth0
allow-hotplug eth0
iface eth0 inet static
address 192.168.1.1
netmask 255.255.255.0
gateway 192.168.1.254
The eth0
with static IP address shall have forever lease time:
$ ip a show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:dc:a1:1b brd ff:ff:ff:ff:ff:ff
inet 192.168.1.1/24 brd 192.168.1.255 scope global global eth0
valid_lft forever preferred_lft forever
Lastly, setup the DNS resolver to make WAN requests works properly:
$ cat /etc/resolv.conf
domain example.com
search example.com
nameserver 192.168.1.254
Now, the essential network configuration of the DHCP host is complete.
A DHCP lease may contain useful information like routers
, subnet-mask
, domain-name-servers
, domain-name
, ntp-servers
and other options. These options stores in /etc/dhcp/dhcpd.conf
by default. However, a static IP address host can't utilize this information via DHCP lease request. It shall populate in various config files. Any changes to the configuration may update both host config files and dhcpd.conf
.
Is there a way to make the dhcpd.conf
to act as a single source of truth for DHCP lease information by forcing a dhclient
service to obtain DHCP lease from DHCP service listen to same interface? If that is possible, perhaps we can simplify the configuration steps.
Define a static IP address for network interface
This is require or else dhcp server won't start.
$ cat /etc/network/interfaces
auto eth0
allow-hotplug eth0
iface eth0 inet static
address 192.168.1.1/24
Optional: Define a fixed ip address in DHCP client configuration
This step is optional unless you want the IP address for your DHCP host.
$ cat /etc/dhcp/dhclient.conf
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
send host-name = gethostname();
request subnet-mask, broadcast-address, time-offset, routers,
domain-name, domain-name-servers, domain-search, host-name,
dhcp6.name-servers, dhcp6.domain-search, dhcp6.fqdn, dhcp6.sntp-servers,
netbios-name-servers, netbios-scope, interface-mtu,
rfc3442-classless-static-routes, ntp-servers;
interface "eth0" {
send dhcp-requested-address 192.168.1.1;
}
Define a DHCP pool range for the network
$ cat /etc/dhcp/dhcpd.conf
subnet 192.168.1.0 netmask 255.255.255.0 {
option domain-name "example.lan";
option subnet-mask 255.255.255.0;
option routers 192.168.1.254;
option domain-name-servers 192.168.1.253;
pool {
range 192.168.1.10 192.168.1.200;
}
}
Restart the machine and check you have a static IP address and dhcp server running. Now execute these to get IP address from DHCP server:
# Flush the IP address of interface eth0
$ ip a flush dev eth0
# Remove dhclient lease
$ rm /var/lib/dhcp/dhclient.*
# Start dhclient for interface eth0
dhclient -v eth0
A new IP address shall get allocate for the interface eth0
with DHCP requested options.
The above method works by start dhclient on interface eth0
manually. The self obtain DHCP lease mechanism won't survive after reboot. Let's write a systemd service. The systemd service must only start after isc-dhcp-server
. A sample [email protected]
and ansible script are available to show how it works.
After deploy [email protected]
file with ansible script. The dhclient service will always start and obtain DHCP lease after reboot.
The above configuration works with a potential issue - IP address may suddenly lost due to the lease didn't renew after lease expire. During DHCP lease RENEW state, the DHCP server does not acknowledge DHCP Request
from same interface:
DHCPREQUEST for 192.168.1.5 on eth0 to 192.168.1.1 port 67
DHCPREQUEST for 192.168.1.5 on eth0 to 192.168.1.1 port 67
DHCPREQUEST for 192.168.1.5 on eth0 to 192.168.1.1 port 67
The consequences of losing IP address is whole network service down once all leases expired. Fortunately, this issue can be solved with failover peer deployment strategy.
When DHCP discover packet broadcast to DHCP failover cluster, the DHCP service always offer dhcp-server-identifier
as IP address of primary DHCP (not secondary DHCP). In this case, both primary and secondary DHCP host shall receive dhcp-server-identifier
as primary DHCP IP address. When dhclient
attempt to renew, primary DHCP host will fail to renew lease due to DHCP request to same interface. Only DHCP request from secondary DHCP success.
Apparently, in order to make self assigned DHCP service works, we can force dhlient
acquire and renew lease from it's peer.
Acquire DHCP lease from peer
Primary DHCP host - 192.168.1.1:/etc/dhcp/dhclient.eth0.conf
:
ExecStart=/sbin/dhclient -4 -d -v -cf /etc/dhcp/dhclient.%I.conf -s 192.168.1.2 -pf /var/run/dhclient.%I.pid -lf /var/lib/dhcp/dhclient.%I.leases %I
Secondary DHCP host - 192.168.1.2:/etc/dhcp/dhclient.eth0.conf
:
ExecStart=/sbin/dhclient -4 -d -v -cf /etc/dhcp/dhclient.%I.conf -s 192.168.1.1 -pf /var/run/dhclient.%I.pid -lf /var/lib/dhcp/dhclient.%I.leases %I
Force renew lease from peer
To force dbclient
renew from peer:
Primay Host - 192.168.1.1:/etc/dhcp/dhclient.eth0.conf
supersede dhcp-server-identifier 192.168.1.2
Secondary Host - 192.168.1.2:/etc/dhcp/dhclient.eth0.conf
supersede dhcp-server-identifier 192.168.1.1
After start the dhclient@eth0
service, secondary IP address appears as follow:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:dc:a1:1b brd ff:ff:ff:ff:ff:ff
inet 192.168.1.1/24 brd 192.168.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.1.20/24 brd 192.168.255.255 scope global secondary eth0
valid_lft forever preferred_lft forever
This setup shall never fail until one of the peer is down.
Till now, the self obtain IP address works fluently until one of the peer down. The IP address obtained from DHCP lease will eventually expire and a much worst situation happen - losing both IP addresses. This is due to dhclient
will flush all IP addresses of network interface once lease expire. This cause the DHCP host become unreachable.
We can try to solve the issue with
- Forever lease time that never expire. Lease stay forever even if peer host is down.
- Optional:
supersede dhcp-renewal-time
to obtain new lease options in timely fashion (e.g.:3600
seconds) to utilize DHCP service as usual.
This can be done with:
$ cat /etc/dhcp/dhclient.eth0.conf
...
supersede dhcp-renewal-time 3600; // 1 hour
supersede dhcp-rebinding-time -1; // forever
supersede dhcp-lease-time -1; // forever
Most configurations are done so far, there is one more step to do before start or enable the self assign DHCP service. If there are left over lease file: e.g.: /var/lib/dhcp/dhclient.eth0.leases
. Remove the lease file before start the dhclient
service. This is to make sure the interface e.g.: eth0
always obtain a new DHCP lease as secondary IP address. Failure to do so will cause dhclient
flush the original static IP address.
Now, start or enable the service as usual:
$ systemctl enable dhclient@eth0
$ systemctl start dhclient@eth0
If a failover peer host down, the DHCP lease will never expire. The dhclient
service will keep sending DHCPRequest (shown in /var/log/syslog) until the failover peer back online.
The IP address losing issue may solve with 2 IP addresses strategy for the network interface.
First, move the fixed IP address to secondary slot and leave the primary slot as manual:
$ cat /etc/network/interfaces
auto eth0
allow-hotplug eth0
iface eth0 inet manual
auto eth0:1
allow-hotplug eth0:1
iface eth0:1 inet static
address 10.2.0.255
netmask 255.255.0.0
gateway 10.2.0.1
Next, configure isc-dhcp-server
listen to interface eth0:1
:
$ cat /etc/default/isc-dhcp-server
INTERFACESv4="eth0:1"
INTERFACESv6=""
Add a dhclient configuration for interface eth0:1
:
$ cat /etc/dhcp/dhclient.eth0\:1.conf
option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
send host-name = gethostname();
request subnet-mask, broadcast-address, time-offset, routers,
domain-name, domain-name-servers, domain-search, host-name,
dhcp6.name-servers, dhcp6.domain-search, dhcp6.fqdn, dhcp6.sntp-servers,
netbios-name-servers, netbios-scope, interface-mtu,
rfc3442-classless-static-routes, ntp-servers;
The dhclient
on eth0
will flush ip address of eth0:1
, it shall bring back to online with this script
$ cat /etc/dhcp/dhclient-enter-hooks.d/down-eth0\:1
ifdown eth0:1
$ chmod +x /etc/dhcp/dhclient-enter-hooks.d/down-eth0\:1
$ cat /etc/dhcp/dhclient-exit-hooks.d/up-eth0\:1
ifup eth0:1
$ chmod +x /etc/dhcp/dhclient-exit-hooks.d/up-eth0\:1
Finally, enable dhclient@eth0:1
service and reboot the host for the changes to take effect:
$ sudo systemctl enable dhclient@eth0:1
$ sudo reboot
After reboot, the eth0
interface shall have these IP addresses:
$ ip a show dev eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:dc:a1:1b brd ff:ff:ff:ff:ff:ff
inet 192.168.1.110/24 brd 192.168.255.255 scope global dynamic eth0
valid_lft 60sec preferred_lft 60sec
inet 192.168.1.1/24 brd 192.168.255.255 scope global secondary eth0:1
valid_lft forever preferred_lft forever
While the solution may work to self obtain DHCP lease from same interface. It is not advisable to deploy DHCP server in such manner. It is kind of chicken and egg problem. Stay with static IP address deployment strategy is safer for serious production environment.