| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
A fast, single-binary DHCPv4 server written in Go (built on insomniacslk/dhcp) with:
# 1) Build
go build -o dhcplane .
# 2) Prepare config and empty lease DB
cp config.example.json dhcplane.config
echo '{"by_ip":{},"by_mac":{}}' > dhcplane.leases
# 3) Allow binding to UDP:67 without root (Linux)
sudo setcap 'cap_net_bind_service=+ep' "$(pwd)/dhcplane"
# 4) Run
./dhcplane serve --consoleTip: add --console to expose the interactive console socket (or TCP if console_tcp_address is configured) while still seeing logs on stdout/stderr.
Requires Go 1.21+.
go build -o dhcplane .Binary is self-contained; no external services needed.
DHCPv4 servers listen on UDP port 67 (privileged). Options:
sudo setcap 'cap_net_bind_service=+ep' /path/to/dhcplaneThis attaches to that specific binary. Recompiling creates a new file, so you must re-apply setcap to the new binary path.
The server loads a strict JSON file (unknown fields are rejected). On serve startup and on every reload/watch event, config is validated.
{
"interface": "eth0",
"server_ip": "192.168.178.1",
"subnet_cidr": "192.168.178.0/24",
"gateway": "192.168.178.1",
"compact_on_load": false,
"dns": ["1.1.1.1", "9.9.9.9"],
"domain": "lan",
"lease_db_path": "dhcplane.leases",
"pid_file": "dhcplane.pid",
"lease_seconds": 86400,
"lease_sticky_seconds": 86400,
"auto_reload": true,
"pools": [
{"start": "192.168.178.50", "end": "192.168.178.199"}
],
"exclusions": ["192.168.178.100"],
"reservations_path": "dhcplane.reservations",
"ntp": ["192.168.178.1"],
"mtu": 1500,
"tftp_server_name": "192.168.178.2",
"bootfile_name": "pxelinux.0",
"wpad_url": "http://wpad.lan/wpad.dat",
"wins": ["192.168.178.3"],
"domain_search": ["lan", "corp.lan"],
"static_routes": [
{"cidr": "10.10.0.0/16", "gateway": "192.168.178.254"}
],
"mirror_routes_to_249": true,
"vendor_specific_43_hex": "01:04:de:ad:be:ef",
"device_overrides": {
"00-11-22-33-44-55": {
"dns": ["192.168.178.53"],
"tftp_server_name": "192.168.178.2",
"bootfile_name": "special.efi"
}
},
"banned_macs": {
"dc:ed:83:f3:68:5b": {
"first_seen": 1725550000,
"note": "guest device blocked",
"equipment_type": "Gateway",
"manufacturer": "Unknown"
}
},
"equipment_types": ["Switch","Router","AP","Modem","Gateway","Printer"],
"management_types": ["ssh","web","telnet","serial","console"],
"console_max_lines": 10000,
"console_tcp_address": "",
"logging": {
"path": "",
"filename": "dhcplane.log",
"max_size": 20,
"max_backups": 5,
"max_age": 0,
"compress": true
},
"detect_dhcp_servers": {
"enabled": true,
"active_probe": "off",
"probe_interval": 600,
"first_scan": 60,
"rate_limit": 6,
"whitelist_servers": []
},
"arp_anomaly_detection": {
"enabled": false,
"probe_interval": 1800,
"first_scan": 60
}
}Defaults:
{
"interface": "",
"server_ip": "192.168.178.1",
"subnet_cidr": "192.168.178.0/24",
"gateway": "192.168.178.1",
"dns": ["1.1.1.1","9.9.9.9"],
"lease_db_path": "dhcplane.leases",
"lease_seconds": 86400,
"lease_sticky_seconds": 86400,
"auto_reload": true,
"pools": [
{"start":"192.168.178.50","end":"192.168.178.199"}
],
"exclusions": ["192.168.178.100"],
"reservations_path": "dhcplane.reservations",
"domain": "lan",
"ntp": ["192.168.178.1"],
"mtu": 1500,
"tftp_server_name": "192.168.178.2",
"bootfile_name": "pxelinux.0",
"wpad_url": "",
"wins": [],
"domain_search": ["lan"],
"static_routes": [],
"mirror_routes_to_249": false,
"vendor_specific_43_hex": "",
"device_overrides": {},
"banned_macs": {},
"equipment_types": ["Switch","Router","AP","Modem","Gateway"],
"management_types": ["ssh","web","telnet","serial","console"],
"console_max_lines": 10000,
"console_tcp_address": "",
"logging": {
"path": "",
"filename": "dhcplane.log",
"max_size": 20,
"max_backups": 5,
"max_age": 0,
"compress": true
},
"detect_dhcp_servers": {
"enabled": true,
"active_probe": "off",
"probe_interval": 600,
"first_scan": 60,
"rate_limit": 6,
"whitelist_servers": []
},
"arp_anomaly_detection": {
"enabled": false,
"probe_interval": 1800,
"first_scan": 60
}
}Reservations are stored in a separate JSON file (default: dhcplane.reservations, configurable via reservations_path). The file format is a map of MAC addresses to reservation objects:
{
"aa:bb:cc:dd:ee:ff": {
"ip": "192.168.178.10",
"note": "human note",
"first_seen": 1725550000,
"equipment_type": "Switch",
"manufacturer": "Ubiquiti",
"management_type": "web",
"management_interface": "https://192.168.178.10"
}
}MAC keys accept aa:bb:..., aa-bb-..., or aabb... formats.
Backwards compatible: the legacy {"mac":"ip"} style is also accepted on load.
Banned MACs can be declared in config under banned_macs (with optional metadata), and/or via env var dhcplane_BANNED_MACS (comma/space/newline-separated list, any delimiter style, e.g. aabbccddeeff, aa:bb:..., aa-bb-...). Banned MACs:
device_overrides lets you override only:
Global config remains in effect for everything else.
static_routes becomes Option 121 (RFC 3442). If mirror_routes_to_249 is true, the same payload is also sent as proprietary Microsoft option 249.
"static_routes": [
{"cidr":"10.10.0.0/16","gateway":"192.168.178.254"}
]Provide a raw hex payload in many styles: "01:04:de:ad:be:ef", "01 04 de ad be ef", "hex:0104deadbeef", "0x01,0x04,0xDE...".
dhcplane.leases (or the path specified in lease_db_path) is a simple map persisted by the server:
{
"by_ip": {
"192.168.178.100": {
"mac": "aa:bb:cc:dd:ee:ff",
"ip": "192.168.178.100",
"hostname": "host-name",
"allocated_at": 1725551111,
"expiry": 1725637511,
"first_seen": 1725550000
}
},
"by_mac": {
"aa:bb:cc:dd:ee:ff": { /* same structure as above */ }
}
}All timestamps are epoch seconds. Formatting to local time happens only when printing.
If you used a prior version that stored RFC3339 timestamps, the server will tolerantly read and coerce them to epoch on load.
The server listens on UDP:67 on the configured interface (or all interfaces if empty). It is authoritative by default (sends NAKs on invalid requests).
Serve with console echo and file log:
./dhcplane serve \
--config ./dhcplane.config \
--consoleCheck the config:
./dhcplane check -c ./dhcplane.configLive reload via PID file (default dhcplane.pid):
./dhcplane reload -c ./dhcplane.configList current leases (pretty JSON):
./dhcplane leases -c ./dhcplane.configSearch for an IP address:
./dhcplane search 192.168.178.100 -c ./dhcplane.configStats & tables:
# Summary + leased/expiring/expired tables
./dhcplane stats -c ./dhcplane.config
# Full subnet table (hides free addresses) with Type column
./dhcplane stats -c ./dhcplane.config --details
# colour grid of the whole subnet
./dhcplane stats -c ./dhcplane.config --gridAdd or update a reservation:
# MAC, IP, then an optional free-form note
./dhcplane manage add dc:ed:83:f3:68:5b 192.168.178.55 "kitchen display"Remove a reservation:
./dhcplane manage remove dc:ed:83:f3:68:5bStart the DHCP server. Validates config before binding. Creates/updates a PID file. Supports SIGHUP reload and graceful termination.
Key features at runtime:
Print leases from the lease database (default: dhcplane.leases) as a JSON array with formatted timestamps (local time), including AllocatedAt, Expiry, and FirstSeen.
Print allocation rates for the last 1m/1h/24h/7d/30d and lease groupings.
Flags:
Strictly validate the config file (default: dhcplane.config). Unknown fields, wrong types, etc., return precise line/column.
Reads PID from pid_file in the config and sends SIGHUP. Before signaling, re-validates the config and refuses to reload if invalid.
Manipulate reservations in the reservations file (default: dhcplane.reservations):
Edits are written atomically via a temporary file → rename.
Search for an IP address in reservations and leases:
Global flags (apply to all commands unless noted):
Command-specific flags:
dhcplane_BANNED_MACS Extra banned MACs; separated by comma/space/newline. Accepts aa:bb:..., aa-bb-..., or aabb....
COLUMNS If set, used as terminal width hint for grid layout.
When auto_reload is true, the process also watches the config file and reservations file directories and applies changes shortly after writes (with validation and first-seen stamping for reservations/banned MACs where missing).
Example log lines:
START iface="" bind=0.0.0.0:67 server_ip=192.168.178.1 subnet=192.168.178.0/24 gateway=192.168.178.1 lease=24h0m0s sticky=24h0m0s
AUTO-RELOAD: watching ./dhcplane.config
CONSOLE listening on UNIX socket + TCP 0.0.0.0:9090
DETECT start mode=off interval=600s rate_limit=6/min iface=eth0 whitelist=0
DETECT scan started iface=eth0
DETECT scan completed iface=eth0 (no foreign servers found)
DISCOVER from aa:bb:cc:dd:ee:ff hostname="printer" xid=0x12345678
OFFER aa:bb:cc:dd:ee:ff -> 192.168.178.100
ACK aa:bb:cc:dd:ee:ff <- 192.168.178.100 lease=24h0m0s (alloc=2025/09/05 20:40:01, exp=2025/09/06 20:40:01)
DETECT scan started iface=eth0
FOREIGN-DHCP-SERVER detected server_ip=192.168.178.254 from=192.168.178.254 iface=eth0
DETECT scan completed iface=eth0 (found 1 server(s))
ARP-ANOMALY ip=192.168.178.107 mac=d0:27:03:4f:22:60 iface=eth0 reason=unknown found=arp reserved=false leased=false excluded=false
The server includes two optional background monitoring tasks:
Detects devices on the network that aren't managed by the DHCP server:
Configuration:
"arp_anomaly_detection": {
"enabled": false,
"probe_interval": 1800,
"first_scan": 60
}Detects rogue/unauthorized DHCP servers on the network:
Configuration:
"detect_dhcp_servers": {
"enabled": true,
"active_probe": "off",
"probe_interval": 600,
"first_scan": 60,
"rate_limit": 6,
"whitelist_servers": []
}Both tasks run independently in background goroutines and can be enabled/disabled via config.
The interactive console can be accessed via:
UNIX socket (default): Local access via socket file
./dhcplane console attachTCP/IP (when configured): Remote access over network
"console_tcp_address": "0.0.0.0:9090"./dhcplane console attach --tcp 192.168.1.2:9090When console_tcp_address is set, the server listens on both UNIX socket and TCP simultaneously, allowing both local and remote access.
| Back | FazBrowse Home | New Git URL |