Network Programming
Assignment 3

Last updated November 2.

Due Wednesday, November 18th.

You are to work on this assignment in groups of two.

As I get feedback from students and realize that parts of this write-up  need to be extended, corrected, and/or further clarified, I shall update the contents and the Last Updated date. Updated / modified parts will be in bold blue for easy identification. Please check this web page periodically to make sure you have the latest version.



Overview

For this assignment you will be developing and implementing :

I shall be discussing the assignment in class on Tuesday, April 1, and Thursday, April 3.

The following should prove useful reference material for the assignment :

The  VMware  environment

minix.cs.sunysb.edu is a Linux box running VMware. A cluster of ten Linux virtual machines, called vm1 through vm10, on which you can gain access as root and run your code have been created on minixVMware instructions  takes you to a page that explains how to use the system. The ten virtual machines have been configured into a small virtual intranet of Ethernet LANs whose topology is (in principle) unknown to you.

There is a course account cse533 on node minix, with home directory /home/users/cse533. In there, you will find a subdirectory Stevens/unpv13e , exactly as you are used to having on the cs system. You should develop your source code and makefiles for handing in accordingly. You will be  handing in  your source code on the minix node.

Note that you do not need to link against the socket library (-lsocket) in Linux. The same is true for -lnsl and -lresolv. For example, take a look at how the LIBS variable is defined for Solaris, in
/home/courses/cse533/Stevens/unpv13e_solaris2.10/Make.defines (on compserv1, say) :

LIBS = ../libunp.a -lresolv -lsocket -lnsl -lpthread

But if you take a look at Make.defines on minix
(/home/users/cse533/Stevens/unpv13e/Make.defines) you will find only:

LIBS = ../libunp.a -lpthread

The nodes vm1 , . . . . . , vm10 are all multihomed :  each has two (or more) interfaces. The interface  ‘eth0 ’  should be completely ignored and is not to be used for this assignment (because it shows all ten nodes as if belonging to the same single Ethernet 192.168.1.0/24, rather than to an intranet composed of several Ethernets).

Note that vm1 , . . . . . , vm10 are virtual machines, not real ones. One implication of this is that you will not be able to find out what their (virtual) IP addresses are by using nslookup and such. To find out these IP addresses, you need to look at the file /etc/hosts on minix. More to the point, invoking gethostbyname for a given vm will return to you only the (primary) IP address associated with the interface eth0 of that vm (which is the interface you will not be using). It will not return to you any other IP address for the node. Similarly, gethostbyaddr will return the vm node name only if you give it the (primary) IP address associated with the interface eth0 for the node. It will return nothing if you give it any other IP address for the node, even though the address is perfectly valid. Because of this, and because it will ease your task to be able to use gethostbyname and gethostbyaddr in a straightforward way, we shall adopt the (primary) IP addresses associated with interfaces eth0 as the ‘canonical’ IP addresses for the nodes (more on this below).

For help and documentation on Linux specifically, and apart from the man pages generally on minix, you might also find the  Google Linux Special Searches  engine useful.

Time client and server

A time server runs on each of the ten vm machines. The client code should also be available on each vm so that it can be evoked at any of them.

Normally, time clients/servers exchange request/reply messages using the TCP/UDP socket API that, effectively, enables them to receive service (indirectly, via the transport layer) from the local IP mechanism running at their nodes. You are to implement an API using Unix domain sockets to access the local ODR service directly (somewhat similar, in effect, to the way that raw sockets permit an application to access IP directly). Use Unix domain SOCK_DGRAM, rather than SOCK_STREAM, sockets (see Figures 15.5 & 15.6, pp. 418 - 419).

API

You need to implement a msg_send function that will be called by clients/servers to send requests/replies. The parameters of the function consist of : msg_send will format these parameters into a single char sequence which is written to the Unix domain socket that a client/server process creates. The sequence will be read by the local ODR from a Unix domain socket that the ODR process creates for itself.

Recall that the ‘canonical’ IP address for a vm node is the (primary) IP address associated with the eth0 interface for the node. It is what will be returned to you by a call to gethostbyname.

Similarly, we need a msg_recv function which will do a (blocking) read on the application domain socket and return with : This information is written as a single char sequence by the ODR process to the domain socket that it creates for itself. It is read by msg_recv from the domain socket the client/server process creates, decomposed into the three components above, and returned to the caller of msg_recv.

Also see the section below entitled ODR and the API.

Client

When a client is evoked at a node, it creates a domain datagram socket.
The client should bind its socket to a ‘temporary’ (i.e., not ‘well-known’) sun_path name obtained from a call to tmpnam() (cf. line 10, Figure 15.6, p. 419) so that multiple clients may run at the same node.

Note that tmpnam() is actually highly deprecated. You should use the mkstemp() function instead - look up the online man pages on minix (‘man mkstemp’) for details.

As you run client code again and again during the development stage, the temporary files created by the calls to tmpnam / mkstemp start to proliferate since these files are not automatically removed when the client code terminates. You need to explicitly remove the file created by the client evocation by issuing a call to unlink() or to remove() in your client code just before the client code exits. See the online man pages on minix (‘man unlink’, ‘man remove’) for details.
The client then enters an infinite loop repeating the steps below.
  1. The client prompts the user to choose one of vm1 , . . . . . , vm10 as a server node.

  2. Client msg_sends a 1 or 2 byte message to server and prints out on stdout the message
         client at node  vm i1  sending request to server at  vm i2
    (In general, throughout this assignment, “trace” messages such as the one above should give the vm names and not IP addresses of the nodes.)

  3. Client then blocks in msg_recv awaiting response. This attempt to read from the domain socket should be backed up by a timeout in case no response ever comes. I leave it up to you whether you ‘wrap’ the call to msg_recv in a timeout, or you implement the timeout inside msg_recv itself.
    When the client receives a response it prints out on stdout the message
         client at node  vm i1 : received from   vm i2  <timestamp>
    If, on the other hand, the client times out, it should print out the message
         client at node  vm i1 : timeout on response from   vm i2
    The client then retransmits the message out, setting the flag parameter in msg_send to force a route rediscovery, and prints out an appropriate message on stdout. This is done only once, when a timeout for a given message to the server occurs for the first time.

  4. Client repeats steps 1. - 3.

Server

The server creates a domain datagram socket. The server socket is assumed to have a (node-local) ‘well-known’ sun_path name which it binds to. This ‘well-known’ sun_path name is designated by a (network-wide) ‘well-known’ ‘port’ value. The time client uses this ‘port’ value to communicate with the server.

The server enters an infinite sequence of calls to msg_recv followed by msg_send, awaiting client requests and responding to them. When it responds to a client request, it prints out on stdout the message
               server at node  vm i1  responding to request from  vm i2

ODR

The ODR process runs on each of the ten vm machines. It is evoked with a single command line argument which gives a “staleness” time parameter, in seconds.

Because it is dealing with fixed topologies, ODR is, by and large, considerably simpler than AODV. In particular, discovered routes are relatively stable and there is no need for all the paraphernalia that goes with the possibility of routes changing (such as maintenance of active nodes in the routing tables and timeout mechanisms; timeouts on reverse links; lifetime field in the RREP messages; dest_sequence_#  field in the RREQ messages; source_sequence_#  fields in RREQ and RREP messages; etc.)

However, we want ODR to discover shortest-hop paths, and we want it to do so in a reasonably efficient manner. This necessitates having one or two aspects of its operations work in a different, possibly slightly more complicated, way than AODV does. ODR has several basic responsibilities :


RREQ, RREP, and time client/server request/response messages will all have to be carried as encapsulated ODR protocol messages that form the data payload of Ethernet frames. So we need to design the structure of ODR protocol messages. The format should contain a type field (0 for RREQ, 1 for RREP, 2 for application payload ). The remaining fields in an ODR message will depend on what type it is. The fields needed for (our simplified versions of AODV’s) RREQ and RREP should be fairly clear to you, but keep in mind that you need to introduce two extra fields:

A type 2, application payload, message needs to contain the following type of information :

The fields above essentially constitute a ‘header’ for the ODR message. Note that fields which you choose to have carry numeric values (rather than ascii characters, for example) must be in network byte order. ODR-defined numeric-valued fields in type 0, RREQ, and type 1, RREP, messages must, of course, also be in network byte order.

Also note that only the ‘canonical’ IP addresses are used for the source and destination nodes in the ODR header. The same has to be true in the headers for type 0, RREQ, and type 1, RREP, messages. The general rule is that ODR messages only carry ‘canonical’ IP node addresses.

The last field in the type 2 ODR message is essentially the data payload of the message.

An ODR protocol message is encapsulated as the data payload of an Ethernet frame whose header it fills in as follows :

Last but not least, whenever ODR writes an Ethernet frame out through its socket, it prints out on stdout the message
     ODR at node  vm i1 : sending      frame  hdr    src  vm i1      dest  addr
                                                      ODR msg      type n     src  vm i2      dest  vm i3

where addr is in presentation format (i.e., hexadecimal xx:xx:xx:xx:xx:xx) and gives the destination Ethernet address in the outgoing frame header. Other nodes in the message should be identified by their vm name. A message should be printed out for each packet sent out on a distinct interface.

ODR and the API

When the ODR process first starts, it must construct a table in which it enters all well-known ‘port’ numbers and their corresponding sun_path names. These will constitute permanent entries in the table.

Thereafter, whenever it reads a message off its domain socket, it must obtain the sun_path name for the peer process socket and check whether that name is entered in the table. If not, it must select an ‘ephemeral’ ‘port’ value by which to designate the peer sun_path name and enter the pair  < port value , sun_path name >  into the table. Such entries cannot be permanent otherwise the table will grow unboundedly in time, with entries surviving for ever, beyond the peer processes’ demise. We must associate a time_to_live field with a non-permanent table entry, and purge the entry if nothing is heard from the peer for that amount of time. Every time a peer process for which a non-permanent table entry exists communicates with ODR, its time_to_live value should be reinitialized.

Note that when ODR writes to a peer, it is possible for the write to fail because the peer does not exist :  it could be a ‘well-known’ service that is not running, or we could be in the interval between a process with a non-permanent table entry terminating and the expiration of its time_to_live value.

Notes

Hand-in

Unlike previous assignments, you will submit your code on the minix node, using a slightly modified procedure: please click on  electronic hand-in  and read.

Each group hands in just one copy of the Assignment, under either partner’s login name. If you mis-coordinate with your partner and each hands in a copy under his/her name, make sure one of you resubmits just a Readme file, and nothing else, saying something to the effect of “Please ignore this submission”. Please do not make us grade the same thing twice over because you are unable to get this simple coordination with your partner straight.

Your submission must absolutely include:

a Readme file containing an identification of the members in the group;
a Makefile which


Valid HTML 4.01! Valid CSS!