6.4 KiB

Implementing an ARP/ICMP Responder

Introduction

This exercise extends the IPv4 Forwarding program to allow your switches to respond to ARP and ICMP requests. Once implemented, your hosts will be able to ping other hosts connected to the switch, and have the switch respond.

This exercise makes several simplifying assumptions:

  1. The network topology contains exactly one switch and two hosts.
  2. ARP and ICMP requests to the hosts are ignored; only requests sent to the switch receive responses.

Implementing the full functionality of ARP and ICMP is straightforward but beyond the scope of this tutorial and left as an exercise to the reader.

Spoiler alert: There is a reference solution in the solution sub-directory. Feel free to compare your implementation to the reference.

Step 1: Run the (incomplete) starter code

The directory with this README also contains a skeleton P4 program, arp.p4, which initially drops all packets. Your job will be to extend it to reply to ARP and ICMP requests.

As a first step, compile the incomplete arp.p4 and bring up a switch in Mininet to test its behavior.

  1. In your shell, run:

    ./run.sh
    

    This will:

    • compile arp.p4, and
    • start a Mininet instance with one switch (s1) connected to two hosts (h1, h2).
    • The hosts are assigned IPs of 10.0.1.10 and 10.0.2.10.
  2. Once the P4 source compiles without error, you can test your program at the mininet prompt using the ping utility.

    mininet> h1 ping h2

    Once the program is implemented correctly, you will see a response to the ping in the mininet window.

  3. Type exit in the mininet terminal to exit.

A note about the control plane

P4 programs define a packet-processing pipeline, but the rules governing packet processing are inserted into the pipeline by the control plane. When a rule matches a packet, its action is invoked with parameters supplied by the control plane as part of the rule.

In this exercise, the control plane logic has already been implemented. As part of bringing up the Mininet instance, the run.sh script will install packet-processing rules in the tables of each switch. These are defined in the simple_router.config file.

Step 2: Implement ARP/ICMP Replies

In this exercise, we are using entries in the ipv4_lpm table as a database, which we can reference when responding to ARP and ICMP requests. Without the proper entries in the table, the solution will not work.

From a high-level, the task involves implementing two main components: ARP replies and ICMP replies.

ARP Reply

When the switch receives and ARP request asking to resolve the switch's IP address, it will need to perform the following actions:

  1. Swap the source and destination MAC addresses in the Ethernet header,
  2. set the ARP operation to ARP_REPLY (2) in the ARP header,
  3. update the sender hardware address (SHA) and sender protocol address (SPA) in the ARP header to be the MAC and IP addresses of the switch, and
  4. set the target hardware address (THA) and target protocol address (TPA) to be the SHA and SPA of the arriving ARP packet.

ICMP Reply

When the switch receives and ICMP request containing the switch's IP and MAC addresses, it will need to perform the following actions:

  1. Swap the source and destination MAC addresses in the Ethernet header,
  2. swap the source and destination IP addresses in the ICMP header, and
  3. set the type field in the ICMP header to ICMP_ECHO_REPLY (0).
  4. To simplify the exercise, we can ignore the checksum by setting to checksum field to 0.

We have provided a skeleton arp.p4 file to get you started. In this file, places to modify are marked by TODO.

There are, of course, different possible solutions. We describe one approach below. It builds on the IPv4 Forwarding solution, which used the table ipv4_lpm for L3 forwarding, by adding a second table named forward, which checks if a packet is an ARP or ICMP packet and invokes actions to send an ARP reply, forward an IPv4 packet, or send an ICMP reply.

Broadly speaking, a complete solution will contain the following components:

  1. Header type definitions for ethernet_t, arp_t, ipv4_t, and icmp_t.

  2. A structure (named my_metadata_t in arp.p4) with metadata fields for the packet's souce and destination MAC addresses, IPv4 address, egress port, as well as a hard-coded MAC address for the switch.

  3. TODO: Parsers for Ethernet, ARP, IPv4, and ICMP packet header types.

  4. TODO: A control type declaration for ingress processing, containing:

    1. An action for drop.

    2. An action (named set_dst_info) to store information in the metadata structure, rather than immediately writing to the packet header.

    3. A table (named ipv4_lpm) that will match on the destination IP address and invoke the set_dst_info action.

    4. An action to send an ICMP reply.

    5. An action to send an ARP reply.

    6. A table (named forward) that will forward IPv4 packets, send an ARP reply, send an ICMP reply, or drop a packet.

    7. An apply block that implements the control logic to invoke the two tables.

  5. A deparser that emits headers in the proper order.

To keep the exercise simple, we will ignore the ipv4_checksum. You should not need any control plane rules for this exercise.

Step 3: Run your solution

Follow the instructions from Step 1. This time, you should be able to successfully ping the switch.

Troubleshooting

There are several ways that problems might manifest:

  1. arp.p4 fails to compile. In this case, run.sh will report the error emitted from the compiler and stop.

  2. arp.p4 compiles, but the switch does not process packets in the desired way. The build/logs/<switch-name>.log files contain trace messages describing how each switch processes each packet. The output is detailed and can help pinpoint logic errors in your implementation.

Note that there are no control plane rules installed in this example, and so the receive.py and send.py scripts from the IPv4 Forwarding example will not work.

Cleaning up Mininet

In the latter case above, run.sh may leave a Mininet instance running in the background. Use the following command to clean up these instances:

mn -c

Next Steps

Congratulations, your implementation works! Move on to the next exercise: turning your switch into a Calculator.