Skip to content
Snippets Groups Projects
Commit c6600d42 authored by ekirci's avatar ekirci
Browse files

added second week's exercise

parent 9a74cefa
No related branches found
No related tags found
No related merge requests found
Showing
with 819 additions and 0 deletions
# Equal-Cost Multi-Path Routing
## Introduction
In this exercise we will implement a layer 3 forwarding switch that is able to load balance traffic
towards a destination across equal cost paths. To load balance traffic across multiple ports we will implement [ECMP](https://en.wikipedia.org/wiki/Equal-cost_multi-path_routing) (Equal-Cost
Multi-Path) routing. When a packet with multiple candidate paths arrives, our switch should assign the next-hop by hashing some fields from the
header and compute this hash value modulo the number of possible equal paths. For example in the topology below, when `s1` has to send
a packet to `h2`, the switch should determine the output port by computing: `hash(some-header-fields) mod 4`. To prevent out of order packets, ECMP hashing is done on a per-flow basis,
which means that all packets with the same source and destination IP addresses and the same source and destination
ports always hash to the same next hop.
<p align="center">
<img src="images/multi_hop_topo.png" title="Multi Hop Topology"/>
<p/>
To read more about ECMP see the following [page](https://www.juniper.net/documentation/us/en/software/junos/flow-packet-processing/topics/topic-map/security-ecmp-flow-based-forwarding.html)
## Before Starting
As usual, we provide you with the following files:
- `p4app.json`: describes the topology we want to create with the help of mininet and p4-utils package.
- `network.py`: a Python scripts that initializes the topology using *Mininet* and *P4-Utils*. One can use indifferently `network.py` or `p4app.json` to start the network.
- `p4src/ecmp.p4`: p4 program skeleton to use as a starting point.
- `p4src/includes`: In today's exercise we will split our p4 code in multiple files for the first time. In the includes directory you will find `headers.p4` and `parsers.p4` (which also have to be completed).
- `send.py`: a small python script to generate multiple packets with different tcp port.
#### Notes about p4app.json
For this exercise (and next one) we will use a new IP assignment strategy. If you have a look at `p4app.json` you will see that
the option is set to `mixed`. Therefore, only hosts connected to the same switch will be assigned to the same subnet. Hosts connected
to a different switch will belong to a different `/24` subnet. If you use the namings `hX` and `sX` (e.g h1, h2, s1...), the IP assignment
goes as follows: `10.x.x.y`. Where `x` is the switch id (upper and lower bytes), and `y` is the host id. For example, in the topology above,
`h1` gets `10.0.1.1` and `h2` gets `10.0.6.2`.
You can find all the documentation about `p4app.json` in the `p4-utils` [documentation](https://nsg-ethz.github.io/p4-utils/usage.html#json). Also, you can find information about assignment strategies [here](https://nsg-ethz.github.io/p4-utils/usage.html#automated-assignment-strategies).
## Implementing the L3 forwarding switch + ECMP
To solve this exercise we have to program our switch such that is able to forward L3 packets when there is one
possible next hop or more. For that we will use two tables: in the first table we match the destination IP and
depending on whether ECMP has to be applied (for that destination) we set the output port or a ecmp_group. For the later we
will apply a second table that maps `(ecmp_group, hash_output)`` to an egress port.
This time you will have to fill the gaps in several files: `p4src/ecmp.p4`, `p4src/include/headers.p4` and `p4src/include/parsers.p4`. Additionally, you will have to create a `cli` command file for each switch and name them `sX-commands.txt` (see inside the `p4app.json`). To keep the less cluttered, we placed all the command files inside `sw-commands` directory.
To successfully complete the exercise you have to do the following:
1. Use the header definitions that are already provided.
2. Define the parser that is able to parse packets up to `tcp`. Note that for simplicity we do not consider `udp` packets
in this exercise. This time you must define the parser in: `p4src/include/parsers.p4`.
3. Define the deparser. Just emit all the parsed headers in the right order.
4. Define a match-action table that matches the IP destination address of every packet and has three actions: `set_nhop`, `ecmp_group`, `drop`.
Set the drop action as default.
5. Define the action `set_nhop`. This action takes 2 parameters: destination mac and egress port. Use the parameters to set the destination mac of the packet and
`egress_spec`. Set the source mac as the previous destination mac (this is not what a real L3 switch would do, we just do it for simplicity). In a more realistic implementation we would create a table
that maps egress_ports to each switch interface mac address, however since the source mac address is not very important for this exercise just do the swap). When sending packets from a switch to another switch, the destination
address is not very important, and thus you can use a random one. However, keep in mind that when the packet is sent to a host it needs to have the right destination MAC address otherwise the linux kernel will drop the packet.
Finally, and still in `set_nhop` action, decrease the packet's TTL by 1.
**Note:** The syntax for accomodating multiple parameters to `table_add` is the following (with no brackets or commas):
```
table_add ecmp_group_to_nhop set_nhop 1 0 => 00:00:0a:00:02:01 2
```
**Note:** since we are in a L3 network, when you send packets from `s1` to `s2`
you have to use the dst mac of the switch interface not the mac address of the
receiving host, that instead is done in the very last hop. Finally, decrease the
packet's TTL by 1.
6. Define the action `ecmp_group`. This action takes two parameters, the ecmp group id (14 bits), and the number of next hops (16 bits). This action is one of the key parts of the ECMP algorithm. You have to do several things:
1. In this action we will compute a hash function. To store the output you need to define a metadata field. Define `ecmp_hash` (14 bits) inside
the metadata struct in `headers.p4`. Use the [`hash`](https://github.com/p4lang/p4c/blob/57f54582a9401b8a89f8254738fca0f350dd557e/p4include/v1model.p4#L453) extern function to compute the hash of packets 5-tuple (src ip, dst ip, src port, dst port, protocol). The signature of a hash function is:
`hash(output_field, (crc16 or crc32), (bit<1>)0, {fields to hash}, (bit<16>)modulo)`. You can find all the available hash functions [here](https://github.com/p4lang/p4c/blob/57f54582a9401b8a89f8254738fca0f350dd557e/p4include/v1model.p4#L403). For example, if you want to use `crc32`, your algorithm will be: `HashAlgorithm.crc32`. For example if you want to hash the src and dst IPs, and get a value between 0 and 8, and store it in `meta.output` you would do it like this:
```
hash(meta.output,
HashAlgorithm.crc32,
(bit<1>)0,
{ hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr,
},
(bit<16>)8);
```
2. Define another metadata field and call it `ecmp_group_id` (14 bits).
3. Finally copy the value of the first action parameter ecmp group in the metadata field you just defined (`ecmp_group_id`) this will be used
to match in the second table.
**Note**: Why is the`ecmp_group_id` is needed?. In few words, it allows you to map from
one ip address to a set of ports, which does not have to be the 4 ports we use
in this exercise. For example, you could have that for `IP1` you use only the
upper 2 ports and for `IP2` you loadbalance using the two lower ports. Thus, by
creating two ecmp groups you can easily map any destination address to any set
of ports.
7. Define the second match-action table used to set `ecmp_groups` to real next
hops. The table should have `exact` matches to the metadata fields your defined
in the previous step. Thus, it should match to the `meta.ecmp_group_id` and then
to the output of the hash function `meta.ecmp_hash` (which will be a value
ranging from 0 to `NUM_NEXT_HOPS-1`). A match in this table should call the
`set_nhop` action that you already defined above, a miss should mark the packet
to be dropped (set `drop` as default action). This enables us to use any subset
of interfaces. For example imagine that in the topology above we have `h2` and
`h3` ( h3 does not exist, but just for the sake of the example) we could define
two different `ecmp` groups (in the previous table), one that maps to port 2 and
4, and one that maps to port 3 and 5. And then in this table we could add two
rules per group, to make the outputs `[0,1]` from the hash function match [2,4]
and `[3,5]` respectively.
8. Define the ingress control logic:
1. Check if the ipv4 header was parsed (use `isValid`).
2. Apply the first table.
3. If the action `ecmp_group` was called during the first table apply. Call the second table.
Note: to know which action was called during an apply you can use a switch statement and `action_run`, to see more information about how to check which action was used, check out
the [P4 16 specification](https://p4.org/p4-spec/docs/P4-16-v1.2.2.html#sec-invoke-mau)
9. In this exercise we modify a packet's field for the first time (remember we have to subtract 1 to the ip.ttl field). When doing so, the `ipv4` checksum field need
to be updated otherwise other network devices (or receiving hosts) might drop the packet. To do that, the `v1model` provides an `extern` function that can be called
inside the `MyComputeChecksum` control to update checksum fields. In this exercise, you do not have to do anything, however just go to the `ecmp.p4` file and check how
the `update_checksum` is used.
10. This time you have to write six `sX-commands.txt` files, one per switch. Note that only `s1` and `s6` need to have `ecmp` groups installed. For all
the other switches setting rules for the first table (using action `set_nhop`) will suffice. For `s1` you have to set a direct next hop towards `h1`, and a ecmp
group towards `h2`. Set the ecmp group with `id = 1` and `num_hops = 4`. Then define 4 rules that map from 0 to 3 to one of the 4 switch output ports
(using the second table).
## Testing your solution
Once you have the `ecmp.p4` program finished (and all the `commands.txt` files) you can test its behaviour:
1. Start the topology (this will also compile and load the program).
```bash
sudo p4run
```
or
```bash
sudo python network.py
```
2. Check that you can ping:
```bash
mininet> pingall
```
3. Monitor the 4 links from `s1` that will be used during `ecmp` (from `s1-eth2` to `s1-eth5`). Doing this you will be able to check which path is each flow
taking.
```
sudo tcpdump -enn -i s1-ethX
```
4. Ping between two hosts:
You should see traffic in only 1 or 2 interfaces (due to the return path).
Since all the ping packets have the same 5-tuple.
5. Do iperf between two hosts:
You should also see traffic in 1 or 2 interfaces (due to the return path).
Since all the packets belonging to the same flow have the same 5-tuple, and thus the hash always returns the same index.
6. Get a terminal in `h1`. Use the `send.py` script.
```bash
python send.py 10.0.6.2 1000
```
This will send `tcp syn` packets with random ports. Now you should see packets going to all the interfaces, since each packet will have a different hash.
#### Some notes on debugging and troubleshooting
We have added a [small guideline](https://github.com/nsg-ethz/p4-learning/wiki/Debugging-and-Troubleshooting) in the documentation section. Use it as a reference when things do not work as
expected.
2-Programmability/02-Load_Balancing/01-ECMP/images/hash.png

30.2 KiB

2-Programmability/02-Load_Balancing/01-ECMP/images/multi_hop_topo.png

16.6 KiB

from p4utils.mininetlib.network_API import NetworkAPI
net = NetworkAPI()
# Network general options
net.setLogLevel('info')
# Network definition
net.addP4Switch('s1', cli_input='sw-commands/s1-commands.txt')
net.addP4Switch('s2', cli_input='sw-commands/s2-commands.txt')
net.addP4Switch('s3', cli_input='sw-commands/s3-commands.txt')
net.addP4Switch('s4', cli_input='sw-commands/s4-commands.txt')
net.addP4Switch('s5', cli_input='sw-commands/s5-commands.txt')
net.addP4Switch('s6', cli_input='sw-commands/s6-commands.txt')
net.setP4SourceAll('p4src/ecmp.p4')
net.addHost('h1')
net.addHost('h2')
net.addLink("h1", "s1")
net.addLink("h2", "s6")
net.addLink("s1", "s2")
net.addLink("s1", "s3")
net.addLink("s1", "s4")
net.addLink("s1", "s5")
net.addLink("s2", "s6")
net.addLink("s3", "s6")
net.addLink("s4", "s6")
net.addLink("s5", "s6")
# Assignment strategy
net.mixed()
# Nodes general options
net.enablePcapDumpAll()
net.enableLogAll()
net.enableCli()
net.startNetwork()
\ No newline at end of file
{
"p4_src": "p4src/ecmp.p4",
"cli": true,
"pcap_dump": true,
"enable_log": true,
"topology": {
"assignment_strategy": "mixed",
"links": [["h1", "s1"], ["h2", "s6"], ["s1", "s2"], ["s1", "s3"], ["s1", "s4"], ["s1", "s5"], ["s2", "s6"], ["s3", "s6"], ["s4", "s6"], ["s5", "s6"]],
"hosts": {
"h1": {
},
"h2": {
}
},
"switches": {
"s1": {
"cli_input": "sw-commands/s1-commands.txt"
},
"s2": {
"cli_input": "sw-commands/s2-commands.txt"
},
"s3": {
"cli_input": "sw-commands/s3-commands.txt"
},
"s4": {
"cli_input": "sw-commands/s4-commands.txt"
},
"s5": {
"cli_input": "sw-commands/s5-commands.txt"
},
"s6": {
"cli_input": "sw-commands/s6-commands.txt"
}
}
}
}
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>
//My includes
#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) {
action drop() {
mark_to_drop(standard_metadata);
}
action ecmp_group(bit<14> ecmp_group_id, bit<16> num_nhops){
//TODO 6: define the ecmp_group action, here you need to hash the 5-tuple mod num_ports and safe it in metadata
}
action set_nhop(macAddr_t dstAddr, egressSpec_t port) {
//TODO 5: Define the set_nhop action.
}
table ecmp_group_to_nhop {
//TODO 7: define the ecmp table, this table is only called when multiple hops are available
}
table ipv4_lpm {
//TODO 4: define the ip forwarding table
}
apply {
//TODO 8: implement the ingress logic: check validities, apply first table, and if needed the second table.
}
}
/*************************************************************************
**************** 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 {
update_checksum(
hdr.ipv4.isValid(),
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.dscp,
hdr.ipv4.ecn,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16);
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
//switch architecture
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
/*************************************************************************
*********************** H E A D E R S ***********************************
*************************************************************************/
const bit<16> TYPE_IPV4 = 0x800;
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<6> dscp;
bit<2> ecn;
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;
}
header tcp_t{
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<4> res;
bit<1> cwr;
bit<1> ece;
bit<1> urg;
bit<1> ack;
bit<1> psh;
bit<1> rst;
bit<1> syn;
bit<1> fin;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}
struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
tcp_t tcp;
}
struct metadata {
//TODO 6.1: define the metadata needed to store the ecmp_group_id and the hash output
}
/*************************************************************************
*********************** 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 {
//TODO 2: Define a parser for ethernet, ipv4 and tcp
}
}
/*************************************************************************
*********************** D E P A R S E R *******************************
*************************************************************************/
control MyDeparser(packet_out packet, in headers hdr) {
apply {
//TODO 3: Deparse the ethernet, ipv4 and tcp headers
}
}
\ No newline at end of file
#!/usr/bin/env python3
import argparse
import sys
import socket
import random
import struct
from scapy.all import sendp, get_if_list, get_if_hwaddr
from scapy.all import Ether, IP, UDP, TCP
def get_if():
ifs=get_if_list()
iface=None # "h1-eth0"
for i in get_if_list():
if "eth0" in i:
iface=i
break
if not iface:
print("Cannot find eth0 interface")
exit(1)
return iface
def main():
if len(sys.argv)<3:
print('pass 2 arguments: <destination> <number_of_random_packets>')
exit(1)
addr = socket.gethostbyname(sys.argv[1])
iface = get_if()
print("sending on interface %s to %s" % (iface, str(addr)))
for _ in range(int(sys.argv[2])):
pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff')
pkt = pkt /IP(dst=addr) / TCP(dport=random.randint(5000,60000), sport=random.randint(49152,65535))
sendp(pkt, iface=iface, verbose=False)
if __name__ == '__main__':
main()
# Add your static control plane commands here
# Add your static control plane commands here
# Add your static control plane commands here
# Add your static control plane commands here
# Add your static control plane commands here
# Add your static control plane commands here
# Flowlet Switching
## Introduction
In the previous exercise we implemented ECMP, a very basic (but widely used) technique to load balance traffic across
multiple equal cost paths. ECMP works very well when it has to load balance many small flows with similar sizes (since it
randomly maps them to one of the possible paths). However, real traffic does not look as described above, real traffic is composed by many
small flows, but also but very few that are quite bigger. This makes ECMP suffer from a well-known performance problem such as hash collisions,
in which few big flows end up colliding in the same path. In this exercise we will use state and information provided by the simple_switch's
`standard_metadata` to fix the collision problem of ECMP, by implementing flowlet switching on top.
Flowlet switching leverages the burstiness of TCP flows to achieve a better load balancing. TCP flows tend to come in bursts (for instance because
a flow needs to wait to get window space). Every time there is gap which is big enough (i.e., 50ms) between packets from the same flow, flowlet switching
will rehash the flow to another path (by hashing an ID value together with the 5-tuple).
For more information about flowlet switching check out this [paper](https://www.usenix.org/system/files/conference/nsdi17/nsdi17-vanini.pdf)
<p align="center">
<img src="images/multi_hop_topo.png" title="Multi Hop Topology"/>
<p/>
## Before Starting
As usual, we provide you with the following files:
- `p4app.json`: describes the topology we want to create with the help of mininet and p4-utils package.
- `network.py`: a Python scripts that initializes the topology using *Mininet* and *P4-Utils*. One can use indifferently `network.py` or `p4app.json` to start the network.
- `p4src/flowlet_switching.p4`: p4 program skeleton to use as a starting point.
- `p4src/includes`: In the includes directory you will find `headers.p4` and `parsers.p4` (which also have to be completed).
- `send.py`: a small python script to send burst of packets that belong to the same flow.
#### Notes about p4app.json
For this exercise (and the next two) we will use a new IP assignment strategy. If you have a look at `p4app.json` you will see that
the option is set to `mixed`. Therefore, only hosts connected to the same switch will be assigned to the same subnet. Hosts connected
to a different switch will belong to a different `/24` subnet. If you use the namings `hY` and `sX` (e.g h1, h2, s1...), the IP assignment
goes as follows: `10.x.x.y`. Where `x` is the switch id (upper and lower bytes), and `y` is the host id. For example, in the topology above,
`h1` gets `10.0.1.1` and `h2` gets `10.0.2.2`.
You can find all the documentation about `p4app.json` in the `p4-utils` [documentation](https://nsg-ethz.github.io/p4-utils/usage.html#json). Also, you can find information about assignment strategies [here](https://nsg-ethz.github.io/p4-utils/usage.html#automated-assignment-strategies).
## Implementing the flowlet switching enhancement
This exercise is an enhancement of ECMP, and thus you can start by copying all the code from your previous exercise. You will use the exact same headers,
parser, tables, and cli commands (so you do not need to modify this part either).
To solve this exercise you will have to use two `registers`, one for `flowlet_ids` (to extend previous hash fields) and one to keep the last timestamp for
every flow. You will have to slightly change the ingress logic, define a new action to read/write the flowlet registers. And modify
the hash function used in ECMP, adding a new field (the `flowlet_id`) which will vary over time.
You will have to fill the gaps in several files: `p4src/flowlet_switching.p4`, `p4src/include/headers.p4`
and `p4src/include/parsers.p4`.
To successfully complete the exercise you have to do the following:
1. Like in the previous exercise, header definitions are already provided.
2. Define the parser that is able to parse packets up to `tcp`. Note that for simplicity we do not consider `udp` packets
in this exercise. This time you must define the parser in: `p4src/include/parsers.p4`.
3. Define the deparser. Just emit all the headers.
4. Copy the tables and actions from the previous exercise. You will have to slightly modify them.
5. Define two registers `flowlet_to_id` and `flowlet_time_stamp` (for register sizing use the constant defined at the
beginning of `flowlet_switching.p4` file: REGISTER_SIZE, TIMESTAMP_WIDTH, ID_WIDTH). We will use this two registers to keep two things:
1. In `flowlet_to_id` register we keep the id (a random generated number) of each flowlet, this id is now added to the
hash function that devices the output port. As long as this id does not change, packets for that flow will stay in the same path.
2. In `flowlet_time_stamp` register we keep the last timestamp for the last observed packet belonging to a flow.
**Note:** for more information about registers look at the [v1model.p4 architecture file](https://github.com/p4lang/p4c/blob/main/p4include/v1model.p4#L289) or check the lecture slides.
6. Define an action to read the flowlet's register values (`read_flowlet_registers`). In this task, you will need to hash the 5-tuple of each packet to determine the index for reading the flowlet registers. To store the index, define a new metadata field with a width of 14 bits. Using the index you got from the hash function read flowlet id and last timestamp and save them in a metadata field (you
also have to define them). Finally, update the timestamp register using `standard_metadata.ingress_global_timestamp`.
7. Define another action to update the flowlet id (`update_flowlet_id`). We will use this action to update flowlet ids when needed.
In this action you just have to generate a random number, and then save it in the flowlet to id register (using the
id you already computed previously).
8. Modify the `hash` function you defined in the ECMP exercise (`ecmp_group`), now instead of just hashing the 5-tuple, you have to
add the metadata field where you store the `flowlet_id` you read from the register (or you just updated).
9. Define the ingress control logic (keep the logic from the ecmp example and add):
Before applying the `ipv4_lpm` table:
1. Read the flowlet registers (calling the action)
2. Compute the time difference between now and the last packet observed for the current flow.
3. Check if the time difference is bigger than `FLOWLET_TIMEOUT` (define at the beginning of the file with a default
value of 200ms).
4. Update the flowlet id if the difference is bigger. Updating the flowlet id will make the hash function output a new value.
5. Apply `ipv4_lpm` and `ecmp_group` is the same way you did in `ecmp`.
10. Copy the `sX-commands.txt` from the previous exercise.
## Testing your solution
Once you have the `flowlet_switching.p4` program finished you can test its behaviour:
1. Start the topology (this will also compile and load the program).
```bash
sudo p4run
```
or
```bash
sudo python network.py
```
2. Check that you can ping:
```bash
mininet> pingall
```
3. Monitor the 4 links from `s1` that will be used during `ecmp` (from `s1-eth2` to `s1-eth5`). Doing this you will be able to check which path is each flow taking.
```bash
sudo tcpdump -enn -i s1-ethX
```
4. Ping between two hosts:
If you run a normal ping from the mininet cli, or using the terminal, by default it will send a ping packet every 1 second. In this
case every ping should belong to a different flowlet, and thus it should be crossing different paths all the time.
5. Do iperf between two hosts:
If you do iperf between `h1` and `h2` you should see all the packets cross the same interfaces almost
all the time (unless you set the gap interval very small).
6. Get a terminal in `h1`. Use the `send.py` script.
```bash
python send.py 10.0.6.2 1000 <sleep_time_between_packets>
```
This will send `tcp syn` packets with the same 5-tuple. You can play with the sleep time (third parameter). If you set it bigger than your gap, packets should change
paths, if you set it smaller (set it quite smaller since the software model is not very precise) you will see all the packets cross the same interfaces.
#### Some notes on debugging and troubleshooting
We have added a [small guideline](https://github.com/nsg-ethz/p4-learning/wiki/Debugging-and-Troubleshooting) in the documentation section. Use it as a reference when things do not work as
expected.
2-Programmability/02-Load_Balancing/02-Flowlet_Switching/images/multi_hop_topo.png

16.6 KiB

from p4utils.mininetlib.network_API import NetworkAPI
net = NetworkAPI()
# Network general options
net.setLogLevel('info')
# Network definition
net.addP4Switch('s1', cli_input='sw-commands/s1-commands.txt')
net.addP4Switch('s2', cli_input='sw-commands/s2-commands.txt')
net.addP4Switch('s3', cli_input='sw-commands/s3-commands.txt')
net.addP4Switch('s4', cli_input='sw-commands/s4-commands.txt')
net.addP4Switch('s5', cli_input='sw-commands/s5-commands.txt')
net.addP4Switch('s6', cli_input='sw-commands/s6-commands.txt')
net.setP4SourceAll('p4src/flowlet_switching.p4')
net.addHost('h1')
net.addHost('h2')
net.addLink("h1", "s1")
net.addLink("h2", "s6")
net.addLink("s1", "s2")
net.addLink("s1", "s3")
net.addLink("s1", "s4")
net.addLink("s1", "s5")
net.addLink("s2", "s6")
net.addLink("s3", "s6")
net.addLink("s4", "s6")
net.addLink("s5", "s6")
# Assignment strategy
net.mixed()
# Nodes general options
net.enablePcapDumpAll()
net.enableLogAll()
net.enableCli()
net.startNetwork()
\ No newline at end of file
{
"p4_src": "p4src/flowlet_switching.p4",
"cli": true,
"pcap_dump": true,
"enable_log": true,
"topology": {
"assignment_strategy": "mixed",
"links": [["h1", "s1"], ["h2", "s6"], ["s1", "s2"], ["s1", "s3"], ["s1", "s4"], ["s1", "s5"], ["s2", "s6"], ["s3", "s6"], ["s4", "s6"], ["s5", "s6"]],
"hosts": {
"h1": {
},
"h2": {
}
},
"switches": {
"s1": {
"cli_input": "sw-commands/s1-commands.txt"
},
"s2": {
"cli_input": "sw-commands/s2-commands.txt"
},
"s3": {
"cli_input": "sw-commands/s3-commands.txt"
},
"s4": {
"cli_input": "sw-commands/s4-commands.txt"
},
"s5": {
"cli_input": "sw-commands/s5-commands.txt"
},
"s6": {
"cli_input": "sw-commands/s6-commands.txt"
}
}
}
}
/* -*- P4_16 -*- */
#include <core.p4>
#include <v1model.p4>
//My includes
#include "include/headers.p4"
#include "include/parsers.p4"
#define REGISTER_SIZE 8192
#define TIMESTAMP_WIDTH 48
#define ID_WIDTH 16
#define FLOWLET_TIMEOUT 48w200000 //200ms
/*************************************************************************
************ 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 5: define the id and timestamp registers
action drop() {
mark_to_drop(standard_metadata);
}
action read_flowlet_registers(){
//TODO 6: define the action to read the registers using the flowlet index you get from hashing the 5-tuple.
}
action update_flowlet_id(){
//TODO 7: define the action to update the flowlet id (if needed).
}
//TODO 4: copy the tables and actions from ECMP
//TODO 8: add the flowlet id to the hash function in the ecmp_group action
apply {
//TODO 9: write the ingress logic as described in the exercise description
}
}
/*************************************************************************
**************** 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 {
update_checksum(
hdr.ipv4.isValid(),
{ hdr.ipv4.version,
hdr.ipv4.ihl,
hdr.ipv4.dscp,
hdr.ipv4.ecn,
hdr.ipv4.totalLen,
hdr.ipv4.identification,
hdr.ipv4.flags,
hdr.ipv4.fragOffset,
hdr.ipv4.ttl,
hdr.ipv4.protocol,
hdr.ipv4.srcAddr,
hdr.ipv4.dstAddr },
hdr.ipv4.hdrChecksum,
HashAlgorithm.csum16);
}
}
/*************************************************************************
*********************** S W I T C H *******************************
*************************************************************************/
//switch architecture
V1Switch(
MyParser(),
MyVerifyChecksum(),
MyIngress(),
MyEgress(),
MyComputeChecksum(),
MyDeparser()
) main;
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment