Assignment 3: Supporting ARP, ICMP, RIP in the Router
Released: 02/27/2025
Due: 03/13/2025
- Part 0: Setup
- Part 1: ICMP
- Part 2: ARP
- Part 3: RIP
- Submission
- Appendix: Table Insertion API for
controller.py
Part 0: Setup
- Clone the skeleton code to your private repository.
- To obtain the skeleton code, create a private repository by selecting
Use this template> Create a repository
on the GitHub repository page. - Don’t forget to select
Private
while creating the repository.
- To obtain the skeleton code, create a private repository by selecting
- You should instantiate your experiment in the same way as the previous assignments.
- Make sure to use profile
cs356-base
- Make sure to specify your group during instantiation.
- Make sure to use profile
- You should execute
$ bash setup.sh
upon every$ git clone
and$ git pull
.- The script will automate updating your change in the
src
directory to thelabs
directory by creating a hard link for each file insrc
. - Otherwise, you must copy and paste your code whenever you make changes.
- The script will automate updating your change in the
- Do NOT modify the provided skeleton code other than the commented as
PARTn_TODO
(e.g.,PART1_TODO
).- In this assignment, a lot of skeleton code is doing tedious work on your behalf, so you can focus on the core logic.
- You can just read the comments to understand the skeleton code.
- Part 3 topology has six routers, so we recommend you use multiple
tmux
windows.- Other than splitting a window into panes, you can create a new tmux window within a session by typing
Ctrl+b c
. After creating windows, you can navigate the windows usingCtrl+b p
andCtrl_b n
. (Don’t forget to pause after Ctrl+b) - Please refer to here for more details on how to use
tmux
.
- Other than splitting a window into panes, you can create a new tmux window within a session by typing
Part 1: ICMP
Overview
Your tasks are implementing part of ICMP protocol support on src/l3_routing.p4
. The tasks are commented as PART1_TODO
in the p4 file. If you have done the setup correctly, your source code also should appear in the labs/part1_icmp/shared
directory. The changes on one file will be reflected in the other. The ARP and routing table are static in this part of the assignment. The Kathara lab for this part is located in labs/part1_icmp
. The virtual network topology is illustrated below.
Tasks
All the tasks are on the src/l3_routing.p4
file.
Task 1: If the arriving IPv4 packet’s TTL field is 1, send an ICMP Time Exceeded message to the sender of the packet.
- First, call
action send_ICMP_error
with typeICMP_TYPE_TIME_EXCEEDED
(11 or 0x0b) and code0
. - Then modify the source IP address of the packet to the IP address of the ingress port by applying the
icmp_ingress_port_ip
table.
Task 2: If the arriving IPv4 packet’s destination IP is one of the router’s IP, handle the packet.
- If the packet is an ICMP echo request packet, send an ICMP echo reply by calling
action send_ICMP_echo_reply
- Complete the
action send_ICMP_echo_reply
following the instructions on the skeleton code. You may refer to theaction send_ICMP_error
. - Else if the packet is either TCP or UDP packet, send an ICMP error using
send_ICMP_error
with typeICMP_TYPE_DEST_UNREACAHBLE
(3) and codeICMP_CODE_PORT_UNREACHABLE
(3).
Test your implementation
- Compile the P4 code and launch the P4 and controller program on the routers (
r[1-3]
).- All the necessary commands are provided as script files in the Kathara lab’s
shared
directory. - After starting the Kathara lab, compile the P4 code with
$ bash compile_p4.sh
on one of the routers (e.g.,r1
) after$ cd /shared
. - Then, on each router, launch the compiled P4 program with
$ bash run_router.sh
and the controller with$ bash r[1-3]_run_controller.sh
.
- All the necessary commands are provided as script files in the Kathara lab’s
- Test the functionality.
- For Task 1, try to run
$ traceroute 20.0.0.10
onh1
. It basically sends packets with small RTTs (incrementing by 1 until the packet reaches the destination) to examine the route to the destination. If you implement it correctly, you should be able to see the route to20.0.0.10
(h2
) fromh1
. - For Task 2, try to run
$ ping 10.0.0.1
onh1
(i.e., ping tor1
). It should work properly if you implement Task 2 correctly. Also, try to run$ wget 10.0.0.1
onh1
. It will try to send a TCP packet tor1
and should return an error immediately.
- For Task 1, try to run
Part 2: ARP
Overview
Your tasks are implementing part of ARP protocol support on src/l3_routing.p4
and src/controller.py
. The tasks are commented as PART2_TODO
in the p4
and python
files. If you have done the setup correctly, your source code also should appear in the labs/part2_arp/shared
directory. The changes on one file will be reflected in the other. The routing table is static in this part of the assignment. The Kathara lab for this part is located in labs/part2_icmp
. The virtual network topology is illustrated below.
Tasks
Task 1 is on the src/l3_routing.p4
file and Task 2 is on the src/controller.py
file.
Task 1: Complete the action send_ARP_response
on the p4 file.
- Refer to the skeleton code for detailed instructions.
- This action will be invoked upon ARP request on one of the router’s network interfaces.
- It changes an incoming ARP request to an ARP reply and sends the reply to who sends the request.
Task 2: Complete the ARP reply handling on the python file.
- Refer to the skeleton code for detailed instructions.
- Once the router receives the ARP reply, it forwards the reply to the controller.
- Then the controller should install the ARP table and forwarding table using the information in the ARP reply packet.
- Your job here is to insert the
arp_table
anddmac_forward
table entries into the router data plane. - It should be quite similar to what you’ve done in A2.
Test your implementation
- Compile the P4 code and launch the P4 and controller program on the routers (
r[1-3]
).- All the necessary commands are provided as script files in the Kathara lab’s
shared
directory. - After starting the Kathara lab, compile the P4 code with
$ bash compile_p4.sh
on one of the routers (e.g.,r1
) after$ cd /shared
. - Then, on each router, launch the compiled P4 program with
$ bash run_router.sh
and the controller with$ bash r[1-3]_run_controller.sh
.
- All the necessary commands are provided as script files in the Kathara lab’s
- After implementing both tasks, try running
$ ping 20.0.0.10
onh1
. It should work, and the ARP request and reply packets should appear along the path. You may check these with$tcpdump—i any arp
onr1
andr2
.
Part 3: RIP
Overview
Your tasks are implementing part of RIP protocol support on src/controller.py
. The tasks are commented as PART3_TODO
in the python
file. If you have done the setup correctly, your source code also should appear in the labs/part3_rip/shared
directory. The changes on one file will be reflected in the other. The routing table is static in this part of the assignment. The Kathara lab for this part is located in labs/part3_rip
. The virtual network topology is illustrated below.
Tasks
All the tasks are on the src/controller.py
file.
Task 1: Complete the mergeRoute
method of the Route
class.
- The
Route
class stores routing information (i.e., next hop IP and cost). - The information would be associated with a certain destination IP address and stored in the
routing_table
dictionary. - Refer to the skeleton code for detailed specifications.
Task 2: Complete the RIP response handling logic.
- Your task is handling the routing table entry updates and installations.
- Once the router receives an RIP response, it forwards the packet to the controller.
- Then, the controller should update or install the routing table entries if the routes in the RIP response are for unknown destinations or known but with cheaper costs.
- When you build the table entry for
ipv4_route
(a LPM table), use a prefix length of 32 for thematch_fields
parameter ofp4info_helper.buildTableEntry
. In part 3, we don’t use the notion of a subnet for simplicity. - When you update an existing table entry, specify
is_modify=True
as the parameter ofs1.WriteTableEntry
. - Refer to the skeleton code for more detailed specifications.
Test your implementation
- Compile the P4 code and launch the P4 and controller program on the routers (
r[1-6]
). You may want to test your program on the subset of the routers to ease debugging.- All the necessary commands are provided as script files in the Kathara lab’s
shared
directory. - After starting the Kathara lab, compile the P4 code with
$ bash compile_p4.sh
on one of the routers (e.g.,r1
) after$ cd /shared
. - Then, on each router, launch the compiled P4 program with
$ bash run_router.sh
and the controller with$ bash r[1-6]_run_controller.sh
.
- All the necessary commands are provided as script files in the Kathara lab’s
- After implementing both tasks, try to ping to
h[2-6]
onh1
. It should work properly. You may verify the routing table information on each controller’s stdout. It will dump the routing table every 10 seconds.
Submission
Please submit your code (modified assignment3 repository) to the Canvas Assignments page in either tar.gz
or zip
format.
The naming format for the file is assign3_groupX.[tar.gz/zip]
.
Appendix: Table Insertion API for controller.py
- Create a table entry you want to insert.
In the skeleton code, the instance of the
helper
class,p4info_helper
, is already created and initialized. Its role is to interpret the human-readable format of P4 objects in the way that the P4 program understands. To build a table entry in a way that the P4 program understands, you can use thehelper
class’sbuildTableEntry()
method. Its API is described below.def buildTableEntry(self, table_name, # human-readable table name in string match_fields=None, # a dictionary with a human-readable match field as a key and its value as a value default_action=False, # human-readable default action name in string (This is not necessary if you already define the default_action in the P4 code) action_name=None, # human-readable action name in a string (This action is executed upon hit) action_params=None, # a dictionary with a human-readable action parameter name as a key and its value as a value priority=None # unused in our case ):
For the value for the
match_fields
andaction_params
dictionaries, here’s a guideline to the format of the value:- IP address: String in the dotted decimal notation (e.g.,
10.0.0.1
) - IP prefix for longest prefix matching: a tuple with IP prefix in string and prefix length in integer. (e.g.,
(10.0.0.0, 24)
which means the table key is in the range10.0.0.[1-255]
) - MAC address: String in the
xx:xx:xx:xx:xx:xx
format (e.g.,00:00:0a:00:00:01
) - Port number: Integer
To set a timeout, you should change the attribute,
idle_timeout_ns
, of the table entry as below in integer and the unit of nanoseconds.table_entry.idle_timeout_ns = int(1 * 1e9) # timeout is 1 second
For more details, refer to the file,
assignment2/labs/star_four_hosts/shared/utils/p4runtime_lib/helper.py
. - IP address: String in the dotted decimal notation (e.g.,
- Send a table entry to the switch.
The
Bmv2SwitchConnection
object,s1
, is provided and initialized. The object is the abstraction of a connection between the switch and the controller. To add a table entry you built in the above step, you can simply call theWriteTableEntry
method of theBmv2SwitchConnectoin
object withtable_entry
as a parameter. (e.g.,s1.WriteTableEntry(table_entry)
)
Usage example
Say there’s a l2_simple_switch.p4
, and it defines a table in the ingress control block as below.
...
control MyIngress() {
table dmac_forward {
key = {
hdr.ethernet.dstAddr: exact;
}
actions = {
forward_to_port;
drop;
}
size = 4;
default_action = drop;
}
...
}
When you compile this program, the compiler will create l2_simple_switch.p4info.txt
. The P4info file contains a human-readable name to an instance id (integer) mapping. You can retrieve the arguments for the buildTableEntry
method from here. Below is the example code snippet from the controller code used in the in-class demo.
mac_to_port = {"00:00:0a:00:00:01":1,
"00:00:0a:00:00:02":2,
"00:00:0a:00:00:03":3,
"00:00:0a:00:00:04":4}
for eth_src_addr, port_id in mac_to_port.items():
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.dmac_forward",
match_fields={"hdr.ethernet.dstAddr": eth_src_addr},
action_name="MyIngress.forward_to_port",
action_params={"egress_port": port_id}
)
s1.WriteTableEntry(table_entry)