This is a tiny IPv4-only dhcp server written in Golang.
Currently, this only works on Linux, though it is possible to run unit tests on Mac.
Other popular dhcpd servers such as isc-dhcpd and dnsmasq are written in C and occasionally have CVEs.
I wanted to make a small and safe Go solution, as well as to learn the DHCP wire protocol.
- Clone repo
- go build
- Configure conf.yaml
- Run with permissions needed to listen on port 67 (eg run as root or use linux capabilities), as follows
- Run a separate VM on the same bridge/vlan as a dhcp client
We use yaml. Multiple pools can be defined this way. DHCP traffic to interfaces not listed will be ignored.
pools:
- name: vm testing
network: 172.17.0.0
mask: 255.255.255.0
start: 172.17.0.100
end: 172.17.0.200
leasetime: 60
myip: 172.17.0.1
routers: [ 172.17.0.1 ]
dns: [ 1.1.1.1, 8.8.8.8 ]
# Optional static IPs by mac address
hosts:
- ip: 172.17.0.5
hw: 0:1c:42:b4:6e:1d
verbose: false # Set to true for debug logging
interfaces: [ eth1 ]
leasedir: /var/lib/golang-dhcpd
mkdir /etc/golang-dhcpd
cp conf.yml /etc/golang-dhcpd/conf.yaml
docker compose up --build -d
docker compose logs -f
Note: The Docker setup uses ipvlan networking for security. Update your config to:
- Set
interfaces: [ eth0 ]
(interface name inside container may not be the same as host interface) - Set
myip:
to match the container's IP address
Warning: Using network_mode: "host"
with unprivileged users may fail
to bind to port 67 due to Docker limitations with capabilities in host networking mode,
so one can either use ipvlan
mode with dhcp user or host
mode with root user.
root@ubuntu1:~/dev/golang-dhcpd# go build
root@ubuntu1:~/dev/golang-dhcpd# ./mygodhcpd -conf conf.yaml
2021/07/05 21:36:58 Loaded pool vm testing on interface eth1
2021/07/05 21:37:18 DHCPREQUEST from 0:1c:42:b4:6e:1d for 172.17.0.100
2021/07/05 21:37:18 Unrecognized lease for 0:1c:42:b4:6e:1d
2021/07/05 21:37:18 Sending DHCPNAK to 0:1c:42:b4:6e:1d
2021/07/05 21:37:18 DHCPDISCOVER from 0:1c:42:b4:6e:1d (ubuntu2)
2021/07/05 21:37:18 Sending DHCPOFFER with 172.17.0.100 to 0:1c:42:b4:6e:1d
2021/07/05 21:37:18 DHCPREQUEST from 0:1c:42:b4:6e:1d for 172.17.0.100
2021/07/05 21:37:18 Sending DHCPACK with 172.17.0.100 to 0:1c:42:b4:6e:1d
root@ubuntu2:~# dhclient eth1
root@ubuntu2:~# ip -4 a show eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
inet 172.17.0.100/24 brd 172.17.0.255 scope global dynamic eth1
valid_lft 54sec preferred_lft 54sec
root@ubuntu2:~#
- Be small
- Be fast
- Be configurable
- Don't require anything outside of the Go standard library (except maybe testify)
- Verified to work with Alpine's
udhcpc
client, Ubuntu'sdhclient
client, Windows 10, LG WebOS, Android phones. - Relay requests verified to work with isc-dhcp-relay.
- Bare minimum wire protocol for DHCPDISCOVER, DHCPOFFER, DHCPREQUEST, DHCPNAK, DHCPACK, and DHCPRELEASE to work
- Supports relayed requests
- Supports multiple IP Pools, sourced from configuration
- Supports hosts in config with hardcoded IPs, based on mac address
- Support acting as a relay
- Support arbitrary options, including options scoped to specific hosts
- PXE with usage examples
- Example systemd unit, deb/rpm packages, etc
- More Tests