Skip to content

Example #1 Simple L3 forwarding (Bmv2)

Sandor Laki edited this page Aug 24, 2022 · 17 revisions

Source code: Simple L3 forwarding

Before starting

  1. To run this example, you need to prepare at least 2 laptops and 1 Raspberry Pis, the two laptops can be replaced to two Raspberry Pis
  2. This exercise may take a few hours for beginners, but you will learn a lot of networking knowledge

Introduction

In this example, you will develop a P4 program that implements basic forwarding for IPv4. With IPv4 forwarding, the switch must perform the following actions for every packet:

  1. Update the source and destination MAC addresses.
  2. Decrement the time-to-live (TTL) in the IP header.
  3. Forward the packet out the appropriate port.

Your switch will have a single table, which the control plane will populate with static rules. Each rule will map an IP address to the MAC address and output port for the next hop. The repository contains a skeleton P4 program, basic.p4, which initially drops all packets. It also contains key pieces of logic replaced by TODO comments. Your assignment is to extend this skeleton program to properly forward IPv4 packets, replacing each TODO with logic implementing the missing piece. We have already defined the control plane rules, so you only need to implement the data plane logic of your P4 program. TheP4 program will be written for the v1model architecture implemented on P4.org’s Bmv2 software switch.

Setup

The evaluation of this project requires 1 Raspberry Pis and 2 laptops, or 3 Raspberry Pis. Please work in groups to test this assignment if you do not have enough devices.

The roles of the devices:

  • Laptop (or Raspberry Pi) #0 –host 1
  • Raspberry Pi #1 –switch
  • Laptop (or Raspberry Pi) #2 –host 2

Implementation

Implement the switch based on the skeleton program basic.p4. Replace all TODO comments with your code. Make sure to implement parser, deparser, match-action control logic etc.

A complete basic.p4 will contain the following components:

  1. Header type definitions for Ethernet (ethernet_t) and IPv4 (ipv4_t).
  2. TODO: Parsers for Ethernet and IPv4 that populate ethernet_t and ipv4_t fields.
  3. An action to drop a packet, using mark_to_drop().
  4. TODO: An action (called ipv4_forward) that:
    1. Sets the egress port for the next hop.
    2. Updates the ethernet destination address with the address of the next hop.
    3. Updates the ethernet source address with the address of the switch.
    4. Decrements the TTL.
  5. TODO: Fix ingress control logic that:
    1. ipv4_lpm table should be applied only when IPv4 header is valid
  6. TODO: A deparser that selects the order in which fields inserted into the outgoing packet.

The following figure shows the configuration using a Raspberry Pi as a switch:

The following is an example of an expected configuration. MAC addresses will be different. IP addresses may be different. Importantly, each subnet needs to use the same sub-domain (e.g., 169.254.21.0/16):
ETH0: IP address: 169.254.21.222, MAC address: e4:5f:01:8c:7e:52
ETH2: IP address: 169.254.18.165, MAC address: e4:5f:01:8d:d2:a6
USB1A: IP address: 169.254.21.220, MAC address: 0c:37:96:5f:8a:20
USB1B: IP address: 169.254.18.164, MAC address: 0c:37:96:5f:8a:0e

Configuration steps

  • Login to Raspberry Pi #1 (switch)

  • Check the interfaces (USB adapters) that connect to the other two hosts by using ip a:

In our example, there are two interfaces: USB-1A is enx0c37965f8a20 connected to host 1 (host1 ETH0) and USB-1B is enx0c37965f8a0e connected to host 2 (host2 ETH2). Both interfaces (USB-1A and USB-1B) need to be assigned an IP address, one is 169.254.21.220/16, and the other is 169.254.18.164/16.

  • Login to host 1
  • Setup a static IP for host 1 (must match the subnet of USB-1A):
sudo ifconfig eth0 169.254.21.222 netmask 255.255.0.0 

or

sudo ifconfig eth0 169.254.21.222/16 
  • Login to host 2
  • Setup a static IP for host 2 (must match the subnet of USB-1B):
sudo ifconfig eth0 169.254.18.165 netmask 255.255.0.0 

or

sudo ifconfig eth0 169.254.18.165/16 
  • Set a route to host1 through eth0 (use the local interface name and the remote ip address):
sudo ip route add dev eth0 169.254.21.222
  • Setup static ARP for host 2 so that it can reach host 1(arp -s ):
sudo arp -s 169.254.21.222 e4:5f:01:8c:7e:52   
  • Set a route to host2 through eth0 (use the local interface name and the remote ip address):
sudo ip route add dev eth0 169.254.18.165 
  • Go back to the terminal of host 1 and configure the static ARP, so it can also reach host 2:
sudo arp -s 169.254.18.165 e4:5f:01:8d:d2:a6   
  • Go to the terminal of Raspberry #1 (switch) and compile the completed P4 program using p4c.
p4c --target bmv2 --arch v1model --std p4-16 basic.p4 
  • Run the JSON file with two interfaces that connect to other two hosts.
simple_switch -i 0@enx0c37965f8a20 -i 1@enx0c37965f8a0e basic.json & 
  • Populate flow rules

[Option 1]

  1. Run runtime CLI :
Simple_switch_CLI 
  1. Enter two forwarding rules in the runtime terminal:
table_add MyIngress.ipv4_lpm MyIngress.ipv4_forward 169.254.21.222/32 => e4:5f:01:8c:7e:52 0  
table_add MyIngress.ipv4_lpm MyIngress.ipv4_forward 169.254.18.165/32 => e4:5f:01:8d:d2:a6 1 

[Option 2]

  1. Create a empty file named commands.txt
  2. Add two forwarding rules in commands.txt
table_add MyIngress.ipv4_lpm MyIngress.ipv4_forward 169.254.21.222/32 => e4:5f:01:8c:7e:52 0 
table_add MyIngress.ipv4_lpm MyIngress.ipv4_forward 169.254.18.165/32 => e4:5f:01:8d:d2:a6 1 
  1. Execute commands.txt in runtime CLI:
simple_switch_CLI < commands.txt 
  • Open a terminal in host 1 and ping host 2:
ping 169.254.18.165 

When compiling the original (empty) P4 program, any attempt to ping will fail, because the switch is programmed to drop all packets on arrival. If your code was implemented correctly and the configurations of hosts are correct, you should be able to ping between devices. Try also running an iperf test!

Solution

The complete P4 program is available in https://github.com/p4lang/p4pi/tree/master/examples/bmv2/l3forwarding/solution.

Clone this wiki locally