Project 2: IP đŚ
Milestone due: Friday, October 4, 11:59pm EDT
Final submission due: Thursday, October 17, 11:59pm EDT
Looking for a stencil link? We will send you a stencil link via email when teams are assigned on Friday, September 27.
Introduction
In this assignment. you will be constructing a Virtual IP Network that implements a link layer, IP forwarding, and routing. Normally, these functions are provided by a host or routerâs OS, drivers, and physical hardwareâthese make up that deviceâs ânetwork stackâ, ie. the set of software and hardware that implement the protocols and layer of abstraction that make these devices work. To demonstrate how these critical protocols work, and to give you practice thinking at different layers of abstraction, we will construct our own virtual networking stack, implemented entirely in our own programs and by sending packets over UDP sockets.
Beyond just teaching you about IP and routing, this project is designed to teach you how to build and work at different layers of abstractionâboth in terms of networking and in software design. On the networking side, you practice with handling data encapsulated in IP packets. On the software side, you will build a library that will realize your âIP stackâ and an API to use it across two different programs. In your next project, you will add to your networking stack by implementing TCP, getting closer to the set of functionality provided by an OS!
Warning: Do not leave this project until the last minute. This project can be tricky and a lot of it is about design. The best way to stay on top of it is to start working on your design early so that you have opportunities to ask questions.
At the start of the project, you will have a milestone design meeting with a TA to encourage you to plan your designâyou should use this time wisely, so you can bring questions and get feedback during this meeting! Start talking with your partner as soon as you receive your team assignment, and get ready to get connected!
Collaboration
This is a 2-person assignment. To form a group, you should fill out the group assignment form, located here.
If you prefer to work with a specific person, you can indicate it on the form â both students on a team should fill out the form, and only mutual requests will be honored. Otherwise, we will match each student to a partner based on their language preferences, in-person/remote status, time zones, and so on.
Once the groups are set, youâll be assigned a mentor TA to follow you in your design and grading meetings for this project and TCP. TCP will build on this project, so your effort on your IP design will pay off twice.
Capstone students: If you are using this class to fulfill your capstone requirement, you will need to complete one of two extra components. If both students are taking the class as a capstone, both capstone components must be completed.
Development Environment
You can continue to use the course container for development, which you can also use to run Wireshark. Wireshark will be incredibly helpful in both IP and TCP, as it allows you to observe network traffic in real-time to debug your work.
If you had issues using the course container environment during Snowcast, either for developing code or running Wireshark, please let us know on Edstem. We are happy to help debug any issues you may encounter â we want you to have a good workflow for development and testing!
Overview
Core Requirements
There are two main parts to this assignment:
- IP forwarding: this includes handling for receiving packets, delivering them locally if appropriate, or looking up a next hop destination and forwarding them out to an appropriate interface. For this part, you will need to build and maintain a forwarding table that you will use to make decisions about each packet.
- IP stack API: you will create an API to send and receive packets using your forwarding implementation that will provide the basis for implementing âhigher-layerâ protocols in this project, and your next project (TCP).
- IP Routing with RIP: Routing is the process of exchanging information to populate the forwarding table. We will do this using RIP, a relatively simple routing protocol for small networks. We will discuss RIP in detail in the next few lectures, so itâs not expected youâll know what this means when the project starts. For more info, see Routing with RIP.
You will implement two programs, vhost
and vrouter
, that will import your IP stack library and act as a host and router, respectively. For the most part, your host and router programs be very similar* and will behave mostly the same way, except that routers will implement RIP. This is intentionalâwe want you to see how much of the network layer is common across all IP nodes! In the next project, weâll then build a lot more functionality in to our hosts by adding TCP.
The following sections contain an overview of how each part works and links to more specifications elsewhere on this site.
Note on navigation: Weâre still trying to figure out the best way to organize all of the information here in a way thatâs easy to navigate, especially as we add more resources. If you ever canât find something, use the search box at the top of this page! This will search all pages on the site.
Capstone Requirements
Capstone students must implement at least one extra feature in their project. For details, see Capstone Requirements
Background: our virtual network
You will build our IP stack in a virtual network: instead of dealing with real hardware or drivers or the kernel, weâll build our IP stack entirely in userspace programs that will make up our hosts and routers, and send packets between using UDP sockets. This creates a similar abstraction without the hassle.
The next few sections describe exactly what we mean by a âvirtualâ network. To start, hereâs an example network topology we might want to create:
flowchart LR
H1[H1] --- R1(R1)
R1 --- R2(R2)
R2 --- H2[H2]
R2 --- H3[H3]
This network has 3 hosts and two routers. To run this network and others, we define the topology using configuration files.
Configuration files
We set up our network from a set of configuration files that define how the nodes connect to each other, what interfaces they have, and their IP addresses. There are two types of configuration files: network definition files, and lnx files.
Network definition files (network-name.json
)
These files define the network topology, including how many nodes are in each network (number of hosts, number of routers, and how they are connected together.
Note that you donât need to use these files directly, but you will run scripts that doâwe provide a script, vnet_generate
that reads these files and creates a unique config file for each node in the network called an lnx file. Your nodes will read these lnx files to determine their network settings. For examples of some networks weâll use, see Sample networks.
Lnx files (<node name>.lnx
)
This file defines the network settings for a host or routerâyou will read this file at startup to initialize your IP stack by creating interfaces, assigning IP addresses, and so on. You will use this to populate your nodeâs forwarding table, and (for routers) determine your initial configuration for RIP. For a full reference on what can be configured in an Lnx file, see the specification.
Important: You do not need to write a parser for the lnx filesâwe have made one for you. To import our parsers, look here.
Interfaces and IPs
Each node has one or more interfaces, which are defined by the lnx file passed in at startup. Our interfaces will be simulated using UDP sockets: each interface has its own UDP port where it can send/receive packets: sending a packet from this UDP port is equivalent to sending the packet from that interface.
For each interface, you can think of that nodeâs UDP port similar to a MAC address: a unique value (within our network) to identify that interface.
Notation: Similar to snowcast, all of our virtual interfaces will listen on the real hostâs localhost interface (ie.
127.0.0.1
). We typically write an interfaceâs address as127.0.0.1:5555
where the UDP port is 5555.
Each nodeâs interfaces are defined by interface
directive in its lnx file. For example here are r2
âs interface definitions from the example above (weâll unpack the rest in the following sections):
interface if0 10.1.0.2/24 127.0.0.1:5003 # to network r1-r2
neighbor 10.1.0.1 at 127.0.0.1:5002 via if0 # r1
interface if1 10.2.0.1/24 127.0.0.1:5004 # to network r2-hosts
neighbor 10.2.0.2 at 127.0.0.1:5005 via if1 # h2
neighbor 10.2.0.3 at 127.0.0.1:5006 via if1 # h3
This line says that r2
has two interfaces, if0
and if1
, corresponding to the two links in the figure, which listen on UDP ports 5003 and 5004, respectively. For more info, see here.
Virtual IPs
Just like any real IP network interface, a virtual interface has a virtual IP address, netmask, and other settings for how to communicate with hosts on the local network. These IP addresses and networks do not route to the Internetâthey exist solely within our virtual network.
Following the example above, r2 is a member of two virtual IP networks:
10.1.0.0/24
with virtual IP10.1.0.2
(shared with r1)10.2.0.0/24
with virtual IP10.2.0.1
(shared with hosts h2 and h3)
Neighbors and local networks
In our virtual network, an interface is connected to one or more other nodes on a single IP subnet (eg. 10.1.0.0/24
). All nodes on the same subnet can always communicate with each other, and always know each otherâs IP addresses and âlink-layerâ UDP port informationâthis is provided in the nodeâs lnx file using the neighbor
directive.
For example, for each of r2âs subnets, there is a neighbor
directive to tell r2 about each other host on the network, as follows:
interface if0 10.1.0.2/24 127.0.0.1:5003 # to network r1-r2
neighbor 10.1.0.1 at 127.0.0.1:5002 via if0 # r1
interface if1 10.2.0.1/24 127.0.0.1:5004 # to network r2-hosts
neighbor 10.2.0.2 at 127.0.0.1:5005 via if1 # h2
neighbor 10.2.0.3 at 127.0.0.1:5006 via if1 # h3
This means that r2 (and any node: host or router) will always know how to reach its neighbors connected to the same subnet. For example, r2 always knows that there are two neighbors reachable from interface if1 (which has prefix 10.2.0.1/24
):
10.2.0.3
at127.0.0.1:5005
10.2.0.2
at127.0.0.1:5006
This is a simplification from how it works IRL: weâll learn in a couple of lectures how hosts need to discover each otherâs MAC addresses to send packets on the link layer using the ARP protocolâwe avoid needing a protocol like ARP by specifying all of this info ahead of time in the lnx file.
You can use this information when you define your interface: you may assume that you always know the UDP addresses and ports to send to your neighbors. From there, the challenge in this project is learning how to communicate between subnets.
What you will build
Your implementation will consist of two programs called vrouter
and vhost
to implement a router and host on our virtual network. However, most of your implementation will be your IP stack, which will be shared between the two programs: you will do most of your work in library (or module, package, etcâwhatever the term for your language) that you will import into both programs.
The IP stack
The core of your IP stack will be a virtual âlink layerâ and ânetwork layerâ, which together make up a framework to send and receive IP packets over the virtual network. To do this, you will create a representation for a nodeâs virtual âinterfacesâ (actually UDP sockets) and an API for how the rest of your code will send and receive IP packets across the links.
The following figure shows an overall architecture for the major components (detailed below):
When your nodes start up, you will listen for packets on each interface and send them to your virtual network layer for processingâyou will parse the packet, determine if itâs valid, and then decide what to do with it based on the forwarding table. Routers have multiple interfaces and can forward packets to another interface. In our network, hosts have only one interface and only send or receive packets.
From there, you can use your IP stack to send and receive packets over our virtual network. To do this in an extensible way, you will design and build an API for key functions for how higher layers will use your IP stack.
At this stage, we have two âhigher layersâ:
- Test packets (both hosts and routers): A simple interface for sending packets from the command line on each host/router
- RIP (routers only): Routers will communicate with each other to build a global view of all networks in the system and adapt to network changes.
We will build on this in our next project to implement TCP!
Command line interface: Both hosts and routers will have a small command line interface (defined here) send packets, gather information about your network stack, and enable/disable interfaces. We will use these commands to test your network.
The following sections describe some fundamental background on how our virtual network is structured and the major components you will implement.
IP-in-UDP encapsulation
You will use UDP as your link layer for this project. Each node will create an interface for every line in its links file - those interfaces will be implemented by a UDP socket. All of the virtual link layer frames it sends should be directly encapsulated as payloads of UDP packets that will be sent over these sockets. You must observe an Maximum Transfer Unit (MTU) of 1400 bytes; this means you must never send a UDP packet (link layer frame) larger than 1400 bytes.
Wait, what? Why are we putting IP packets inside UDP packets?
Normally, IP packets are wrapped in an Ethernet (link layer) frame before being sent across a physical medium. However, this requires a network driver and its functionality is implemented on the kernel level, which is not the purpose of this assignment.
Instead, we will use IP packets wrapped in UDP packets, which provides a virtual link layer abstraction that emulates link layer functionality without delving into specifics of Ethernet and the physical medium. On reliable networks (i.e.
localhost
), the connectionless/unreliability aspect of UDP is not a concern; thus, as an implementer, (de-)serializing packets to/from a UDP packet is the same interface as (de-)serializing to/from an Ethernet frame.To reiterate: for this project, instead of encapsulating IP packets within Ethernet frames (which is what would actually happen in the real world, when using Ethernet as a link layer), you will encapsulate your IP packets within UDP packets. Sending these IP-in-UDP packets back and forth between the nodes in your test networks will simulate a link layer for this project and the next one (TCP).
IP Forwarding
In addition, you will design a network layer that sends and receives IP packets using your link layer. Overall, your network layer will read packets from your link layer, then decide what to do with the packet: deliver it locally delivery or forward it to the next hop destination.
Definition: Next hop destination â An adjacent node in the network through which you should send a packet so that the packet travels through the minimum number of âhopsâ required to reach its destination IP address.
In general, your IP forwarding process should follow the IPv4 protocol, as described in RFC791, but you are not required to implement all features of this standard: this section provides details what parts we can ignore. In general, when you receive a packet, your IP forwarding implementation must validate the packetâs checksum, inspect the destination address to determine how it should be processed and (if necessary) forward it to the appropriate next hop.
Longest-prefix matching
Your IP forwarding implementation must be able to perform longest prefix matching in order to handle cases when there are multiple matches for a destination address. However, you are NOT required to implement longest-prefix matching efficientlyâa linear search over your forwarding table to collect matching entries is fine.
How would efficient longest-prefix matching work?
High-performance routers implement forwarding tables using fast, purpose-built hardware called TCAM (Ternary Content-Addressable Memory). TCAMs can check all forwarding table entries simultaneously, in constant timeâthis is extremely important for high-performance routers, which can forward packets at many terabits per second.
An efficient way to do forwarding table lookups in software is to build a prefix trie, which organizes prefixes in a tree-like structure, allowing faster searches. You are NOT required to implement a prefix trie in your project, though you certainly can do so if you want. For more info, see this section of the Dordal textbook.
For some extra reading on how the Linux kernel does forwarding table lookups, see here and here. (This is much more than we need for the project, though!)
IP header format
Your IP packets must use the standard IPv4 header format, described in Section 3.1 of RFC791. When forwarding, you must decrement the packetâs TTL value and recompute its checksum. You can use a starting TTL value of at least 16, which is more than sufficient for all the networks we will test in this assignment.
You do not need to manually define a struct or write serialization/deserialization code for the header yourself. Any language you choose should have a library that can do this for you, and provide types and utilities for working with IP addresses:
- In Go, the supplemental
netip
package contains several types for working with IP addresses and prefixes. We have also posted a go module to parse the IP headerâour version uses some new Go features not present in Goâs standard IP header struct that will be useful for this project. For an example of how to use it, see the IP-in-UDP example - In C/C++, a struct for the IP packet header is available in
/usr/include/netinet/ip.h
asstruct ip
. For an example of how to use it, see this code example. - In Rust, the
etherparse
crate provides an IP packet header struct.
Essential code example: be sure to take a look at the IP-in-UDP example for a demo of how to encapsulate IP packets inside UDP packets, parse the IP header, and compute the checksum! See the exampleâs README, and Gearup II, for more details.
Unsupported features and robustness
Your IP stack is NOT required to support advanced IP features such as fragmentation or IP options. In addition, all other IP advanced header fields (IP Identification, Type of Service, ECN, flags) may be set to zero when you generate packets, as we do not use any of these features. However, your design should be able to receive these packets and act on them in a sensible way, meaning that you shouldnât crash if you receive a packet with a feature you donât know how to process.
Similarly, you are not required to send fragmented packets, or implement reassembly of fragmented packets that you receive. Instead, just forward/deliver these packets normally. You are not required to handle IP options and MAY
simply drop any packets that use them (which is what most Internet routers will do).
Interoperation
Overall, your vhost and vrouter programs MUST
be able to interoperate with the reference versions when forwarding packets. Make sure you test your node in networks that contain the reference node. As you work to implement forwarding, remember that you have Wireshark to help you! You can use Wireshark to view the contents of your IP packets (and those sent by the reference) to make sure they are formatted properly.
The Warmup (online after sencils are released) has more information on how to use Wireshark to inspect your IP packets. In addition, we provide a custom RIP dissector for our custom protocol. As always, feel free to post Wireshark related questions on Ed.
IP stack API
The majority of your implementation code will be shared between your host and router programs. Indeed, a goal of this project is to demonstrate how IP is a common protocol used by all network devices! As you build your implementation, you should be thinking about how to construct a reusable âlibraryâ that can be included in both your vhost
and vrouter
program and an API (ie, set of public functions) that these programs can use to interact with your IP implementation.
What does âlibraryâ mean? We just mean a modular piece of code that you include in both programsâthe actual term will vary depending on your language:
- For C/C++, this would just be some functions you include with a header file (like
protocol.h
in the socket examples)- For Go, a separate module in your repo (like
protocol.go
in the socket examples)You do NOT need to build a redistributable library (eg. something publishable for other people to use)âit just needs to be something that you can import into both
vhost
andvrouter
.
We are asking you to do a bit of software engineering hereâat minimum, the part of your code that handles IP packets MUST
be shared between your vhost
and vrouter
programs in some kind of library. In other words, you may not copypaste/rewrite this code between programs.
Example API
To show you what we mean by an API, here are some example functions you might considerâthe idea here is to expose a set of functions to that the host and router can use to implement other functionality.
This is only an exampleâyou can design your API functions any way you like, but at minimum we recommend that your IP stack provide at least the following functionality:
Initialization
Initialize(configInfo IpConfig) (error) // Example 1 (pick ONE, or something like it)
Initialize(configinfo IpConfig) (*IPStack, error) // Example 2
Set up your IP stack based on information passed in from the config file. (Remember, we give you parsers for the config fileâfor Go, this creates the IpConfig
struct in the example above) This would perform tasks like creating interfaces and opening sockets, starting up threads, etc.
Why are there two examples?
In general, there are two ways to think about representing your IP stack to the rest of the code using it:
- Example 1: Use global variables to represent your IP stackâs state (interfaces, forwarding table, etc.) and then have some public functions that use them
- Example 2: Return a struct or object (depending on your language) that holds all your IP stackâs state. If you do this, the other functions listed here would become methods that use this struct.
Both of these options are equal for the projectâyou can pick whichever is more comfortable for you based on your language and preferences.
Send packets
SendIP(dst netip.Addr, protocolNum uint8, data []byte) (error)
This is the interface to send packets! For this projectâand for the network layer in generalâthe goal is to expose functionality that allows the user (in this case, the program calling the function) to send a packet to any IP in our network. From the perspective of the network layer, the packet could be any arbitrary array of bytes. The protocolNum
sets the protocol
field in the IP header, which is used by the receiver to denote the packetâs type (more on this in the next section).
Add protocol handler
type HandlerFunc func(...)... // You decide what this function looks like
RegisterRecvHandler(protocolNum uint8, callbackFunc HandlerFunc)
To receive packets, you will need to provide a way for the user (ie, you, the person implementing vhost
and vrouter
) to receive different types of packets and connect them to âhigher layersâ of your networking stack that will process them. The IP header can differentiates between âtypesâ with the IP Protocol field.
Since we will handle a few different types of packets (and add more in the next project), you must make this interface extensible: for a given protocol number, your API should register a handler function (a callback) that gets called when you receive a valid packet of that type.
In this project, we will need to support two different IP protocols:
-
Test Protocol (hosts and routers, protocol value == 0): to test our implementation, your devices will implement a command to send an arbitrary string in a packet using the
send
command. Your test protocol handler just needs to print out the packetâs content; see the Driver section for more details. The test protocolâs IP protocol value is0
. -
Routing Information Protocol (RIP) (routers only, protocol value == 200): routers will communicate with each other using the RIP protocol. When you receive a RIP packet, you will need to pass it off to an appropriate handler that will process the packet according to our RIP specification and update the forwarding table as necessary.
In the next project, you will use this same mechanism to add another handler for TCP. This will be the starting point for receiving TCP packets, mapping them to sockets, etc.
Here are some examples of what handler functions might look like in each language (feel free to make your own, though!):
Go
One potential type definition could be
type HandlerFunc = func(*Packet, []interface{}) RegisterRecvHandler(protocolNum uint8, callbackFunc HandlerFunc)
where
[]interface{}
contains data that a handler could need.C/C++
For some
some_data_t
,typedef void (*handler_t)(some_data_t *, struct ip *); void net_register_recv_handler(uint8_t protocol_num, handler_t handler);
Rust
This one is a little trickier; one potential type might be
type Handler = Arc<Mutex<dyn FnMut(IPPacket) -> Result<()> + Send>>;
Alternatively, you could create a trait with a corresponding register function:
pub trait Handler: Send { fn handle_packet(&self, packet: IpPacket); } pub fn register<H: Handler + 'static>(table: &mut HandlerTable, protocol: u8, handler: H) {...}
For example, for RIP packets, the RIP packet should be the payload for the IP packet. As a protocol, an RIP handler should be able to be registered with the following in order to receive incoming packets with an IP protocol field of 200:
RegisterHandler(200, RIPHandler)
RegisterHandler(0, TestPacketHandler)
Routing with RIP
One part of this assignment is to implement routing using a modified version of RIP protocol, which is defined by RFC2453. We will discuss the RIP protocol in detail during class within the first week of the assignmentâs release. You can also read more about the RIP protocol in Sections 13.1â13.2 of the Dordal textbook or in Section 4.2.2 of the Peterson textbook.
Our implementation of RIP is slightly different than the RFC version. For essential details on our version, see the RIP specification.
Command-line interface (REPL)
Your vhost
and vrouter
programs must provide a command-line interface (ie, a REPL) that can be used to send packets and print out network information.
Similar to Snowcast, your command-line interface MUST
follow a specific format for how commands are structured and what output you print. For details, see the REPL commands specification.
Program arguments
Both your vhost
and vrouter
programs MUST
take in one command-line option --config
, that specifies the lnx file that specifies the lnx file used to configure it, like this:
vhost --config <lnx file>
vrouter --config <lnx file>
You can add other options if you want (for debugging, logging, etc), so long as they are optional.
Extra capstone requirements
Capstone students will need to implement at least one of the following extra features in their implementation. Each student completing a capstone must implement one featureâso if both students on a team are doing capstone, you must implement both requirements.
These features are open-ended graded manually during interactive grading, mainly by demonstration and discussion. You have broad freedom on how you implement each feature and can choose your own behavior for any design decisions not specified here, as long as they do not break any other assignment requirements.
Traceroute (Capstone Only)
Your driver should include the following command for demonstrating traceroute:
traceroute <vip>
Which should print out the sequence of hops in the following format: <hop num> <vip>
. So an example output would be:
Traceroute from 192.168.0.2 to 192.168.0.5
1 192.168.0.2
2 192.168.0.8
3 192.168.0.5
Traceroute finished in 3 hops
From this command, we should be able to see changes in the path when any node in the network is brought up or down. If a host is not in the network, or is unreachable, you should print that information.
In your README (or a separate file in your repo), you should submit a writeup about how your traceroute works, your major design decisions implementing it, and any problems you observed.
Neighbor discovery (Capstone Only)
In this project, we assume that all hosts on a local network always know about all of their neighbors. For example, in the configuration below, r2
knows about two neighboring hosts on its r2-hosts
network:
interface if1 10.2.0.1/24 127.0.0.1:5004 # to network r2-hosts
neighbor 10.2.0.2 at 127.0.0.1:5005 via if1 # h2
neighbor 10.2.0.3 at 127.0.0.1:5006 via if1 # h3
What if we didnât want to know about all hosts ahead of time? Design and implement a small protocol for a host to connect to a router and âregisterâ itself as a neighbor on a particular network.
What is the minimum amount of information that the host and router would need to know about each other ahead of time?
You can design this protocol any way you want. You donât need to have a fully-complete implementation for the grading meeting, but you should have a complete specification of what kinds of messages are involved and how they would work. As you built your implementation, what kind of problems did you notice? In your README (or a separate document) you should submit a writeup with your protocol design, an overview of how you implemented it, and any problems you encountered.
How to get started
Stencil repo
Once teams are defined, you will be assigned a Github classroom link to create a repository for your team. You will receive an email with your team assignmentâan announcement will be posted on Ed after these are sent so you can make sure you received it. Until then, there is no stencil to clone. We appreciate your patience!
Once your teams are made, itâs time to start building your design and (after some design work) writing code! See the Getting started guide for info on how to clone the stencil and use the reference version.
Essential scripts and examples
We provide several programs to help you run networks in different configurations:
util/vnet_generate
: Generate configuration files (.lnx
files) from a network JSON fileutil/vnet_run
: Run all hosts and routers in a network automatically in atmux
session
See the Getting started guide for a tutorial on how to use these, as well as the Tools and Resources for more tutorials and information on how to test your work.
Reference version
Similar to Snowcast, we have provided a reference version of the vhost
and vrouter
binaries in the reference
directory of your repository so you can get a feel for how the project should work and test our your implementation to make sure it interoperates with ours. We strongly encourage you to test and debug your implementation by running your networks with a combination of our routers/hosts and yours.
See the Getting started guide for info on how to run the reference version with our scripts vnet_run
and vnet_generate
.
Note: If you are using an M1 Mac, you should use the versions of these binaries in the
reference/arm64
directoryâthese binaries are compiled for your CPU architecture.
Getting Help
This project has a large scope, but we donât intend for it to be painful! We have provided a number of resources to help you, and we are always happy to answer questions. To start, make sure you have read this handout and the linked specifications and resources to help understand how the virtual network architecture works. Ed is always a good place to get help on general topicsâplease read the (pinned) FAQ/Reading list post, for a list of common questions. During office hours, we are happy to talk about concepts and how to approach your software design, or help with more focused debugging.
Note: Whenever you link us to your repository (for an Ed post, final submission, etc.), please make sure it contains a Makefile per our submission requirements so we know how to build your code.
How to submit your work
Similar to Snowcast, please include a Makefile such that make
builds your project and places your vhost
and vrouter
binaries in the top-level directory of your repository.
Collaboration
This assignment has some very tightly-coupled components, so it is important that you work closely with your partner to design and implement the core parts of the project together. It will not be possible for you to go off into separate rooms, implement your half, and âjust hook them up.â
We recommend designing the core components of your work as a team, such as the layer interfaces and the forwarding table. After you have defined your interfaces and tested some of the low-level components, you might divide up additional work on the link layer interface and RIP, but you can do whatever you feel is appropriate.
Your stencil repo should be set up so that you and your and your partner can use it to collaborate using git. For some resource on using git, we recommend the following lectures from CS0060 (an older course on systems fundamentals):
However, please note that your Git repos should be private, and you are permitted to share code with other groups. You are welcome to talk to other groups about concepts, algorithms, etc., but each groupâs code must be their own.
Mentor TAs
Each group will have a member of the course staff (TA or instructor) assigned as their mentor. During the milestone phase of the assignment, youâll meet with your mentor TA to discuss your projectâs design. At the conclusion of the project, you will meet with your mentor TA again to grade your work (see Handing In and Interactive Grading for more info).
While the project is out, your mentor TA will be best-equipped to help answer questions about, debugging, discussing design, etc., in their office hours or on Ed, but they are not your only support resource: you should feel free to ask questions of any member of the course staff. You should not be contacting your mentor TA for help directly outside of hoursâplease use our standard course resources (Ed, hours, etc) for this.
Grading
Milestone - 20%
You will schedule a milestone design meeting with your mentor TA by Friday, October 4th at the latest. An announcement will be made on Ed with instructions on how to scheduleâwe will be creating meeting slots in the week leading up to the milestone deadline. At this milestone, you should have a design for your program and be ready to ask about any areas where you have questions.
For this meeting, you should:
- (Each team member) Follow the instructions in the Getting Started Guide to clone your repo and start running the reference versionâthis will make sure you know how to run the tools. At the end of the Guide, youâll be asked to commit a screenshot to your repo to demonstrate that youâve done this part.
- (As a group) Commit something to your repo to demonstrate your design to us â this could be some data structure definitions, interface designs for the different layers, diagrams, etc. We arenât going to constrain you with requirements here, but we urge you to use the time before your meeting wisely and plan what you want to show us, and have questions. The feedback you receive will pay off for your implementation.
As you think about your design and prepare for the meeting, be ready to answer specific questions about your design, for instance:
- What are the different parts of your IP stack and what data structures do they use? How do these parts interact (API functions, channels, shared data, etc.)?
- What fields in the IP packet are read to determine how to forward a packet?
- What will you do with a packet destined for local delivery (ie, destination IP == your nodeâs IP)?
- What structures will you use to store routing/forwarding information?
- What happens when a link is disabled? (ie, how is forwarding affected)?
Note: We may not have covered routing with RIP in class by the time you have your milestone meetingâyou arenât expected to know the details of how RIP works for your meeting, but you should understand how it fits into your overall program architecture as a component that will update your forwarding table.
Functionality - 60%
Most of your grade will be based on how well your program conforms to our specification. As in the Snowcast project, you will be expected to interoperate with the reference implementation. Among other details, your program will be expected to maintain forwarding tables, handle packet forwarding, network loops, and maintain interfaces that may go up or down at any time without causing the network to crash.
Historically, this project has been graded manually. We are working on automated testing for some parts of the assignment, but these will only inform how much manual review we do and what we ask you about during grading meetingsâyour grade will be determined entirely by a human.
Interface Design / Code Quality - 15%
Because part of the specification prescribes the programming interfaces for the link layer and for the upper layer handlers to IP, we will be evaluating the design of those interfaces as well as general code quality.
README - 5%
Please include a README that describes your design decisions, including:
- How you build your abstractions for the IP layer and interfaces (What data structures do you have? How do your vhost and vrouter programs interact with your shared IP stack code?)
- How you use threads/goroutines
- The steps you will need to process IP packets
You should also list any other notable design decisions and any known bugs. For any discussion of known bugs, please include a description of how you have attempted to fix the bug/where you think the bug originates from in your codeâwe will take off fewer points for bugs that we do not find ourselves.
Handing in and Interactive Grading
How we will build your code
Similar to Snowcast, please include a Makefile such that make
builds your project and places your vhost
and vrouter
binaries in the top-level directory of your repository. Make sure your binaries follow our specification for command line arguments.
How to submit
Once you have completed the project you should commit and push your work to the main
branch of your git repository, then upload it to the IP assignment on Gradescope. Only one team member needs to submit, but you MUST add your partner to your submission.
Post-project form: Within 24 hours of your final submission (late or not), each team member MUST
fill out a form to discuss their overall contributions to the project, and how collaboration worked on their team. The instructor will review all form responses and may ask questions of individual team members to ensure all parties contributed to the project. Filling out the form is required to receive a gradeâif you do not fill it out, you will not receive a grade on the project.
How grading works: We will primarily grade your projects interactively, mainly by pulling your code from your git repository and looking at some automated test feedback on Gradescope. After the deadline, your team will meet with your mentor to demonstrate the functionality of your program.
Bugfixes: Between the time youâve handed in and the demo meeting, you MAY
continue to make minor tweaks and bug fixes (documentation, README, fixing small issues, refactoring) and push them to your git repo. However, you shouldnât be making any major changes or getting any functionality to work for the first time. For example, fixing a small bug in a mostly-working component is fine, but implementing RIP or fixing a bunch of code you pushed without prior testing is not. We add this flexibility because youâll be re-using your codebase for TCP, so we want you to have the ability think about maintaining and cleaning up your code as you prepare for this next step. We reserve the right refer to your original version during grading to ask you about how your code has changed.