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-dhcpdmkdir /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
udhcpcclient, Ubuntu'sdhclientclient, 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