Implementation requirements
This project builds on Project 2 but from the
perspective of the server. In this project, you will not need to focus as much
on the interpretation and manipulation of the binary data, as we have provided
some helper functions that provide that functionality for you. Instead, your
code will focus on receiving UDP messages from a client and constructing the
appropriate response.
As with the previous project, this project will be built incrementally.
Your first task involves responding to a single client message, then shutting
down. Next, you will implement the server responses for the full four-message
sequence, assigning IP addresses for up to four clients. You'll then add
support for releasing an IP address. Finally, you'll add a minimal
multithreaded structure to emulate the behavior of a more realistic server.
Phase 1: DHCP echo server (partial credit)
For this phase, you will implement the server partner of the client from
Phase 3 of Project 2. In that step, the command-line
option -x 0
served as a special case where the client sent a single
DHCPDISCOVER
message and the server replied with a
DHCPOFFER
. Your code will need to set up a server socket, receive
the message from a client, construct the response, and send it back.
Similarly, your echo server will also respond to DHCPREQUEST
messages based on the request's server ID option. Throughout this project, you
will assume that your server is operating on 192.168.1.0 (note, though, that
your actual socket communication will use 127.0.0.1 for all communication). If
the DHCPREQUEST
includes a server ID that matches, then you will
respond with the corresponding DHCPACK
. Otherwise, you will send
a DHCPNAK
to indicate the request failed. You do not need to do
any other error checking.
As in the previous project, you will need to consult
RFC 2131 and
RFC 2132 to determine the
fields that are required for each message type. Note that many of the fields
in the BOOTP struct (such as chaddr
) are identical between the
client and server messages. Consequently, you can simplify your implementation
by using the received data as the basis of your response.
Testing your server
Throughout this project, you are building a server. Servers typically execute
in the background without a user directly viewing its STDOUT output. To capture
this nature, all output from your code will be discarded by
make test
. To assist with debugging, you can print out
anything that you want, but the testing framework will be based solely on output
from the provided client
program.
To test your code, you should use two windows. In one window, you should
execute your server by running ./dhcps -s 10
to keep the server
running for 10 seconds before shutting down. In a second window, you will
run the provided ./tests/client
program with one of the text
files in tests/data
. You do not need to understand or modify the
structure of these files, but an explanation is provided below for those
interested.
Phase 2: A DHCP server (C requirements)
In this phase, you'll extend your echo server by implementing the full
four-message sequence for DHCP. That is, you will first receive a
DHCPDISCOVER
and reply with a DHCPOFFER
that
includes a unique IP address. The client will then send a DHCPREQUEST
.
If the IP address and server ID match, you will respond with a
DHCPACK
. However, if there is a mismatch for either, you will
send a DHCPNACK
.
You'll need to support up to 4 clients, assigning the IP addresses
192.168.1.1, 192.168.1.2, etc. If you receive a 5th DHCPDISCOVER
,
you should reply with a DHCPNAK
.
Phase 3: Tracking and releasing assignments (B requirements)
For this phase, you'll need to build a mechanism for tracking the IP addresses
that have been assigned to a given chaddr
. You'll need to do this
for a couple of reasons. First, messages might be interleaved. For example,
assume you originally encounter the following sequence:
Client 1 (xid=15, chaddr=1234) sends DHCPDISCOVER
Server offers 192.168.1.1 to Client 1
Client 1 sends DHCPREQUEST for 192.168.1.1
Server sends ACK to Client 1
Client 2 (xid=99, chaddr=5566) sends DHCPDISCOVER
Server offers 192.168.1.2 to Client 2
...
You now have to support the following sequence:
Client 1 (xid=15, chaddr=1234) sends DHCPDISCOVER
Server offers 192.168.1.1 to Client 1
Client 2 (xid=99, chaddr=5566) sends DHCPDISCOVER
Server offers 192.168.1.2 to Client 2
Client 1 sends DHCPREQUEST for 192.168.1.1
Server sends ACK to Client 1
...
The second reason for tracking offers is to add support for
DHCPRELEASE
messages. These are sent by clients voluntarily to
allow an IP address to be re-assigned. When you receive a DHCPRELEASE
,
you do not send a reply to the client.
It is important to note one key feature that goes
along with DHCPRELEASE
actions: servers try to assign the same
IP address to subsequent DHCPDISCOVER
messages from the same
chaddr
. That is, if a client previously used 192.168.1.3, sent a
DHCPRELEASE
to release its DHCP lease, then sends a subsequent
DHCPDISCOVER
, the server should again offer 192.168.1.3 unless
it has been assigned to a different client.
There are many ways you could implement this
tracking and removal of records. A simple way would be to create an array
of four records, each of which contains the chaddr
and a
"tombstone" record, which are commonly used in data structures like
hash tables.
When you use a tombstone, you just mark the record as no longer in use but
leave the data otherwise intact.
Phase 4: Once more with threads (A requirements)
In this final phase, you'll emulate part of the behavior of a multithreaded
server. Instead of processing the requests right away, your server will collect
the requests into a small queue until the queue is full. Once the queue is full,
your server will process all the requests.
Given that implementing a multithreaded server can be a HUGE
undertaking, we are going to greatly simplify the task so that it is feasible
but you still get some insight into how to implement this. The only test case
for this feature will start by sending 5 DHCPDISCOVER
messages.
After receiving the fourth, you should start a separate thread to handle those
four messages. Later, you will receive DHCPRELEASE
messages. You
should then start a separate thread for the fifth original request.
Testing notes
There is a single test case for this phase. Due to the nature of multithreaded
programs, it is possible that the output cannot match the expected output as a
result of timing issues. When grading this phase, we will also run your code
manually and inspect the implementation to determine if it is correct.
Additional notes
The files used by the provided client program contain information about how
to generate messages in a predictable manner. These files contain lines such as the
following:
1 1 42 010102020303:...
3 1 42 010102020303 192.168.1.0 192.168.1.1:[...]
Each line starts by indicating the message type (1=DHCPDISCOVER on the first
line 3=DHCPREQUEST on the second), then the hardware type (1=ETH for both), and
the hardware address (010102020303 is chaddr
). The DHCPREQUEST
message must also include a server ID (192.168.1.0) and requested IP address
(192.168.1.1). Note that the information after the ":" is just to provide a
human-readable interpretation and is not actually used.