|
|
|
|
Project 2
Project Overview.
|
In Project 2, you will hack on the WRT54GL wireless
routers. In Part 1, you will install and configure the routers, and
setup the necessary cross-compilation development environment to
compile C programs for the router. In part 2, your task will be to
create a web server that serves a simple 'hello world' page from a
file. Finally, in part 3, you will develop software to capture
interesting activity from the router's network vantage point and have
this information be served by the web server you've developed in part
2.
|
Part 1.
|
For information on setting up and configuring the router,
please see
the slides
from the discussion section on 10/16.
|
Due Date.
Due Friday, October 24th, at midnight
via catalyst.
|
Part 2.
|
Overview
|
In this part of the project you will create a minimal web server to
serve text and html files. Your webserver will have a single dynamic
component for listing files in a directory, otherwise it will only
need to read and send file contents to clients. Your server must
support multiple clients using the select() call or pthreads (see
below), and compile and work on the router and on
attu.cs.washington.edu. A builtroot environment will be provided for
compiling your webserver for the router.
|
Strategy
|
Here is the recommended set of strategies for developing your
web-server:
- Do all your development work on attu.cs.washington.edu
- Begin by writing a server that supports a single client
- After you have completed all the requirements for a server
supporting a single client, you should then implement a server capable
of supporting multiple clients.
- Only after you have completed all the requirements should you
begin work on porting your server to the router
To test your implementation you can use a combination of netcat, curl,
wget, firefox and telnet.
Use netcat (aka nc), to understand how the HTTP protocol
works. Start by running "nc -l 9999" on attu to run a listening server
on port 9999. Then connect to the server with firefox by going to
"http://attu.cs.washington.edu:9999". You should see an HTTP GET
request appear as server output. You can now respond to firefox by
sending it commands directly from the server's terminal window. Type
"HTTP/1.0 OK 200\n\nHello World^d" where the "\n" character indicates
a line-break, and "^d" stands for Control-d (hold control and press
d). You should now see "Hello World" appear in the firefox
window. This exchange illustrates how HTTP works. See the next section
entitled "How does HTTP work?" for more details.
Use curl, and wget to test your server for simple headers
that these programs generate and to inspect the headers exchanged
between your server and these clients. Use telnet to manually check
that your server supports multiple clients, correctly times out
unresponsive clients, and performs error checking on the header. Feel
free to automate these by writing a client of your own. Finally, you
can fine-tune the look of the web-pages that your server generates
with firefox.
|
How does HTTP work?
|
HTTP is a client-server request-response protocol. All communication
happens over a TCP connection. The server listens on a TCP port, and
accepts new connections. The client connects to the server and sends
an HTTP Request. The server responds with an HTTP Response. For this
class, your webserver should service a single HTTP Request per
connection (the full HTTP protocol supports multiple requests per
connection).
HTTP Requests and Responses are in ascii. Here is an example of an
HTTP Request that a server may receive (line numbers do not appear in
the actual transmission):
1. GET / HTTP/1.1
2. User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1
3. Host: google.com
4. Accept: */*
5.
Line 1 of this request contains the HTTP Method (GET), the
request URI (/), and the HTTP version (HTTP/1.1). For this project,
your web-server can ignore all other request header fields.
Note that the request is terminated by a single blank line (line
5). This is used to indicate to the server that the client has
finished sending the HTTP header.
The server's response to this request might look like the following:
1. HTTP/1.1 200 OK
2. Date: Wed, 29 Oct 2008 03:24:19 GMT
3. Server: Apache/2.2.8 (Fedora) DAV/2 mod_pubcookie/3.3.3 mod_ssl/2.2.8 OpenSSL/0.9.8g
4. Accept-Ranges: bytes
5. Transfer-Encoding: chunked
6. Content-Type: text/html
7. Content-Language: en
8.
9. ... HTTP CONTENT ...
...
Line 1 of this response contains the HTTP version (HTTP/1.1), the
status code (200), and the reason phrase (OK). This is then followed
by some number of response headers. For this project, your web-server
does not need to generate any other response header fields.
Note that like the HTTP request, the response is also terminated with
a blank line (line 8). The lines immediately following the blank line
(line 9+) contain HTTP content.
Your server can assume that the client sends a single HTTP request,
and can close the TCP connection after sending the HTTP response to
the client.
|
Project requirements
|
A web-server that..
- Takes a single server port argument on the command line
- Compiles for attu, and compiles for the router
with buildroot.
- Works with firefox
- Support HTTP versions 1.0 and 1.1
- Serves files out of the current directory
- Shows a page listing available files for URI '/'
- Handles multiple clients
By "works with firefox," we mean that if your server is run on attu on
port X, and there is a file in the current directory where the server
was run called F.txt, then typing
"http://attu.cs.washington.edu:X/F.txt" in the firefox location bar
should bring up the contents of the file F.txt in firefox window.
Your web-server is required to generate only three status codes and
respective reason phrases:
200 OK
400 Bad Request
404 Not Found
A minimal web-server will (1) read the request line: request
method, request URI, HTTP version, (2) wait for a blank line (3)
respond with a Bad Request error message if an invalid/incomplete
request is received; close the connection if the client times out
(after 5 seconds) (5) respond with content of request file (or list
current directory contents) using a valid HTTP 1.0 response message
and (6) returns a status code of 200, 400, or 404.
|
Serving Files
|
Your web-server should serve files from its current directory. That
is, when you run the web-server program, the program's current
directory will be some directory (e.g., a subdirectory of your home
directory). HTTP GET requests for files in only that directory should
be satisified. In other words, if you get a request like "GET
/hello.html HTTP/1.0", you should return the file "hello.html" that is
located in the current working directory of the web server. For this
project it is assumed that the web-served serves HTML and plain text
files only.
|
Listing files
|
For listing available files for URI '/', you will need to be able to
read the contents of the current directory. To read a directory in C
use opendir(). Then call readdir() repeatedly to read the contents,
and finally call closedir() to close the directory handle. Use the
readdir
manpage as a starting point for finding out further information.
You may return the listing of files in plain text, with one entry per
line, or as formatted HTML, either of these is acceptable.
|
Writing a TCP server
|
In project 1 you wrote a minimal TCP client. This project will teach
you how to write a TCP server that handles multiple clients. A TCP
server differs from a TCP client in only a few socket calls. The basic
series of calls you will make in a TCP server is the following:
- socket() - create the server's socket
- bind() - associate the socket to a known port
- listen() - wait for incoming connections requests
- accept() - accept a new connection
- recv(),send() - handle the connection
- close() - release allocated socket resources
The two new calls introduced by this project are listen() and
accept(). The listen function has the following prototype:
int listen(int sockfd, int backlog);
This function is non-blocking and will make a newly allocated socket
into a 'passive' socket -- the socket can only be used to accept new
connections but not receive or send data. The backlog argument
indicates the number of client connections that may be queued waiting
to be accepted on the socket. For this project, set backlog to 10
or higher.
The accept function has the following prototype:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
The first argument to accept() is the listening socket. This call
returns a socket file descriptor of the new socket associated with an
accepted connection. Note that accept is a blocking call.
The single-client TCP server pseudocode looks like this:
s=socket() // s is a passive (listening) socket
bind(s)
listen(s)
while (1) {
s' = accept(s) // s' is an active socket
request = recv(s')
response = process(request)
send(response)
close(s')
}
|
Handling multiple clients
|
There are two recommended ways for handling multiple simultaneously
connections to the server. The first is to use the select() call. Here
is a pseudocode showing how you would use select (s and s' are defined
as above),
FD_ZERO(rfdset)
while (1) {
rfdset' = rfdset
ret = select(rfdset', NULL,...)
if FD_ISSET(s,rfdset') {
s'=accept(s)
FD_SET(s',rfdset)
} else {
process(rfdset')
}
}
Note that a passive socket with an outstanding connection is defined
as 'readable', and select will return the readset argument passed to
it with this socket's file descriptor 'set.' The above code checks for
this in the first line of the if statement.
A second way you may handle multiple connections is to use
threads. Here is pseudocode showing how you would use threads,
void run(s') {
request = recv(s')
response = process(request)
send(response)
close(s')
}
while (1) {
s' = accept(s)
tid = pthread_create(0,0,run,s')
pthread_detach(tid) // auto-reap
}
To find out more information about these two methods, run 'man select'
for select, or 'man pthread_select' on attu.cs.washington.edu.
|
Porting the web-server to the router
|
As with project 2, part 2, to port the web-server to the router simply
compile your application using gcc compiler for WRT54GL platform
provided on attu. On attu, it is located here: /homes/iws/ivan/bin/gcc
Using this compiler you should be able to compile any web-server that
does not use pthreads. If your server uses pthreads then you
should follow the following steps.
1. If your wireless router is connected to the internet. Then run the
following two commands:$ ipkg update
$ ipkg install libpthread
The last command should download, configure and install the pthreads
library onto the router.
2. If your wireless router is not connected to the
internet. You should
download
the pthreads package. Then transfer it to the router (using scp), and
finally run the following command on the router:ipkg install
libpthread.ipk
Once you install libpthread on the router, programs that are compiled
using gcc -lpthread on attu will work on the router.
Note that the router runs a webserver by default. To use port 80 for
your web-server you will need to disable the default webserver. To do
this, run the following commands as the root user on the router
$ mv /etc/rc.d/S50httpd /etc/rc.d/K50httpd
$ killall httpd
|
Extra-Credit
|
If you would like to receive extra credit, there are a number of
extensions to the web-server described above. You may consider some of
the following ideas:
- Implements more than the minimum protocol (e.g. other response codes, POST method..)
- Implements some security
- Serve other dynamic content (besides listing files)
You may also propose an original idea. Just email Ivan.
|
Due Date.
|
NOTE: deadline extended to November 7th
The project is due on Friday, November 7th at 11:59PM.
Please hand in a tarball or a zip file of a directory that contains
the source and instructions on how to compile your web-server, for the
router and atu.cs.washington.edu. Submissions must be made
via catalyst.
|
Part 3.
|
Enable BOOT_WAIT on your router
|
If you mis-configure your router, you will need to re-flash it. To
facilitate this, you should all run the following two commands as root
on the router:
$ nvram set boot_wait=on
$ nvram commit
Please run these commands before proceeding with this part of the
project. For more information on why this is necessary, you can read
the following.
|
Overview
|
Now that you have your web server running on the router, in this final
part of Project 2, we would like for you to have the web server report
simple statistics about the network state of the router. We would like
for you to report some minimum information, and there is other
information that you can decide to report for extra credit. Feel free
to add other information that you think would be interesting and/or
insightful for use in Project 3!
|
Required Information
|
- Number of times the router rebooted
- A running list of MAC addresses observed by the router on the
LAN. You can exclude the router's MAC address if you wish, as well as
the MAC address of wherever the packets are sent to by the router to
reach the internet (to your ISP).
- A running total of the number of broadcast packets seen on the
LAN by the router
- Average bandwidth utilization, in bytes per second, over (1) last
minute, (2) last hour, (3) last 6 hours of the network device being
monitored by your program.
|
How to track number of reboots?
|
Here is one of many strategies for tracking the number of times the
router has been rebooted.
Maintain the count of reboots in a file and use your web-server to
only serve file content as in Part 2. Next, make your server start
automatically when the router boots. To do so, follow the following
instructions (note that you can easily experiment with this procedure
by using a daemon that does a sleep(1) in an infinite loop):
- Place your daemon into /etc/ on the server (note: /tmp/ gets wiped
on every router reboot!)
- Make sure that your daemon is executable and works as expected
from the command line
- Download, the following sample startup
script. Modify this script to refer to your program in /etc/. Note
that the ampersand (&) sign is important -- do not remove it. Upload
your modified startup script into /etc/init.d/ on the router
- Chmod this script to 755, i.e. chmod 755 /etc/init.d/myserver
- Note that you can now start your daemon using:
/etc/init.d/myserver start, and stop it using: /etc/init.d/myserver
stop
- Create a symlink to this file from /etc/rc.d/, using the command:
ln -s /etc/init.d/myserver /etc/rc.d/S90myserver. The name of this
symlink needs to start with S90, or use a similar number that does not
conflict with other files in /etc/rc.d/.
- Reboot and check that the daemon is running by doing ps | grep
[mydaemon]
Finally, whenever your router reboots, your webserver can update the
number of reboots value inside the file. Alternatively you can read
the /proc/uptime file on web-server and update the number of reboots
whenever uptime < last uptime.
|
Using pcap : overview
|
To collect the other three pieces of dynamic information, you will
need to use pcap. Below is an outline of steps you would implement to
use pcap in your code. The next section gives a brief description of
each of these steps, and the last section after will present the
details of the cross-compilation procedure.
- Determine which device(s) to sniff on
- Tell pcap which device(s) to sniff on
- Create, compile, and apply sniffing filters
- Tell pcap to enter an execution loop
- Receive packets via a callback function
- Close pcap session
|
Details of using pcap
|
To help you understand this section please download and use the
following two pieces of code as samples for your
project: pcap_list_devs.c lists the
known devices on the current
host; sniffex_ip.c captures all ip packets
sent\received on the default interface used by the host.
To determine which devices to sniff on you can use a number of
strategies. You can make use of ifconfig to determine the
device names. Alternatively you can compile and
run pcap_list_devs.c. You may also
use the all device name, however, this may not always work as
all available devices must then support the flags you will use when
initializing a pcap handle (below).
Pcap is initialized using the pcap_open_live() function call. This
call takes a number of arguments. Here are some details on these arguments:
- device : device name (from prior step)
- snaplen : max number of bytes to capture
- promisc : promiscuous mode (1: capture packets not destined for nor generated by this host, or 0: to not do so)
- to_ms : ms internal read waits before timing out (see pcap_dispatch() execution loop explanation) Returns a handle used for all further interactions with pcap
The pcap_open_live() call returns a pcap handle that you will
use for all further interactions with pcap.
Your next step is to setup pcap filters. Pcap uses the tcpdump filter
syntax, making it extremely simple to understand and construct its
filters (for more information on the syntax of tcpdump filters, refer
to the links listed in the additional pcap resources section
below). As an example, the filter 'ip' will only capture ip packets,
while the filter 'port 23' will only capture packets destined for and
sent on port 23. Once you have a filter string, you will need to
compile it into a structure used by pcap. You perform this compilation
using the pcap_compile() function. Once you have compiled your filter,
you have to associate the filter with a specific pcap handle. To do
so, use the pcap_setfilter() call.
Once you have your filters ready, you have to decide on an execution
loop used by pcap. There are three options:
- pcap_next : capture just one packet
- pcap_loop : capture # of packets
- pcap_dispatch : capture until internal read times out
For this project, the execution loop provided by pcap_loop() is most
appropriate, but feel free to use either of three listed above.
Finally, you should always close the pcap session and release pcap
allocated memory. The following two functions are used for this.
- pcap_freecode : frees filter-related allocations
- pcap_close : releases a pcap handle
|
Cross compiling pcap programs
|
To cross compile a program called filename.c that uses pcap, run the
following command on attu.cs.washington.edu:
/homes/iws/ivan/bin/gcc [filename.c] /homes/iws/ivan/libpcap.a
Then move and execute this binary on the router.
If you would like to debug your programs off the router, you will need
to find a machine on which you have root privileges -- linux of osx
work out of the box. You can compile on this machine using the -lpcap
flag and then run the resulting binary as root.
|
Extra Credit Information
|
You may additionally decide to display other dynamic information with
web-server. Here are a few example of what you might want to include.
- Frequency table of ports used by packet observed by the router
- Traffic statistics for an observed IP/MAC address or a domain
- Periodic sampling of traffic flows to minimize capturing overhead, and relevant statistical measures
- Display the active MACs observed over the last (1) minute and/or
(2) hour.
Feel free to propose an original idea missing from this list. Just
email Ivan.
|
Additional pcap and related resources
|
|
Due Date
|
This project is due on Friday, November 14th at 11:59 PM.
Please hand in a tarball or a zip file of a directory that contains
your source files and instructions on how to setup the router to
collect the information your web-server will display. Make sure to
include all the necessary scripts, and files with instructions on
where they have to be placed on the router. Submissions must be made
via catalyst.
|
|