CSE logo University of Washington Computer Science & Engineering
 CSE 461: Computer Communication and Networks (Autumn 2008)
  CSE Home   About Us    Search    Contact Info 

Course home
 Home
Administrivia
 Overview
 Using course email
 Email archive
Schedule
 Lectures and readings
 Section and tutorials
 Midterms and exams
Assignments
 Homework
 Projects
Lab information
 Getting lab accounts
 Unix tutorials
   

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.


CSE logo Computer Science & Engineering
University of Washington
Box 352350
Seattle, WA  98195-2350
(206) 543-1695 voice, (206) 543-2969 FAX