I hope you've done your homework. In a previous article about networking we set an exercise to configure some machines with static IP addresses.
You probably agree that it would be annoying if you had to change the address range as it is configured in all the different computers.
Wouldn't it be better if we could store this information in the network somehow?
Well, guess what? We can! Its called DHCP.
This works by nominating one computer on the network to act as the master of networking configuration.
Computers which want to opt into the network communicate with this server to be told what addresses to use.
Normally your router-modem that connects you to the internet takes this responsibility, but any machine, real or virtual, may do it.
Example with network namespaces
As before, we're going to use network namespaces to simulate multiple machines sharing a network.
For DHCP to work you need a DHCP server on the managing machine, and a DHCP client on every client machine that will join this network. Since we are only virtualising the networking, we need both on the same machine.
You will likely have dnsmasq and dhclient on your system, since while systemd can provide a DHCP server and client through networkd, this currently is generally only used in servers, embedded and containers, while NetworkManager uses dnsmasq and dhclient on desktops.
While networkd is capable of being both a DHCP client and server, we can only demonstrate its use as a client, since it requires a full container to isolate its configuration from the host.
Configuring the virtual network
We're going to use a network namespace called
which will be connected to the host network namespace
by a virtual ethernet device.
# ip netns add dhcptest # ip link add host type veth peer name guest # ip link set guest netns dhcptest
The subnet of the network linking us to
dhcptest we use is
which ranges from
Since the machine in
dhcptest will be the DHCP server,
we need to assign its own address statically.
# ip netns exec dhcptest ip addr add 10.0.0.1 dev guest # ip netns exec dhcptest ip link set guest up # ip netns exec dhcptest ip route add 10.0.0.0/24 dev guest
Using dnsmasq as the DHCP server
In a terminal, run:
# ip netns exec dhcptest dnsmasq --dhcp-range=10.0.0.2,10.0.0.254,255.255.255.0 --interface=guest --no-daemon dnsmasq: started, version 2.72 cachesize 150 dnsmasq: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect dnsmasq-dhcp: DHCP, IP range 10.0.0.2 -- 10.0.0.254, lease time 1h dnsmasq: reading /etc/resolv.conf dnsmasq: using nameserver 127.0.1.1#53 dnsmasq: read /etc/hosts - 1 addresses
This starts dnsmasq on
serving the address range
with the subnet
10.0.0.0/24 (this is the
on theguest` network interface.
--no-daemon forces dnsmasq to stay running in the foreground,
which makes it easier to clean up when finished,
since it can be cancelled with
Using dhclient as the DHCP client
In a different terminal to the one that the DHCP server was started in, run:
# dhclient -d host Internet Systems Consortium DHCP Client 4.3.1 Copyright 2004-2014 Internet Systems Consortium. All rights reserved. For info, please visit https://www.isc.org/software/dhcp/ Listening on LPF/host/16:c0:28:6b:ea:76 Sending on LPF/host/16:c0:28:6b:ea:76 Sending on Socket/fallback DHCPDISCOVER on host to 255.255.255.255 port 67 interval 3 (xid=0xd6a3b68) DHCPREQUEST of 10.0.0.105 on host to 255.255.255.255 port 67 (xid=0x683b6a0d) DHCPOFFER of 10.0.0.105 from 10.0.0.1 DHCPACK of 10.0.0.105 from 10.0.0.1 bound to 10.0.0.105 -- renewal in 1643 seconds.
This shows that it has successfully requested
10.0.0.105 as its address.
The dhclient process will continue to run, as it hasn't been given that address forever, it has been given that address for an hour, at which point it will expire and it must stop using it, but before then dhclient may renew the address to allow us to keep using it.
Using networkd as the DHCP client
First create the config file:
# install -m 644 /dev/stdin /etc/systemd/network/dhcptest.network <<'EOF' > [Match] > Name=host > [Network] > DHCP=yes > EOF
Since networkd currently only reads its config at startup, we must restart it for the new config to take effect. (This also has the effect of starting it for the first time, on distros that don't start networkd by default.)
# systemctl restart systemd-networkd.service
This won't give any immediate feedback whether it worked, but you can inspect its status by running:
$ networkctl status host ● 6: host Link File: /lib/systemd/network/99-default.link Network File: /etc/systemd/network/dhcptest.network Type: ether State: routable (configured) Driver: veth HW Address: 16:c0:28:6b:ea:76 MTU: 1500 Address: 10.0.0.105 fe80::14c0:28ff:fe6b:ea76 Gateway: 10.0.0.1 DNS: 10.0.0.1
Note that this has created some persistent configuration,
so the next time the
host ethernet device is created networkd will DHCP.
If you don't want to do this in future, you can remove the configuration
In the terminal running dnsmasq you should see:
dnsmasq-dhcp: DHCPDISCOVER(guest) 10.0.0.105 16:c0:28:6b:ea:76 dnsmasq-dhcp: DHCPOFFER(guest) 10.0.0.105 16:c0:28:6b:ea:76 dnsmasq-dhcp: DHCPREQUEST(guest) 10.0.0.105 16:c0:28:6b:ea:76 dnsmasq-dhcp: DHCPACK(guest) 10.0.0.105 16:c0:28:6b:ea:76 HOSTNAME dnsmasq-dhcp: not giving name HOSTNAME to the DHCP lease of 10.0.0.105 because the name exists in /etc/hosts with address 127.0.1.1
In another terminal you can see that it configured the address by running:
$ ip addr show dev host 6: host: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 16:c0:28:6b:ea:76 brd ff:ff:ff:ff:ff:ff inet 10.0.0.105/24 brd 10.0.0.255 scope global host valid_lft forever preferred_lft forever inet6 fe80::14c0:28ff:fe6b:ea76/64 scope link valid_lft forever preferred_lft forever
Testing the link
We can now prove it works with netcat.
ip netns exec dhcptest nc -l 1234 in one terminal,
nc 10.0.0.1 1234 in another,
and you will be able to see that text entered is repeated in both terminals.