NAME
dhcp_probe - locate DCHP and BootP servers on a directly-attached
network
SYNOPSIS
dhcp_probe [ -c config_file ] [ -d debuglevel ] [ -f ] [ -h ] [ -l
log_file ] [ -o capture_file ] [ -p pid_file ] [ -Q vlan_id ] [ -s
capture_bufsize ] [ -T ] [ -v ] [ -w cwd ] interface_name
DESCRIPTION
dhcp_probe attempts to discover DHCP and BootP servers on a directly-
attached Ethernet network. A network administrator can use this tool
to locate unauthorized DHCP and BootP servers.
The program must be run with root privilege.
The program periodically broadcasts a number of DHCP and BootP request
packets out a single physical interface. Several different kinds of
request packets are sent, as a DHCP or BootP server may only respond to
certain requests, depending on the server’s configuration.
Essentially, dhcp_probe mimics a BootP or DHCP client in a variety of
possible states, attempting to provoke responses from servers.
After sending each request packet, dhcp_probe listens for responses.
After filtering out responses that do not appear to be in response to
the probe, and responses from known DHCP and BootP servers (identified
by their IP source addresses and optionally by their Ethernet source
addresses), it logs any responses from unknown servers.
Optionally, responses from unknown servers may also be written to a
packet capture file.
Optionally, an external program may be called each time a response from
an unknown server is received.
dhcp_probe may not be able to locate all DHCP and BootP servers; see
LIMITATIONS below.
As DHCP broadcasts do not ordinarily cross IP routers, dhcp_probe will
locate only servers that are attached to the same physical network as
the interface specified on the command line. Although BootP Relay
Agents running on this network may help the broadcasts cross IP
routers, these agents typically are configured to convert the
broadcasts to unicasts directed to only the well-known DHCP or BootP
servers located on other physical networks. As a result, BootP Relay
Agents will allow your the servers to receive the requests issued by
dhcp_probe, but will not cause remote unknown servers to hear these
requests. Therefore, if you have multiple physical networks, you may
wish to run dhcp_probe on each of these networks to discover unknown
DHCP and BootP servers on each of them.
dhcp_probe functions on a single Ethernet interface specified on the
command line; it does not listen on multiple interfaces. However, if
the host has multiple physical interfaces, you may run an instance of
dhcp_probe on each interface. If your physical interface supports
802.1Q, you can use that to create a logical interface on each VLAN,
then run an instance of dhcp_probe on each logical interface.
dhcp_probe is intended for use by a network administrator. Before
running dhcp_probe on any network other than one for which you are
responsible, contact that network’s administrator to learn if it is
acceptable for you to run this software on that network. Running this
software may violate on a network where you don’t have permission to do
so may violate that network’s acceptable use policy.
AVAILABILITY
dhcp_probe is a product of the Network Systems Group at Princeton
University’s Office of Information Technology, and is available from
http://www.net.princeton.edu/software/dhcp_probe/
Presently the product builds and runs on Solaris 9 on SPARC with gcc.
The program relies on the pcap(3) and libnet(3) libraries.
OPTIONS
-c config_file
Specifies the configuration file. If not specified, this
defaults to /etc/dhcp_probe.cf. The configuration file is read
at startup, and is re-read whenever a SIGHUP is received. See
dhcp_probe.cf(5).
-d debuglevel
Sets the debuglevel variable that controls the amount of
debugging messages generated. If not specified, this defaults
to 0 (no debugging). For a summary of the types of messages
produced at each debug level, see DEBUG LEVELS below.
-f Specifies that the program should not fork, but instead remain
in the foreground. Only use this when you are starting the
program manually for testing purposes. When in the foreground,
any messages produced by the program are written to stderr
instead of to syslog(3) or any log_file you might specify with
the -l option.
-h Display a brief usage summary, then exit.
-l log_file
Log messages to the specified file instead of to syslog(3).
(This option is ignored if you also specify the -f option, as
that directs messages to stderr.) The log file is opened
shortly after the program starts. It is closed and re-opened
when the program receives a SIGUSR1 signal.
-o capture_file
When a response packet is received from an unexpected server,
write the packet to the specified file. The file is opened and
truncated shortly after the program starts. It is closed and
re-opened (and truncated) when the program receives a SIGUSR2
signal. The file is a pcap(3) savefile, and may be read with
any program that understands the pcap savefile format; e.g.
tcpdump(1).
-p pid_file
Specifies the file that will contain the program’s processid.
If not specified, this defaults to /var/run/dhcp_probe.pid. The
pid_file is written shortly after the program starts, and is
removed when the program exits under its own control.
-Q vlan_id
Specifies that the packets we send should be tagged with an
802.1Q VLAN ID vlan_id. Valid values range from 0 to 4095. If
not specified, the packets we send do not contain an 802.1Q
header.
-s capture_bufsize
Specifies the size of the buffer that will be used to capture
all the responses (Ethernet frames) to a single request packet;
responses which do not fit are silently dropped. The value is
specified in bytes, and must fit into your host’s range for an
int; values outside that range may result in unpredictable
results. If not specified, this defaults to 30280, which is
enough for twenty maximum-size Ethernet frames (1514*20).
Typical responses are Ethernet frames ranging from 342-590
bytes, so the default capture buffer size should hold over 50 of
them.
-T Enables the ’socket receive timeout’ feature. On some
platforms, dhcp_probe may ignore the response_wait_time
(described in dhcp_probe.cf(5)), instead waiting forever for a
response after it sends a probe packet. As per pcap(3), this is
because the read timeout we pass to pcap_open_live() is not
supported on all platforms. If you encounter this issue, try
enabling our ’socket receive timeout’ feature; it might help.
Enabling this feature causes the program to also set a socket
receive timeout on the socket underlying the pcap capture; we
set this timeout to the response_wait_time. On some platforms,
the program’s socket receive timeout feature does not work;
instead the program will report that it cannot set the receive
timeout, and will exit.
-v Display the program’s version number, then exit.
-w cwd Specifies the working directory; shortly after starting the
program changes its current working directory to this. If not
specified, this defaults to /.
interface_name
Specifies the name of the interface the program should use; this
argument is required. This must be an Ethernet interface which
is up and has been assigned an IP address.
OPERATION
After initialization, the program enters its main event loop, in which
it remains until you signal the program to exit with a SIGINT, SIGTERM,
or SIGQUIT.
The main event loop (a.k.a. the "probe cycle") consists of the
following actions, repeated until the program receives a request to
quit:
1. Handle any signals that have been received.
2. Install a pcap(3) filter to listen for UDP packets destined
to the BootP client port (UDP port 68).
3. Broadcast a DHCP or BootP request packet out the specified
interface.
4. Listen for response_wait_time milliseconds for any
responses received by the pcap(3) filter. (The
response_wait_time defaults to 5000 milliseconds (5
seconds), and may be changed in the dhcp_probe.cf(5) file.)
Any responses that contains a bootp_chaddr field not equal
to the chaddr used in the probe is ignored, as are any that
have incorrect bootp_htype or bootp_hlen fields. These are
not responses to our probe.
Any responses from known DHCP and BootP servers are
ignored. The IP source address for responses from each
known server is declared using a legal_server statement in
the dhcp_probe.cf(5) file. Any response with an IP source
address that does not appear in a legal_server statement is
treated as an unknown server.
The Ethernet source address for responses from each known
server is also optionaly declared using a
legal_server_ethersrc statement in the dhcp_probe.cf(5)
file. If at least one legal_server_ethersrc is specified,
then any response with an Ethernet source address that does
not appear in a legal_server_ethersrc statement is treated
as an unknown server. If no legal_server_ethersrc
statements appear, then the response’s Ethernet source
address is not checked. (The legal_server_ethersrc
statement is considered experimental in version 1.3.0, as
it has received only limited testing.)
For each response from an unknown server:
a) If the reponse packet contains a non-zero yiaddr field,
and one or more lease_network_of_concern statements were
specified, determine if the yiaddr value falls within
any of the "Lease Networks of Concern".
a) Log a message showing the response packet’s source IP
and Ethernet addresses. If the response packet’s yiaddr
is non-zero and falls within a "Lease Networks of
Concern", the log message also reports that.
b) If the -o option was specified, the packet is also
written to a packet capture file.
c) If an alert_program_name was specified in the
dhcp_probe.cf(5) file, that program is executed, with
the following arguments in order: the name of the
calling program (e.g. dhcp_probe), the name of the
interface on which the unexpected response packet was
received, the IP source address of the packet, and the
Ethernet source address of the packet. (We do not wait
for the alert_program_name to complete; it runs in a
child process.)
d) If an alert_program_name2 was specified in the
dhcp_probe.cf(5) file, that program is executed, with
the following required options:
-p the name of the calling program (e.g. dhcp_probe)
-I the name of the interface on which the unexpected response packet was received
-i the IP source address of the packet
-m and the Ethernet source address of the packet
If the response packet’s yiaddr is non-zero and falls
within a "Lease Networks of Concern", the following
optional options are also passed:
-y the non-zero yiaddr value
(We do not wait for the alert_program_name2 to complete;
it runs in a child process.)
5. Remove the pcap(3) filter installed earlier.
6. If any signals have arrived requesting that we quit,
exit gracefully.
7. Repeat steps 2-6 for each flavor of DHCP and BootP
request packet the program supports (see PACKET FLAVORS
below).
8. Handle any signals that have been received.
9. Sleep for cycle_time seconds. (The cycle_time defaults
to 300 seconds, and and may be changed in the
dhcp_probe.cf(5) file.)
The pcap(3) filter the program installs normally does not specify that
the interface should be placed into promiscuous mode (although it is
possible the interface is already in promiscuous mode for some other
reason). However, if in the dhcp_probe.cf(5) file you specify a chaddr
or ether_src value other than the interface’s actual hardware address,
then the pcap filter will specify that the interface should be placed
into promiscuous mode.
Although the filter used with pcap(3) specifies only UDP packets
destined to port bootpc should be collected, on systems where bpf isn’t
part of the kernel, pcap(3) must implement bpf as part of the
application. This can increase the number of packets that must be
passed from the kernel to user space to be filtered. The program
attempts to minimize the side-effects of this by removing the pcap(3)
filter when it isn’t actually listening for responses. In particular,
the filter is not installed during the time the program sleeps between
each probe cycle (the cycle_time).
If you do specify an alert_program_name, take care that the program you
specify is safe for a privileged user to run; it is executed with the
same (i.e. root) privileges as the calling program.
PACKET FLAVORS
No single request packet is likely to provoke a response from every
possible BootP and DHCP server. Some servers may only response to
either BootP, or DHCP, but not both. Some servers may be configured to
only respond to a small set of known clients. Some DHCP servers will
only provide leases to a small set of known clients, but may be willing
to respond (negatively) to unknown clients that request a lease renewal
on an inappropriate IP address. Therefore, dhcp_probe actually sends
not one, but five different flavor request packets, in the hopes of
provoking responses from a wider variety of unknown servers.
The packet flavors are:
BOOTPREQUEST
This packet is typical of a BootP client requesting an IP
address.
It will typically provoke a BOOTPREPLY from a BootP server
willing to respond to any BootP client. (BootP servers
configured to only respond to a set of known clients may not
respond.)
DHCPDISOVER (INIT)
This packet is typical of a DHCP client in the INIT state.
The options field contains a DHCP Message Type specifying
DHCPDISCOVER.
The options field contains a DHCP Client Identifier, which is
computed by prepending 0x’01’ to the value of chaddr. (The
value chaddr is specified in the dhcp_probe.cf(5) file,
otherwise it defaults to the interface’s Ethernet address.)
This packet will typically provoke a DHCPOFFER from a DHCP
server willing to respond to any DHCP client. (DHCP servers
configured to only offer leases to a set of known clients may
not respond.)
DHCPREQUEST (SELECTING):
This packet is typical of a DHCP client in the SELECTING state;
i.e. a client which has previously issued a DHCPDISCOVER, then
received a DHCPOFFER from some DHCP server.
The options field contains a DHCP Message Type specifying
DHCPREQUEST.
The options field contains a DHCP Client Identifier, which is
computed by prepending 0x’01’ to the value of chaddr. (The
value chaddr is specified in the dhcp_probe.cf(5) file,
otherwise it defaults to the interface’s Ethernet address.)
The options field contains a DHCP Server Identifier specifying
server_id, which should be an IP address that does not
correspond to any valid DHCP Server Identifier on your network.
(The value server_id is specified in the dhcp_probe.cf(5) file,
otherwise it defaults to 10.254.254.254.)
The options field contains a DHCP Requested IP Address
specifying client_ip_address, which should be an IP address that
does not correspond to any valid IP address on your network.
(The value client_ip_address is specified in the
dhcp_probe.cf(5) file, otherwise it defaults to 172.31.254.254.)
This packet occassionally provokes a response from a broken DHCP
server that fails to respect the DHCP Server Identifier option.
DHCPREQUEST (INIT-REBOOT):
This packet is typical of a DHCP client in the INIT-REBOOT
state; i.e. a client which has obtained a DHCP lease in the
past, is bringing up its IP stack, and hopes to obtain (or
extend) a DHCP lease on the same IP address as in the past.
The options field contains a DHCP Message Type specifying
DHCPREQUEST.
The options field contains a DHCP Client Identifier, which is
computed by prepending 0x’01’ to the value of chaddr. (The
value chaddr is specified in the dhcp_probe.cf(5) file,
otherwise it defaults to the interface’s Ethernet address.)
The options field contains a DHCP Requested IP Address
specifying client_ip_address, which should be an IP address that
does not correspond to any valid IP address on your network;
ideally it should be one that is topologically inappropriate for
your network. (The value client_ip_address is specified in the
dhcp_probe.cf(5) file, otherwise it defaults to 172.31.254.254.)
If the Requested IP Address option is topologically
inappropriate for your network, this packet may provoke a
DHCPNAK from any DHCP server that believes it is authoritative
for the network’s IP topology.
DHCPREQUEST (REBINDING)
This packet is typical of a DHCP client in the REBINDING state;
i.e. a client which has obtained a DHCP lease which is between
its DHCP T2 and expiration time.
The options field contains a DHCP Message Type specifying
DHCPREQUEST.
The options field contains a DHCP Client Identifier, which is
computed by prepending 0x’01’ to the value of chaddr. (The
value chaddr is specified in the dhcp_probe.cf(5) file,
otherwise it defaults to the interface’s Ethernet address.)
The ciaddr field contains client_ip_address, which should be an
IP address that does not correspond to any valid IP address on
your network; ideally it should be one that is topologically
inappropriate for your network. (The value client_ip_address is
specified in the dhcp_probe.cf(5) file, otherwise it defaults to
172.31.254.254.)
If the value of ciaddr is topologically inappropriate for your
network, this packet will provoke a DHCPNAK from any DHCP server
that believes it is authoritative for the network’s IP topology.
All the request packets sent by the program share the following common
characteristics:
Ethernet Header
destination: ff:ff:ff:ff:ff:ff
source: ether_src from dhcp_probe.cf(5), else interface
hardware address
type: ETHERTYPE_IP (0x0800)
IP Header
version: 4
header length: 5
tos: 0
total length: 328 (20-byte IP header + 8-byte UDP header +
300-byte BootP/DHCP payload)
identifier: 1
flags: 0
fragment offset: 0
ttl: 60
protocol: IPPROTO_UDP (17)
header checksum: (computed)
source address: 0.0.0.0
destination address: 255.255.255.255
options: (none)
UDP Header
source port: PORT_BOOTPC (68)
dest port: PORT_BOOTPS (67)
checksum: (computed)
BootP/DHCP Payload
op: BOOTREQUEST (1)
htype: HTYPE_ETHER (1)
hlen: HLEN_ETHER (6)
hops: 0
xid: 1
secs: 0
flags: 0
ciaddr: 0.0.0.0 (except for DHCPREQUEST (REBINDING) packets
it is client_ip_address from dhcp_probe.cf(5), else
172.31.254.254)
siaddr: 0.0.0.0
giaddr: 0.0.0.0
chaddr: chaddr from dhcp_probe.cf(5), else interface hardware
address
sname: (all 0’s)
file: (all 0’s)
options: RFC1048 cookie (0x63825363), possibly followed by
DHCP options, followed by END option (0xFF), followed by PAD
options (0x00) to bring the field to 64 bytes
MULTIPLE INTERFACES
Although dhcp_probe only supports monitoring a single physical
interface, you may run an instance of the program on each physical
interface; each monitors a different physical network.
When running multiple copies of dhcp_probe, be sure to specify a
different pid_file for each instance.
If you specify a log_file and/or a capture_file, be sure to specify a
different one for each instance.
You may specify a different config_file for each instance. If you
don’t need to customize the settings in that file for each instance,
you may use the same configuration file for all instances.
If you have multiple logical interfaces on the same physical interface,
or multiple logical IP networks running on a single physical network,
there is no need to run multiple instances of dhcp_probe to monitor
each logical interfaces or logical network. A single instance of the
program running on a physical interface is sufficient to provoke any
servers on that physical network that might be willing to respond.
If your physical interface supports 802.1Q, you can use a single
physical interface to monitor multiple VLANs. Use your operating
system to create a logical interface on each VLAN, then run an instance
of the program on each logical interface. Since the program is
responsible for constructing Ethernet frame headers, you will probably
need to specify the -Q option to instruct it to add to outgoing frames
an 802.1Q VLAN header with the appropriate VLAN ID.
SIGNALS
The program will respond to a number of signals:
SIGUSR1
If logging to a file, close and re-open it. If the program is
in the middle of a probe cycle, handling the signal is deferred
until the end of the cycle. (Has no effect if logging to
syslog(3) or if the -f option was specified.)
SIGUSR2
If capturing to a file, close and re-open it. If the program is
in the middle of a probe cycle, handling the signal is deferred
until the end of the cycle. (Has no effect if the -o option was
not specified.)
Because re-opening the capture file causes the file to be
truncated and a new pcap(3) header to be written to it, if you
want to save the prior contents of the capture file, move the
existing capture file aside before sending the signal.
SIGHUP Reread the configuration file. If the program is in the middle
of a probe cycle, handling the signal is deferred until the end
of the cycle.
SIGTERM, SIGINT, SIGQUIT
Exit gracefully. If the program is in the middle of a probe
cycle, handling the signal is deferred until the program
finishes sending and receiving responses for the current flavor
request packet.
LEASE NETWORKS OF CONCERN
Most rogue BootP/DHCP servers distribute private IP addresses to
clients, or send DHCPNAK messages to legitimate clients. Some even
more disruptive rogue BootP/DHCP servers may distribute IP addresses
that fall within your own networks’ IP ranges. The "Lease Networks of
Concern" feature is intended to help you identify these particularly
disruptive servers.
You may activate the feature by specifying the lease_network_of_concern
statement in your configuration file. Use the statement multiple times
to specify all your legitimate network ranges.
When a rogue BootP/DHCP server is detected, if the rogue’s response
packet contains a non-zero yiaddr value, the value is compared to the
"Lease Networks of Concern" you specified. If the value falls within
any of those network ranges, the message logged by dhcp_probe is
extended to make note of this, and to report the yiaddr value.
Furthermore, if you are using the alert_program_name2 feature, the
alert program is called with an extra -y yiaddr option so that alert
program can take any additional action desired.
DEBUG LEVELS
The program produces increasingly detailed output as the debuglevel
increases. Under normal circumstances, you can run at debuglevel 0.
Here’s roughly what messages are added at each debuglevel.
0 Display the IP source (and Ethernet source) of each unexpected
DHCP or BootP response packet.
Startup and shutdown notice.
Non-fatal errors in the configuration file.
Fatal errors.
1 At startup, show some information about the program’s
configuration.
2 Show each time we start and finish (re-)reading the configuration
file.
Show each time we close and re-open the logfile or capture file.
Report on response packets that could not be parsed (e.g.
truncated).
3 Each time we (re-)read the configuration file, echo the
information we obtain from it.
7 For each parsable response packet, show the Ethernet source and
destination, the IP source and destination, and indicate when the
IP source is a legal (known) server.
11 For each probe cycle, show when the cycle begins and ends, when
we write a packet, and when we begin and end listening for
response packets.
AUTHOR
The program was written by Irwin Tillman of Princeton University’s OIT
Network Systems Group. It was written to run on Solaris, relying on
the generally-available pcap(3) and libnet(3) libraries.
FILES
/etc/dhcp_probe.cf
Configuration file read by the program. See dhcp_probe.cf(5).
The name of this file can be overridden by a command-line
option.
/etc/dhcp_probe.pid
Contains the program’s processid. The name of this file can be
overridden by a command-line option.
LIMITATIONS
dhcp_probe is not guaranteed to locate all unknown DHCP and BootP
servers attached to a network. If a BootP server is configured so it
only responds to certain clients (e.g. those with certain hardware
addresses), it will not respond to the BOOTPREQUEST packet we sent. If
a DHCP server is configured so it only responds to certain clients
(e.g. those with certain hardware addresses or DHCP Client
Identifiers), it will not respond to the packets we send that mimic
DHCP clients in the INIT state. If a DHCP server is configured so it
does not send DHCPNAK packets to clients requesting topologically-
inappropriate IP addresses, it will not respond the packets we send
that mimic DHCP clients in the INIT-REBOOT and REBINDING states.
The upshot is that it is possible that dhcp_probe will be unable to
provoke some BootP and DHCP servers into responding at all.
Flushing out such servers can be extremely difficult. One approach is
to capture all UDP/IP packet destined to the BootP client port which
cross your network; since most of these packets are unicast at Layer 2,
capturing is only effective if all such packets must pass by your
capture device’s Ethernet interface (e.g. the capture device is located
at a network choke point, or the network does not involve any Layer 2
switching). Another approach is to do UDP port scanning for all
devices listening on the BootP server port, and assume that those which
are listening on that port are running a BootP or DHCP server.
Malicious BootP or DHCP servers that forge the IP source address (and
possibly the Ethernet source address) of their responses to match the
values specified by legal_server and legal_server_ethersrc statements
will not be detected.
BUGS
The packet capture buffer size is limited; if a single request packet
provokes more responses than will fit into the buffer, those that do
not fit are silently dropped, without any diagnostic indicating that
the buffer was too small. You can adjust the size of the packet
capture buffer size using the -s capture_bufsize option.
We do not support non-Ethernet interfaces.
Because (re-)opening a packet capture file causes the file to be opened
for writing (not appending), the contents of any existing packet
capture file of the same name is lost when the program starts or
receives a SIGUSR2 signal. If the file’s previous contents should be
preserved, move the old file aside before starting the program or
sending it a SIGUSR2 signal. (This "feature" exists because opening a
pcap(3) savefile always involves writing a pcap header record to the
start of the file, so pcap always opens the file using mode "w".)
Because pcap(3) opens the packet capture file with a simple fopen(3)
without checking to see if the file already exists, dhcp_probe may be
tricked into overwriting or corrupting an existing file. As dhcp_probe
is run with root privileges, this is a serious concern. To avoid this
problem, if you use the -o option, ensure that the directory that will
contain the capture file is writable only by root.
The packet capture file that is written is unparseable after the first
packet. E.g. if read with tcpdump(8), it reports: tcpdump: pcap_loop:
truncated dump file.
On platforms where pcap(3) is unable to support the timeout argument to
pcap_open_live, the program may not reliably detect responses from DHCP
and BootP servers, or may not function at all.
SEE ALSO
dhcp_probe.cf(5)
pcap(3) (a.k.a. libpcap, a packet capture library), available from
http://www.tcpdump.org. (An older version is available from
ftp://ftp.ee.lbl.gov/libpcap.tar.Z.)
libnet(3) (a.k.a libwrite, a packet writing library), available from
http://www.packetfactory.net/libnet