Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port Mirroring Exercise #335

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ you get started with P4 programming, organized into several modules:
* [Calculator](./exercises/calc)
* [Load Balancing](./exercises/load_balance)
* [Quality of Service](./exercises/qos)
* [Mirroring](./exercises/mirroring)

5. Stateful Packet Processing
* [Firewall](./exercises/firewall)
Expand Down
3 changes: 3 additions & 0 deletions exercises/mirroring/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BMV2_SWITCH_EXE = simple_switch_grpc

include ../../utils/Makefile
119 changes: 119 additions & 0 deletions exercises/mirroring/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Port Mirroring

## Introduction

The objective of this exercise is to write a P4 program that mirrors all the incoming packets to a specific port where a collector is located at.

Upon receiving a packet, your program should make a copy of the corresponding packet and send it to the collector host. Your switch will have a single table, which we have populated with static rules. You will only need to implement the logic for cloning packets.

We will use a simple topology for this exercise. It is a single switch connected to three hosts, h1, h2 and h3 where h3 acts as the collector connected to the mirroring port (port 3) as follow:

```
h1 h3 (Collector)
\ /
\ /
s1
/
/
h2

```
> **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,
`mirroring.p4`, which forwards packets between h1 and h2. Your job will be to
extend this skeleton program to mirror all the packets to the collector host, h3.

Before that, let's compile the incomplete `mirroring.p4` and bring
up a switch in Mininet to test its behavior.

1. In your shell, run:
```bash
make run
```
This will:
* compile `mirroring.p4`, and
* start the topology in Mininet and configure all switches with
the appropriate P4 program + table entries, and
* configure all hosts with the commands listed in
[topology.json](topology.json)

2. You should now see a Mininet command prompt. Bring up the terminal for h3.
```bash
mininet> Xterm h3
```
Since this is the collector host, run `tcpdump` to observe incoming mirrored packets.
```
root@p4:~/tutorials/exercise/mirroring# tcpdump -i eth0
```

2. You should now see a Mininet command prompt. Try to ping between
hosts in the topology:
```bash
mininet> h1 ping h2
mininet> pingall
```
If the packets are mirrored properly, you should observe the corresponding packets on `tcpdump`.
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
```

No packets should be received by h3, since the `mirror` action is not implemented yet.
Your job is to extend this file so it mirrors packets to the collector host.

## Step 2: Implement Port Mirroring
1. **TODO:** An action (called `mirror`) that:
1. Invokes the `clone` extern of the V1Model.
2. Passes the appropriate CloneType and session ID as the parameter to the `clone` method.
2. **TODO:** Call the `mirror` action in your program so that it mirrors all arriving packets.
3. **TODO:** Add port 3 to your specified session.
1. In a new terminal, start the `simple_switch_CLI`
2. Execute the command `mirroring_add` followed the session ID the port number.

## Step 3: Run your solution

Follow the instructions from Step 1. This time, you should be able to
observe packets being mirrored to h3. And, you're done!

### Useful Resources
Check out the resources below that contains further details/explanations on `clone/clone3`.
- [V1Model](https://github.com/p4lang/p4c/blob/master/p4include/v1model.p4)
- [BMv2](https://github.com/p4lang/behavioral-model/blob/master/docs/simple_switch.md)
- Guide on [V1Model Special Ops](https://github.com/jafingerhut/p4-guide/blob/master/v1model-special-ops/v1model-special-ops.p4)

### Food for thought

Questions to consider:
-
- What is the difference between clone and clone3?

### Troubleshooting

There are several problems that might manifest as you develop your program:

1. `mirroring.p4` might fail to compile. In this case, `make run` will
report the error emitted from the compiler and halt.

2. `mirroring.p4` might compile, but the switch might not process packets in the desired
way. The `logs/s1.log` file contain detailed logs 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
```
36 changes: 36 additions & 0 deletions exercises/mirroring/include/headers.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*************************************************************************
*********************** 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;
}

header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
ip4Addr_t srcAddr;
ip4Addr_t dstAddr;
}

struct metadata {
}

struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
}
28 changes: 28 additions & 0 deletions exercises/mirroring/include/parsers.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const bit<16> TYPE_IPV4 = 0x800;

/*************************************************************************
*********************** 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) {
TYPE_IPV4 : parse_ipv4;
}
}

state parse_ipv4 {
packet.extract(hdr.ipv4);
transition accept;
}

}
100 changes: 100 additions & 0 deletions exercises/mirroring/mirroring.p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#include <core.p4>
#include <v1model.p4>

#include "include/headers.p4"
#include "include/parsers.p4"

/*************************************************************************
************ 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) {

// TODO: Identify the CloneType to be used
// TODO: Define `mirror` action to clone incoming packets to the
// mirroring port (port 3) of a specific session.

action drop() {
mark_to_drop(standard_metadata);
}

action normal_forward(egressSpec_t port) {
standard_metadata.egress_spec = port;
}

table lpm_forward {
key = {
hdr.ipv4.dstAddr : lpm;
}
actions = {
normal_forward;
NoAction;
}
default_action = NoAction;
const entries = {
0x0a000001 &&& 0xFFFFFFFF : normal_forward(9w1);
0x0a000002 &&& 0xFFFFFFFF : normal_forward(9w2);
}
}

apply {

// TODO: Call `mirror` action to mirror packets

if(hdr.ipv4.isValid()){
lpm_forward.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) {
apply { }
}

/*************************************************************************
************* 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);
packet.emit(hdr.ipv4);
}
}

/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/

V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
1 change: 1 addition & 0 deletions exercises/mirroring/solution/commands
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mirroring_add 0 3
Loading