Exercise for L2 multicast (#288)
This commit is contained in:
parent
b8baba5df5
commit
590f4ff6f2
4
exercises/multicast/Makefile
Normal file
4
exercises/multicast/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
BMV2_SWITCH_EXE = simple_switch_grpc
|
||||
TOPO = sig-topo/topology.json
|
||||
|
||||
include ../../utils/Makefile
|
After Width: | Height: | Size: 97 B |
154
exercises/multicast/README.md
Normal file
154
exercises/multicast/README.md
Normal file
@ -0,0 +1,154 @@
|
||||
# Implementing Multicast
|
||||
|
||||
## Introduction
|
||||
|
||||
The objective of this exercise is to write a P4 program that multicasts packets
|
||||
to a group of ports.
|
||||
|
||||
|
||||
Upon receiving an Ethernet packet, the switch looks up the output port based on
|
||||
the destination MAC address. If it is a miss, the switch broadcast packets on
|
||||
ports belonging to a multicast group (if ingress port appears in the group, the
|
||||
packet will be dropped in the egress pipeline).
|
||||
|
||||
|
||||
Your switch will have a single table, which the control plane will populate with
|
||||
static rules. Each rule will map an Ethernet MAC address to the output port. We
|
||||
have already defined the control plane rules, so you only need to implement the
|
||||
data plane logic of your P4 program.
|
||||
|
||||
We will use the linear topology for this exercise. It is a single switch that
|
||||
connects four hosts as follow:
|
||||
|
||||
h1 h2
|
||||
| |
|
||||
---------------------------- s1
|
||||
| |
|
||||
h3 h4
|
||||
|
||||
Our P4 program will be written for the V1Model architecture implemented on
|
||||
P4.org's bmv2 software switch. The architecture file for the V1Model can be
|
||||
found at: /usr/local/share/p4c/p4include/v1model.p4. This file describes the
|
||||
interfaces of the P4 programmable elements in the architecture, the supported
|
||||
externs, as well as the architecture's standard metadata fields. We encourage
|
||||
you to take a look at it.
|
||||
|
||||
> **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,
|
||||
`multicast.p4`, which initially drops all packets. Your job will be to extend
|
||||
this skeleton program to properly forward Ethernet packets.
|
||||
|
||||
Before that, let's compile the incomplete `multicast.p4` and bring up a switch
|
||||
in Mininet to test its behavior.
|
||||
|
||||
1. In your shell, run:
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
This will:
|
||||
* compile `multicast.p4`, and
|
||||
* start the sig-topo in Mininet and configure all switches with
|
||||
the appropriate P4 program + table entries, and
|
||||
* configure all hosts with the commands listed in
|
||||
[pod-topo/topology.json](./pod-topo/topology.json)
|
||||
|
||||
2. You should now see a Mininet command prompt. Try to ping between
|
||||
hosts in the topology:
|
||||
```bash
|
||||
mininet> h1 ping h2
|
||||
mininet> pingall
|
||||
```
|
||||
3. Type `exit` to leave each xterm and the Mininet command line.
|
||||
Then, to stop mininet:
|
||||
```bash
|
||||
make stop
|
||||
```
|
||||
And to delete all pcaps, build files, and logs:
|
||||
```bash
|
||||
make clean
|
||||
```
|
||||
|
||||
The ping failed because each switch is programmed according to `multicast.p4`,
|
||||
which drops all packets on arrival. Your job is to extend this file so it
|
||||
forwards packets.
|
||||
|
||||
### A note about the control plane
|
||||
|
||||
A P4 program defines a packet-processing pipeline, but the rules within each
|
||||
table are inserted 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, we have already implemented the control plane logic for you.
|
||||
As part of bringing up the Mininet instance, the `make run` command will install
|
||||
packet-processing rules in the tables of each switch. These are defined in the
|
||||
`sX-runtime.json` files, where `X` corresponds to the switch number.
|
||||
|
||||
**Important:** We use P4Runtime to install the control plane rules. The
|
||||
content of files `sX-runtime.json` refer to specific names of tables, keys, and
|
||||
actions, as defined in the P4Info file produced by the compiler (look for the
|
||||
file `build/basic.p4.p4info.txt` after executing `make run`). Any changes in the P4
|
||||
program that add or rename tables, keys, or actions will need to be reflected in
|
||||
these `sX-runtime.json` files.
|
||||
|
||||
## Step 2: Implement L2 Multicast
|
||||
|
||||
The `multicast.p4` file contains a skeleton P4 program with key pieces of logic
|
||||
replaced by `TODO` comments. Your implementation should follow the structure
|
||||
given in this file---replace each `TODO` with logic implementing the missing
|
||||
piece.
|
||||
|
||||
A complete `multicast.p4` will contain the following components:
|
||||
|
||||
1. Header type definitions for Ethernet (`ethernet_t`)
|
||||
2. An action to drop a packet, using `mark_to_drop()`.
|
||||
3. **TODO:** An action (called `multicast`) that sends multiple copies of packets
|
||||
to a group of output ports.
|
||||
4. **TODO:** Add the `multicast` action to the list of available actions
|
||||
5. **TODO:** Set `multicast` as default action for table `mac_lookup`
|
||||
|
||||
## Step 3: Run your solution
|
||||
|
||||
Follow the instructions from Step 1. This time, you should be able to
|
||||
successfully ping between `h1`, `h2` and `h3` but not `h4` in the topology.
|
||||
|
||||
6. **TODO:** Add port 4 to the multicast group in file `sig-topo/s1-runtime.json`
|
||||
|
||||
### Food for thought
|
||||
|
||||
Other questions to consider:
|
||||
- How would you enhance your program to respond to ARP requests?
|
||||
- How would you enhance your program to support MAC learning from the controller?
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
There are several problems that might manifest as you develop your program:
|
||||
|
||||
1. `multicast.p4` might fail to compile. In this case, `make run` will
|
||||
report the error emitted from the compiler and halt.
|
||||
|
||||
2. `multicast.p4` might compile but fail to support the control plane rules in
|
||||
the `s1-runtime.json` file that `make run` tries to install using P4Runtime. In
|
||||
this case, `make run` will report errors if control plane rules cannot be
|
||||
installed. Use these error messages to fix your `multicast.p4` implementation.
|
||||
|
||||
3. `multicast.p4` might compile, and the control plane rules might be installed,
|
||||
but the switch might not process packets in the desired way. The `logs/sX.log`
|
||||
files contain detailed logs that describing how each switch processes each
|
||||
packet. The output is detailed and can help pinpoint logic errors in your
|
||||
implementation.
|
||||
|
||||
#### Cleaning up Mininet
|
||||
|
||||
In the latter two cases above, `make run` may leave a Mininet instance
|
||||
running in the background. Use the following command to clean up
|
||||
these instances:
|
||||
|
||||
```bash
|
||||
make stop
|
||||
```
|
3
exercises/multicast/disable_ipv6.sh
Executable file
3
exercises/multicast/disable_ipv6.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
|
||||
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
|
149
exercises/multicast/multicast.p4
Normal file
149
exercises/multicast/multicast.p4
Normal file
@ -0,0 +1,149 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
|
||||
|
||||
header ethernet_t {
|
||||
macAddr_t dstAddr;
|
||||
macAddr_t srcAddr;
|
||||
bit<16> etherType;
|
||||
}
|
||||
|
||||
|
||||
struct metadata {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** P A R S E R ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
parser MyParser(packet_in packet,
|
||||
out headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
state start {
|
||||
transition parse_ethernet;
|
||||
}
|
||||
|
||||
state parse_ethernet {
|
||||
packet.extract(hdr.ethernet);
|
||||
transition select(hdr.ethernet.etherType) {
|
||||
default : accept;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************ C H E C K S U M V E R I F I C A T I O N *************
|
||||
*************************************************************************/
|
||||
|
||||
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
|
||||
apply { }
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************** I N G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyIngress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
action drop() {
|
||||
mark_to_drop(standard_metadata);
|
||||
}
|
||||
|
||||
// TODO: define `multicast` action to multicast packets to group 1
|
||||
// Hint: Check v1model for multicast group
|
||||
|
||||
action mac_forward(egressSpec_t port) {
|
||||
standard_metadata.egress_spec = port;
|
||||
}
|
||||
|
||||
table mac_lookup {
|
||||
key = {
|
||||
hdr.ethernet.dstAddr : exact;
|
||||
}
|
||||
actions = {
|
||||
// TODO: add `multicast` action to the list of available actions
|
||||
mac_forward;
|
||||
drop;
|
||||
}
|
||||
size = 1024;
|
||||
// TODO : replace default drop action by multicast
|
||||
default_action = drop;
|
||||
}
|
||||
apply {
|
||||
if (hdr.ethernet.isValid())
|
||||
mac_lookup.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** E G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyEgress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
action drop() {
|
||||
mark_to_drop(standard_metadata);
|
||||
}
|
||||
|
||||
apply {
|
||||
// Prune multicast packet to ingress port to preventing loop
|
||||
if (standard_metadata.egress_port == standard_metadata.ingress_port)
|
||||
drop();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* C H E C K S U M C O M P U T A T I O N **************
|
||||
*************************************************************************/
|
||||
|
||||
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
|
||||
apply {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*********************** D E P A R S E R *******************************
|
||||
*************************************************************************/
|
||||
|
||||
control MyDeparser(packet_out packet, in headers hdr) {
|
||||
apply {
|
||||
packet.emit(hdr.ethernet);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
66
exercises/multicast/sig-topo/s1-runtime.json
Normal file
66
exercises/multicast/sig-topo/s1-runtime.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/multicast.p4.p4info.txt",
|
||||
"bmv2_json": "build/multicast.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:01:11"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:02:22"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:03:33"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:04:44"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"multicast_group_entries" : [
|
||||
{
|
||||
"multicast_group_id" : 1,
|
||||
"replicas" : [
|
||||
{
|
||||
"egress_port" : 1,
|
||||
"instance" : 1
|
||||
},
|
||||
{
|
||||
"egress_port" : 2,
|
||||
"instance" : 1
|
||||
},
|
||||
{
|
||||
"egress_port" : 3,
|
||||
"instance" : 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
18
exercises/multicast/sig-topo/topology.json
Normal file
18
exercises/multicast/sig-topo/topology.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"hosts": {
|
||||
"h1": {"ip": "10.0.0.1/24", "mac": "08:00:00:00:01:11",
|
||||
"commands":["ip route add 10.0.0.0/24 dev eth0"]},
|
||||
"h2": {"ip": "10.0.0.2/24", "mac": "08:00:00:00:02:22",
|
||||
"commands":["ip route add 10.0.0.0/24 dev eth0"]},
|
||||
"h3": {"ip": "10.0.0.3/24", "mac": "08:00:00:00:03:33",
|
||||
"commands":["ip route add 10.0.0.0/24 dev eth0"]},
|
||||
"h4": {"ip": "10.0.0.4/24", "mac": "08:00:00:00:04:44",
|
||||
"commands":["ip route add 10.0.0.0/24 dev eth0"]}
|
||||
},
|
||||
"switches": {
|
||||
"s1": { "runtime_json" : "sig-topo/s1-runtime.json" }
|
||||
},
|
||||
"links": [
|
||||
["h1", "s1-p1"], ["h2", "s1-p2"], ["h3", "s1-p3"], ["h4", "s1-p4"]
|
||||
]
|
||||
}
|
149
exercises/multicast/solution/multicast.p4
Normal file
149
exercises/multicast/solution/multicast.p4
Normal file
@ -0,0 +1,149 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
|
||||
|
||||
header ethernet_t {
|
||||
macAddr_t dstAddr;
|
||||
macAddr_t srcAddr;
|
||||
bit<16> etherType;
|
||||
}
|
||||
|
||||
|
||||
struct metadata {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** P A R S E R ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
parser MyParser(packet_in packet,
|
||||
out headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
state start {
|
||||
transition parse_ethernet;
|
||||
}
|
||||
|
||||
state parse_ethernet {
|
||||
packet.extract(hdr.ethernet);
|
||||
transition select(hdr.ethernet.etherType) {
|
||||
default : accept;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************ C H E C K S U M V E R I F I C A T I O N *************
|
||||
*************************************************************************/
|
||||
|
||||
control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
|
||||
apply { }
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
************** I N G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyIngress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
action drop() {
|
||||
mark_to_drop(standard_metadata);
|
||||
}
|
||||
|
||||
action multicast() {
|
||||
standard_metadata.mcast_grp = 1;
|
||||
}
|
||||
|
||||
action mac_forward(egressSpec_t port) {
|
||||
standard_metadata.egress_spec = port;
|
||||
}
|
||||
|
||||
table mac_lookup {
|
||||
key = {
|
||||
hdr.ethernet.dstAddr : exact;
|
||||
}
|
||||
actions = {
|
||||
multicast;
|
||||
mac_forward;
|
||||
drop;
|
||||
}
|
||||
size = 1024;
|
||||
default_action = multicast;
|
||||
}
|
||||
apply {
|
||||
if (hdr.ethernet.isValid())
|
||||
mac_lookup.apply();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** E G R E S S P R O C E S S I N G *******************
|
||||
*************************************************************************/
|
||||
|
||||
control MyEgress(inout headers hdr,
|
||||
inout metadata meta,
|
||||
inout standard_metadata_t standard_metadata) {
|
||||
|
||||
action drop() {
|
||||
mark_to_drop(standard_metadata);
|
||||
}
|
||||
|
||||
apply {
|
||||
// Prune multicast packet to ingress port to preventing loop
|
||||
if (standard_metadata.egress_port == standard_metadata.ingress_port)
|
||||
drop();
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* C H E C K S U M C O M P U T A T I O N **************
|
||||
*************************************************************************/
|
||||
|
||||
control MyComputeChecksum(inout headers hdr, inout metadata meta) {
|
||||
apply {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*********************** D E P A R S E R *******************************
|
||||
*************************************************************************/
|
||||
|
||||
control MyDeparser(packet_out packet, in headers hdr) {
|
||||
apply {
|
||||
packet.emit(hdr.ethernet);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
70
exercises/multicast/solution/s1-runtime.json
Normal file
70
exercises/multicast/solution/s1-runtime.json
Normal file
@ -0,0 +1,70 @@
|
||||
{
|
||||
"target": "bmv2",
|
||||
"p4info": "build/multicast.p4.p4info.txt",
|
||||
"bmv2_json": "build/multicast.json",
|
||||
"table_entries": [
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:01:11"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:02:22"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:03:33"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"table": "MyIngress.mac_lookup",
|
||||
"match": {
|
||||
"hdr.ethernet.dstAddr": "08:00:00:00:04:44"
|
||||
},
|
||||
"action_name": "MyIngress.mac_forward",
|
||||
"action_params": {
|
||||
"port": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"multicast_group_entries" : [
|
||||
{
|
||||
"multicast_group_id" : 1,
|
||||
"replicas" : [
|
||||
{
|
||||
"egress_port" : 1,
|
||||
"instance" : 1
|
||||
},
|
||||
{
|
||||
"egress_port" : 2,
|
||||
"instance" : 1
|
||||
},
|
||||
{
|
||||
"egress_port" : 3,
|
||||
"instance" : 1
|
||||
},
|
||||
{
|
||||
"egress_port" : 4,
|
||||
"instance" : 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user