Examples
This page collects a few short patterns that are useful when you start wiring pydhcp into a real service.
Server config file
pydhcp server --config accepts JSON, YAML, TOML, or INI, selected by file extension. A YAML config:
server:
listen: "*"
pydhcp server --config server.yaml
TOML support requires Python 3.11+ (stdlib tomllib) or the optional tomli package on older versions.
Custom lease backend
The server accepts a pluggable lease backend. That makes it easy to persist leases in memory for tests and swap in a file-backed store for simple deployments.
from pydhcp.lease import InMemoryLeaseBackend
from pydhcp.server import DhcpServer
server = DhcpServer(lease_backend=InMemoryLeaseBackend())
server.listen()
Custom server policy
DhcpServer is designed as a base implementation. Override acquire_lease() when your
application owns address-pool selection, reservations, or site-specific options.
from datetime import datetime, timedelta
from pydhcp import DhcpLease, DhcpOptions
from pydhcp.options import DhcpOptionCode
from pydhcp.network import IPv4
from pydhcp.server import DhcpServer
class FixedLeaseServer(DhcpServer):
def acquire_lease(self, client_id, server_id, msg):
options = DhcpOptions()
options[DhcpOptionCode.DNS] = [IPv4("1.1.1.1")]
return DhcpLease(
IPv4("192.0.2.50"),
datetime.now() + timedelta(hours=1),
options,
)
For DHCPINFORM-only customization, override get_inform_options() so clients can receive
configuration options without allocating an address.
Listening on all interfaces
If you want the server to bind every local IPv4 interface, pass '*' or 0.0.0.0.
You can also force the portable per-interface path with per_interface=True.
from pydhcp.server import DhcpServer
server = DhcpServer(listen="*", per_interface=True)
server.listen()
For deterministic local testing or tools that need several explicit sockets, pass a list of endpoints or a tuple with multiple ports.
from pydhcp.server import DhcpServer
server = DhcpServer(listen=[("127.0.0.1", [6767, 6768])], per_interface=True)
server.listen()
The CLI accepts comma-separated endpoint strings for the same workflow.
pydhcp server --listen 127.0.0.1:6767,127.0.0.1:6768 --log-level debug
Basic packet client
DhcpClient is a small packet client for tests and troubleshooting. It can build and send
common DHCP client messages and queue matching replies, but it does not configure the
operating system network stack.
from pydhcp.client import DhcpClient
from pydhcp.options import DhcpOptionCode
client = DhcpClient(listen=("127.0.0.1", 6768))
discover = client.build_discover(
b"\x00\x11\x22\x33\x44\x55",
parameter_request_list=[DhcpOptionCode.SUBNET_MASK, DhcpOptionCode.ROUTER],
)
client.send(discover, destination="127.0.0.1", port=6767)
reply = client.next_reply(timeout=5)
The example uses high ports so it can run without privileged DHCP ports during local tests. Real DHCP traffic on ports 67/68 may require elevated privileges depending on your OS.
Full DORA exchange in one call
DhcpClient.dora() runs DISCOVER → OFFER → REQUEST → ACK and returns the final DHCPACK (or
None if any step times out). The listener loop must be running (start()) so replies reach
the client's internal queue.
from pydhcp.client import DhcpClient
client = DhcpClient(listen=("0.0.0.0", 68))
client.start()
ack = client.dora(b"\x00\x11\x22\x33\x44\x55", timeout=2.0, retries=2)
if ack is not None:
print(f"Leased {ack.yiaddr}")
Use discover_offer() instead if you only need the DHCPOFFER without following through with a
DHCPREQUEST.
Relaying DHCP across subnets
DhcpRelay implements the RFC 1542 / RFC 2131 §4.1 / RFC 3046 relay agent role: it forwards
client broadcasts to one or more configured upstream DHCP servers (stamping giaddr and
incrementing hops), and forwards server replies back to the original client.
from pydhcp.relay import DhcpRelay
relay = DhcpRelay(
listen="*",
server_addresses=["10.0.0.53", ("10.0.1.53", 6767)],
max_hops=16,
)
relay.bind()
relay.listen()
Pass insert_relay_agent_info=True (with circuit_id/remote_id) to tag forwarded requests with
RELAY_AGENT_INFORMATION (option 82) per RFC 3046 — a request that already carries option 82 (a
chained relay) is passed through unmodified rather than double-tagged.
pydhcp relay --listen 127.0.0.1:6767 --server 127.0.0.1:6768 --insert-relay-agent-info --circuit-id aabbcc
Capturing DHCP packets
The capture command listens with the same endpoint syntax as the server and writes structured
packet data. Use - for stdout.
pydhcp capture --listen 127.0.0.1:6767 --filter msg_type=DHCPDISCOVER --output -
Capture filters support exact matches joined by and.
pydhcp capture --filter "op=BOOTREQUEST and src_port=68"
pydhcp capture --filter "client_id=01:AA:BB:CC:DD:EE:FF"
pydhcp capture --filter "option.DHCP_MESSAGE_TYPE=DHCPREQUEST"
Write one file containing all accepted captures:
pydhcp capture --listen 127.0.0.1:6767 --output captures.json --format json
Write one file per accepted packet:
pydhcp capture --listen 127.0.0.1:6767 --format json --output "output/{client_id}/{timestamp}_{msg_type}.{format}" --output-mode per-capture
Hooks can be trusted Python callables or command paths. Command hooks receive the serialized
packet on stdin and metadata in PYDHCP_CAPTURE_* environment variables.
pydhcp capture --hook myhooks:on_capture
pydhcp capture --hook ./on-dhcp-capture
Custom options
DhcpOptions behaves like an ordered mapping, so you can build option sets explicitly and preserve encode order.
from pydhcp import DhcpOptions
from pydhcp.options import DhcpOptionCode
options = DhcpOptions()
options[DhcpOptionCode.HOSTNAME] = "workstation-01"
options[DhcpOptionCode.DOMAIN_NAME] = "example.internal"
For more typed examples across the built-in option families, see Common DHCP Options.
Inspecting packets
The CLI can decode packets and print their fields, which is handy when you are debugging client behavior or validating captures.
pydhcp packet --decode --input - --format json
For terminal inspection, use the summary output.
pydhcp packet --decode --input - --format summary
--input/--output default to - (stdin/stdout), following standard POSIX convention, so both
can be omitted when piping. Structured packet text can also be encoded back to packet hex, and
--input/--output accept a file path in place of -.
pydhcp packet --encode --input packet.json --format json --output packet.hex
TOML packet workflows require the optional TOML extra: pip install pydhcp[toml].