VNS Internet Router Client
Each group will get their own simple topology (1 router, two end hosts) to use for the tutorial. This topology is the same as will be used to develop the initial software router. If needed, we can create additional topologies with multiple virtual routers so that you can run tests on a more complex topology over the course of the project.
Overview of the Internet Router VNS Client Code
The code provides the foundation on which you can build a router. It communicates with the VNS to read packets from the network, send packets from you to the network, and to get the interface information for your router.
Although it will not be needed for the tutorial and initial stages of the project, the code contains two additional features:
- It can interface with a NetFPGA board to allow the board to forward packets. (The definition of MODE in the Makefile enables this feature.)
- It contains a working TCP implementation that you can plug into the IP layer of your router.
The first file you will want to get familiar with when going over the project code is sr_integration.c. This file contains the definitions for the interface functions between your code and the provided code. You should extend these functions (with the possible exception of sr_integ_low_level_output(...)) to interface with your router implementation.
The integration methods work by accepting a pointer to the router's state as the first argument. The core state of the router (which includes information needed by VNS and NetFPGA) is held in struct sr_instance and defined in sr_base_internal.h. A global singleton instance of struct sr_instance is held in sr_base.c. This is the instance passed to the methods in sr_integration.c and can also be accessed by calling sr_get_global_instance(0).
You must extend a struct for your router's state (routing table, arp cache, interface list etc.) and make sure it is registered with the global instance. You can do this as follows:
- Open the file sr_router.h and locate the struct router_t. Extend that struct to hold the state of your router subsystem.
- Modify the method sr_integ_init(...) in sr_integration.c to register your subsystem with the the global instance. The code should look something as follows:
router_t* subsystem = malloc_or_die( sizeof(router_t) );sr_set_subsystem( sr_get_global_instance(0), subsystem );
- In this same function, you should also initialize your router state appropriately. (Simply using malloc will allocate some memory for your router control structures but not set them to valid initial conditions).
- Anytime you have a pointer to the core router instance you can retrieve a pointer to your subsystem as follows:
router_t* subsystem = sr_get_subsystem(sr);
This is the first function called during router initialization. It is called before connecting to VNS, reading in hardware information, etc. As described above, you can use this function to initialize your own data structures.
This function is called during initialization each time a new interface is added.
This function is called after all of the interface information has been received from VNS. You can use this function to initialize any data structures that require interface information that was not available when sr_integ_init was called. It can also be used to start threads that require interface information, such as the dynamic routing protocol.
This function is called each time the router receives a packet on any interface. The packet buffer, the packet length, and the receiving interface are passed in as parameters. The packet is complete with Ethernet headers. The Ethernet CRC has already been verified and removed.
The packet buffer and interface name memory are managed externally, so you should not free their memory. Furthermore, if you need them beyond the scope of this function, you must make a copy of them.
This function can be used to send a packet to VNS (or to NetFPGA) to be injected into the topology. You must pass a packet buffer, the packet length, and the sending interface to this function. The packet must already have correctly formatted Ethernet and IP headers. The Ethernet CRC will be calculated for you. It is unlikely you will need to modify the contents of this function.
This function is called by the transport layer for outgoing packets generated by the router. The transport layer provides the destination IP address in network byte order of the packet it would like to send. This function should return the IP address in network byte order of the router that should be used as the source address to send a packet to that destination. (Why do we need this? Imagine, for example, a web server running on the router that allows for remote access and administration. When the web server sends TCP/IP packets to a remote client, it has many source IP addresses to choose from for the packet header. Each interface on the router has its own IP address in the subnet of that port. This function allows the transport layer to query the routing layer and pick the IP address for the correct outgoing port)
This function is called by the transport layer for outgoing packets that need to be encapsulated in an IP packet and sent out over the network. This function must construct the appropriate IP header and Ethernet header and send the packet over the network. To determine the destination MAC address of the outgoing packet, this function will need to check with the routing table and ARP cache. After the packet is encapsulated, it can be transmitted by calling sr_integ_low_level_output().
This function is called during shutdown to allow you to deallocate memory.
Simplified Program Flow
Once compiled and run, the project code will connect to the VNS, attempt to reserve a topology, read the interface information for the router and start reading packets to the router from the network. The most important steps of the process are enumerated below.
- User runs the binary sr with the appropriate command line options (e.g., ./sr -t <topID> -s vns-1.stanford.edu -u <vnsUserName> -a <authKeyFile> -r <rtableFile>).
- main(...) starts the low level networking subsystem (which spawns a thread for itself) and starts a thread that you will extend to be a user level application in the future.
- The core of the router is initialized.
- sr_integ_init(...) is called to allow you to initialize your router subsystem.
- VNS is contacted and the host and topology are reserved.
- The interface information is read from the VNS. For each interface, the method sr_integ_add_interface(...) is called. You should extend this method to copy the interface information into your subsystem. It is up to you to decide the best method for handling interfaces.
- sr_integ_hw_setup(...) is called signaling that all the interface information has been read. You may use this function for any additional setup (such as creating the default routing table) and/or debugging.
- For each packet destined to your router on the network, sr_integ_input(...) is called. This is where packets enter your system.
- For each forwarded or generated packet to be placed on the network you must call sr_integ_low_level_output(...). Note that this method requires a pointer to struct sr_instance.