Removed Hula
This commit is contained in:
@@ -1,300 +0,0 @@
|
||||
|
||||
# Implementing HULA
|
||||
|
||||
## Introduction
|
||||
|
||||
The objective of this exercise is to implement a simplified version of
|
||||
[HULA](http://web.mit.edu/anirudh/www/hula-sosr16.pdf).
|
||||
In contrast to ECMP, which selects the next hop randomly, HULA load balances
|
||||
the flows over multiple paths to a destination ToR based on queue occupancy
|
||||
of switches in each path. Thus, it can use the whole bisection bandwidth.
|
||||
To keep the example simple, we implement it on top of source routing exercise.
|
||||
|
||||
Here is how HULA works:
|
||||
- Each ToR switch generates a HULA packet to each other ToR switch
|
||||
to probe the condition of every path between the source and the destination ToR.
|
||||
Each HULA packet is forwarded to the destination ToR (forward path), collects the maximum
|
||||
queue length it observes while being forwarded, and finally delivers that information
|
||||
to the destination ToR. Based on the congestion information collected via probes,
|
||||
each destination ToR then can maintain the current best path (i.e., least congested path)
|
||||
from each source ToR. To share the best path information with the source ToRs so that
|
||||
the sources can use that information for new flows, the destination ToRs notify
|
||||
source ToRs of the current best path by returning the HULA probe back to the source
|
||||
ToR (reverse path) only if the current best path changes. The probe packets include
|
||||
a HULA header and a list of ports for source routing. We describe the elements of HULA header later.
|
||||
- In the forward path:
|
||||
- Each hop updates the queue length field in the hula header if the local queue depth observed by
|
||||
the HULA packet is larger than maximum queue depth recorded in the probe packet. Thus when
|
||||
the packet reaches the destination ToR, queue length field will be the maximum observed queue length
|
||||
on the forward path.
|
||||
- At destination ToR,
|
||||
1. find the queue length of current best path from the source ToR.
|
||||
2. if the new path is better, update the queue length and best path and return
|
||||
the HULA probe to the source path. This is done by setting the direction field
|
||||
in the HULA header and returning the packet to the ingress port.
|
||||
3. if the probe came through the current best path, the destination ToR just updates
|
||||
the existing value. This is needed to know if the best path got worse and hence allow
|
||||
other paths to replace it later. It is inefficient to save the whole path ID
|
||||
(i.e., sequence of switch IDs) and compare it in the data plane;
|
||||
note, P4 doesn't have a loop construct. Instead, we keep a 32 bit digest of a
|
||||
path in the HULA header. Each destination ToR only saves and compares the
|
||||
digest of the best path along with its queue length.
|
||||
The `hula.digest` field is set by source ToR upon creating the HULA packet
|
||||
and does not change along the path.
|
||||
- In the reverse path:
|
||||
- Each hop will update the "routing next hop" to the destination ToR based on the port
|
||||
it received the HULA packet on (as it was the best path). Then it forwards the packet
|
||||
to the next hop in reverse path based on source routing.
|
||||
- Source ToR also drops the packet.
|
||||
- Now for each data packet,
|
||||
- Each hop hashes the flow header fields and looks into a "flow table".
|
||||
- If it doesn't find the next hop for the flow, looks into "routing next hop" to
|
||||
find the next hop for destination ToR. We assume each ToR serves a /24 IP address.
|
||||
The switch also updates the "flow table". "flow table" prevents the path of a flow to change
|
||||
in order to avoid packet re-ordering and path oscilation during updating next hops.
|
||||
- Otherwise, each hop just uses the next hop.
|
||||
|
||||
Your switch will have multiple tables, which the control plane will
|
||||
populate with static rules. We have already defined
|
||||
the control plane rules, so you only need to implement the data plane
|
||||
logic of your P4 program.
|
||||
|
||||
> **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,
|
||||
`hula.p4`, which initially drops all packets. Your job (in the next
|
||||
step) will be to extend it to properly update HULA packets and forward data packets.
|
||||
|
||||
Before that, let's compile the incomplete `hula.p4` and bring up a
|
||||
switch in Mininet to test its behavior.
|
||||
|
||||
1. In your shell, run:
|
||||
```bash
|
||||
./run.sh
|
||||
```
|
||||
This will:
|
||||
* compile `hula.p4`, and
|
||||
* start a Mininet instance with three ToR switches (`s1`, `s2`, `s3`)
|
||||
and two spine switches ( `s11`, `s22`).
|
||||
* The hosts (`h1`, `h2`, `h3`) are assigned IPs of `10.0.1.1`, `10.0.2.2`, and `10.0.3.3`.
|
||||
|
||||
2. You should now see a Mininet command prompt. Just ping `h2` from `h1`:
|
||||
```bash
|
||||
mininet> h1 ping h2
|
||||
```
|
||||
It doesn't work as no path is set.
|
||||
|
||||
3. Type `exit` to close the Mininet command line.
|
||||
|
||||
The message was not received because each switch is programmed with
|
||||
`hula.p4`, which drops all data packets. Your job is to extend
|
||||
this file.
|
||||
|
||||
### 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
|
||||
`sX-commands.txt` files, where `X` corresponds to the switch number.
|
||||
|
||||
**Important:** A P4 program also defines the interface between the switch
|
||||
pipeline and control plane. The `sX-commands.txt` files contain lists of
|
||||
commands for the BMv2 switch API. These commands refer to specific tables,
|
||||
keys, and actions by name, and any changes in the P4 program that add or rename
|
||||
tables, keys, or actions will need to be reflected in these command files.
|
||||
|
||||
## Step 2: Implement Hula
|
||||
|
||||
The `hula.p4` file contains a skeleton P4 program with key pieces of
|
||||
logic replaced by `TODO` comments. These should guide your
|
||||
implementation---replace each `TODO` with logic implementing the missing piece.
|
||||
|
||||
A complete `hula.p4` will contain the following components:
|
||||
|
||||
1. Header type definitions for Ethernet (`ethernet_t`), Hula (`hula_t`),
|
||||
Source Routing (`srcRoute_t`), IPv4 (`ipv4_t`), UDP(`udp_t`).
|
||||
2. Parsers for the above headers.
|
||||
3. Registers:
|
||||
- `srcindex_qdepth_reg`: At destination ToR saves queue length of the best path
|
||||
from each Source ToR
|
||||
- `srcindex_digest_reg`: At destination ToR saves the digest of the best path
|
||||
from each Source ToR
|
||||
- `dstindex_nhop_reg`: At each hop, saves the next hop to reach each destination ToR
|
||||
- `flow_port_reg`: At each hop saves the next hop for each flow
|
||||
4. `hula_fwd table`: looks at the destination IP of a HULA packet. If it is the destination ToR,
|
||||
it runs `hula_dst` action to set `meta.index` field based on source IP (source ToR).
|
||||
The index is used later to find queue depth and digest of current best path from that source ToR.
|
||||
Otherwise, this table just runs `srcRoute_nhop` to perform source routing.
|
||||
5. `hula_bwd` table: at revere path, updates next hop to the destination ToR using `hula_set_nhop`
|
||||
action. The action updates `dstindex_nhop_reg` register.
|
||||
6. `hula_src` table checks the source IP address of a HULA packet in reverse path.
|
||||
if this switch is the source, this is the end of reverse path, thus drop the packet.
|
||||
Otherwise use `srcRoute_nhop` action to continue source routing in the reverse path.
|
||||
7. `hula_nhop` table for data packets, reads destination IP/24 to get an index.
|
||||
It uses the index to read `dstindex_nhop_reg` register and get best next hop to the
|
||||
destination ToR.
|
||||
8. dmac table just updates ethernet destination address based on next hop.
|
||||
9. An apply block that has the following logic:
|
||||
* If the packet has a HULA header
|
||||
* In forward path (`hdr.hula.dir==0`):
|
||||
* Apply `hula_fwd` table to check if it is the destination ToR or not
|
||||
* If this switch is the destination ToR (`hula_dst` action ran and
|
||||
set the `meta.index` based on the source IP address):
|
||||
* read `srcindex_qdepth_reg` for the queue length of
|
||||
the current best path from the source ToR
|
||||
* If the new queue length is better, update the entry in `srcindex_qdepth_reg` and
|
||||
save the path digest in `srcindex_digest_reg`. Then return the HULA packet to the source ToR
|
||||
by sending to its ingress port and setting `hula.dir=1` (reverse path)
|
||||
* else, if this HULA packet came through current best path (`hula.digest` is equal to
|
||||
the value in `srcindex_digest_reg`), update its queue length in `srcindex_qdepth_reg`.
|
||||
In this case we don't need to send the HULA packet back, thus drop the packet.
|
||||
* in reverse path (`hdr.hula.dir==1`):
|
||||
* apply `hula_bwd` to update the HULA next hop to the destination ToR
|
||||
* apply `hula_src` table to drop the packet if it is the source ToR of the HULA packet
|
||||
* If it is a data packet
|
||||
* compute the hash of flow
|
||||
* **TODO** read nexthop port from `flow_port_reg` into a temporary variable, say `port`.
|
||||
* **TODO** If no entry found (`port==0`), read next hop by applying `hula_nhop` table.
|
||||
Then save the value into `flow_port_reg` for later packets.
|
||||
* **TODO** if it is found, save `port` into `standard_metadata.egress_spec` to finish routing.
|
||||
* apply `dmac` table to update `ethernet.dstAddr`. This is necessary for the links that send packets
|
||||
to hosts. Otherwise their NIC will drop packets.
|
||||
* udpate TTL
|
||||
5. **TODO:** An egress control that for HULA packets that are in forward path (`hdr.hula.dir==0`)
|
||||
compares `standard_metadata.deq_qdepth` to `hdr.hula.qdepth`
|
||||
in order to save the maximum in `hdr.hula.qdepth`
|
||||
7. A deparser that selects the order in which fields inserted into the outgoing
|
||||
packet.
|
||||
8. A `package` instantiation supplied with the parser, control, checksum verification and
|
||||
recomputation and deparser.
|
||||
|
||||
## Step 3: Run your solution
|
||||
|
||||
1. Run Mininet same as Step 1
|
||||
|
||||
2. Open a separate terminal, go to `exercises/hula`, and run `sudo ./generatehula.py`.
|
||||
This python script makes each ToR switch generate one HULA probe for each other ToR and
|
||||
through each separate forward path. For example, `s1` first probes `s2` via `s11` and then via `s22`.
|
||||
Then `s1` probes `s3` again first via `s11` and then via `s22`. `s2` does the same thing to probe
|
||||
paths to `s1` and `s3`, and so does `s3`.
|
||||
|
||||
3. Now run `h1 ping h2`. The ping should work if you have completed the ingress control block in `hula.p4`.
|
||||
Note at this point, every ToR considers all paths are equal because there isn't any congestion in the network.
|
||||
|
||||
Now we are going to test a more complex scenario.
|
||||
|
||||
We first create two iperf sessions: one from `h1` to `h3`, and the other from `h2` to `h3`.
|
||||
Since both `s1` and `s2` currently think their best paths to `s3` should go through `s11`,
|
||||
the two connections will use the same spine switch (`s11`). Note we throttled the
|
||||
links from the spine switches to `s3` down to 1Mbps. Hence, each of the two connections
|
||||
achieves only ~512Kbps. Let's confirm this by taking the following steps.
|
||||
|
||||
1. open a terminal window on `h1`, `h2` and `h3`:
|
||||
```bash
|
||||
xterm h1 h2 h3
|
||||
```
|
||||
2. start iperf server at `h3`
|
||||
```bash
|
||||
iperf -s -u -i 1
|
||||
```
|
||||
3. run iperf client at `h1`
|
||||
```bash
|
||||
iperf -c 10.0.3.3 -t 30 -u -b 2m
|
||||
```
|
||||
4. run iperf client in `h2`. try to do step 3 and 4 simultaneously.
|
||||
```bash
|
||||
iperf -c 10.0.3.3 -t 30 -u -b 2m
|
||||
```
|
||||
While the connections are running, watch the iperf server's output at `h3`.
|
||||
Although there are two completely non-overlapping paths for `h1` and `h2` to reach `h3`,
|
||||
both `h1` and `h2` end up using the same spine, and hence the aggregate
|
||||
throughput of the two connections is capped to 1Mbps.
|
||||
You can confirm this by watching the performance of each connection.
|
||||
|
||||
|
||||
Our goal is allowing the two connections to use two different spine switches and hence achieve
|
||||
1Mbps each. We can do this by first causing congestion on one of the spines. More specifically
|
||||
we'll create congestion at the queue in `s11` facing the link `s11-to-s3` by running a
|
||||
long-running connection (an elephant flow) from `s1` to `s3` through `s11`.
|
||||
Once the queue builds up due to the elephant, then we'll let `s2` generate HULA probes
|
||||
several times so that it can learn to avoid forwarding new flows destined to `s3` through `s11`.
|
||||
The following steps achieve this.
|
||||
|
||||
1. open a terminal window on `h1`, `h2` and `h3`. (By the way, if you have already closed mininet,
|
||||
you need to re-run the mininet test and run `generatehula.py` first, to setup initial routes)
|
||||
```bash
|
||||
xterm h1 h2 h3
|
||||
```
|
||||
2. start iperf server at `h3`
|
||||
```bash
|
||||
iperf -s -u -i 1
|
||||
```
|
||||
3. create a long-running full-demand connection from `h1` to `h3` through `s11`.
|
||||
you can do this by running the following at `h1`
|
||||
```bash
|
||||
iperf -c 10.0.3.3 -t 3000 -u -b 2m
|
||||
```
|
||||
4. outside mininet (in a separate terminal), go to `exercises/hula`, and run the following several (5 to 10) times
|
||||
```bash
|
||||
sudo ./generatehula.py
|
||||
```
|
||||
This should let `s2` know that the path through `s11` to `s3` is congested and
|
||||
the best path is now through the uncongested spine, `s22`.
|
||||
5. Now, run iperf client at `h2`
|
||||
```bash
|
||||
iperf -c 10.0.3.3 -t 30 -u -b 2m
|
||||
```
|
||||
You will be able to confirm both iperf sessions achieve 1Mbps because they go through two different spines.
|
||||
|
||||
### Food for thought
|
||||
* how can we implement flowlet routing (as opposed to flow routing) say based on the timestamp of packets
|
||||
* in the ingress control logic, the destination ToR always sends a HULA packet
|
||||
back on the reverse path if the queue length is better. But this is not necessary
|
||||
if it came from the best path. Can you improve the code?
|
||||
* the hula packets on the congested path may get dropped or extremely delayed,
|
||||
thus the destination ToR would not be aware of the worsened condition of the current best path.
|
||||
A solution could be that the destination ToR uses a timeout mechanism to ignore the current best path
|
||||
if it doesn't receive a hula packet through it for a long time.
|
||||
How can you implement this inside dataplane?
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
There are several ways that problems might manifest:
|
||||
|
||||
1. `hula.p4` fails to compile. In this case, `run.sh` will report the
|
||||
error emitted from the compiler and stop.
|
||||
|
||||
2. `hula.p4` compiles but does not support the control plane rules in
|
||||
the `sX-commands.txt` files that `run.sh` tries to install using the BMv2 CLI.
|
||||
In this case, `run.sh` will report these errors to `stderr`. Use these error
|
||||
messages to fix your `hula.p4` implementation.
|
||||
|
||||
3. `hula.p4` compiles, and the control plane rules are installed, 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.
|
||||
The `build/<switch-name>-<interface-name>.pcap` also contains the pcap of packets on each
|
||||
interface. Use `tcpdump -r <filename> -xxx` to print the hexdump of the packets.
|
||||
|
||||
#### Cleaning up Mininet
|
||||
|
||||
In the latter two cases above, `run.sh` may leave a Mininet instance running in
|
||||
the background. Use the following command to clean up these instances:
|
||||
|
||||
```bash
|
||||
mn -c
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Congratulations, your implementation works!
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import sys
|
||||
import socket
|
||||
import random
|
||||
import struct
|
||||
|
||||
from scapy.all import sendp, send, get_if_list, get_if_hwaddr, bind_layers
|
||||
from scapy.all import Packet
|
||||
from scapy.all import Ether, IP, UDP
|
||||
from scapy.fields import *
|
||||
from time import sleep
|
||||
import crcmod
|
||||
|
||||
class Hula(Packet):
|
||||
fields_desc = [ BitField("dir", 0, 1),
|
||||
BitField("qdepth", 0, 15),
|
||||
XIntField("digest", None)]
|
||||
def post_build(self, p, pay):
|
||||
p += pay
|
||||
if self.digest is None:
|
||||
crc32 = crcmod.Crc(0x104c11db7, initCrc=0, xorOut=0xFFFFFFFF)
|
||||
crc32.update(str(p));
|
||||
c = bytes(bytearray.fromhex("%08x" % crc32.crcValue))
|
||||
p = p[:2]+ c +p[6:]
|
||||
#ck = checksum(p)
|
||||
#p = p[:2]+"\x00\x00"+chr(ck>>8)+chr(ck&0xff)+p[6:]
|
||||
return p
|
||||
|
||||
class SourceRoute(Packet):
|
||||
fields_desc = [ BitField("bos", 0, 1),
|
||||
BitField("port", 0, 15)]
|
||||
|
||||
bind_layers(Ether, Hula, type=0x2345)
|
||||
bind_layers(Hula, SourceRoute)
|
||||
bind_layers(SourceRoute, SourceRoute, bos=0)
|
||||
bind_layers(SourceRoute, IP, bos=1)
|
||||
|
||||
def main():
|
||||
period = 0
|
||||
if len(sys.argv) > 1:
|
||||
period = int(sys.argv[1])
|
||||
|
||||
# src, dst , src routing , interface
|
||||
info = [
|
||||
("10.0.1.0", "10.0.2.0", (2, 2, 1, 1), "s1-eth1"),
|
||||
("10.0.1.0", "10.0.2.0", (3, 2, 1, 1), "s1-eth1"),
|
||||
("10.0.1.0", "10.0.3.0", (2, 3, 1, 1), "s1-eth1"),
|
||||
("10.0.1.0", "10.0.3.0", (3, 3, 1, 1), "s1-eth1"),
|
||||
("10.0.2.0", "10.0.1.0", (2, 1, 2, 1), "s2-eth1"),
|
||||
("10.0.2.0", "10.0.1.0", (3, 1, 2, 1), "s2-eth1"),
|
||||
("10.0.2.0", "10.0.3.0", (2, 3, 2, 1), "s2-eth1"),
|
||||
("10.0.2.0", "10.0.3.0", (3, 3, 2, 1), "s2-eth1"),
|
||||
("10.0.3.0", "10.0.1.0", (2, 1, 3, 1), "s3-eth1"),
|
||||
("10.0.3.0", "10.0.1.0", (3, 1, 3, 1), "s3-eth1"),
|
||||
("10.0.3.0", "10.0.2.0", (2, 2, 3, 1), "s3-eth1"),
|
||||
("10.0.3.0", "10.0.2.0", (3, 2, 3, 1), "s3-eth1")]
|
||||
|
||||
|
||||
try:
|
||||
while True:
|
||||
for e in info:
|
||||
ports = e[2]
|
||||
pkt = Ether(src=get_if_hwaddr(e[3]), dst='ff:ff:ff:ff:ff:ff')
|
||||
pkt = pkt / Hula(dir=0, qdepth=0)
|
||||
pkt = pkt / SourceRoute(bos=0, port=ports[0])
|
||||
pkt = pkt / SourceRoute(bos=0, port=ports[1])
|
||||
pkt = pkt / SourceRoute(bos=0, port=ports[2])
|
||||
pkt = pkt / SourceRoute(bos=1, port=ports[3])
|
||||
pkt = pkt / IP(dst=e[1], src=e[0]) / UDP(dport=4321, sport=1234)
|
||||
#pkt.show2()
|
||||
sendp(pkt, iface=e[3], verbose=False)
|
||||
if period == 0:
|
||||
break;
|
||||
else:
|
||||
sleep(period)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,433 +0,0 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<16> TYPE_HULA = 0x2345;
|
||||
|
||||
#define MAX_HOPS 9
|
||||
#define TOR_NUM 32
|
||||
#define TOR_NUM_1 33
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
typedef bit<15> qdepth_t;
|
||||
typedef bit<32> digest_t;
|
||||
|
||||
header ethernet_t {
|
||||
macAddr_t dstAddr;
|
||||
macAddr_t srcAddr;
|
||||
bit<16> etherType;
|
||||
}
|
||||
|
||||
header srcRoute_t {
|
||||
bit<1> bos;
|
||||
bit<15> port;
|
||||
}
|
||||
|
||||
header hula_t {
|
||||
/* 0 is forward path, 1 is the backward path */
|
||||
bit<1> dir;
|
||||
/* max qdepth seen so far in the forward path */
|
||||
qdepth_t qdepth;
|
||||
/* digest of the source routing list to uniquely identify each path */
|
||||
digest_t digest;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
header udp_t {
|
||||
bit<16> srcPort;
|
||||
bit<16> dstPort;
|
||||
bit<16> length_;
|
||||
bit<16> checksum;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
/* At destination ToR, this is the index of register
|
||||
that saves qdepth for the best path from each source ToR */
|
||||
bit<32> index;
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
srcRoute_t[MAX_HOPS] srcRoutes;
|
||||
ipv4_t ipv4;
|
||||
udp_t udp;
|
||||
hula_t hula;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** 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_HULA : parse_hula;
|
||||
TYPE_IPV4 : parse_ipv4;
|
||||
default : accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_hula {
|
||||
packet.extract(hdr.hula);
|
||||
transition parse_srcRouting;
|
||||
}
|
||||
|
||||
state parse_srcRouting {
|
||||
packet.extract(hdr.srcRoutes.next);
|
||||
transition select(hdr.srcRoutes.last.bos) {
|
||||
1 : parse_ipv4;
|
||||
default : parse_srcRouting;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
transition select(hdr.ipv4.protocol) {
|
||||
8w17: parse_udp;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_udp {
|
||||
packet.extract(hdr.udp);
|
||||
transition 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) {
|
||||
|
||||
/* At destination ToR, saves the queue depth of the best path from
|
||||
* each source ToR
|
||||
*/
|
||||
register<qdepth_t>(TOR_NUM) srcindex_qdepth_reg;
|
||||
|
||||
/* At destination ToR, saves the digest of the best path from
|
||||
* each source ToR
|
||||
*/
|
||||
register<digest_t>(TOR_NUM) srcindex_digest_reg;
|
||||
|
||||
/* At each hop, saves the next hop to reach each destination ToR */
|
||||
register<bit<16>>(TOR_NUM) dstindex_nhop_reg;
|
||||
|
||||
/* At each hop saves the next hop for each flow */
|
||||
register<bit<16>>(65536) flow_port_reg;
|
||||
|
||||
/* This action will drop packets */
|
||||
action drop() {
|
||||
mark_to_drop();
|
||||
}
|
||||
|
||||
action nop() {
|
||||
}
|
||||
|
||||
action update_ttl(){
|
||||
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
|
||||
}
|
||||
|
||||
action set_dmac(macAddr_t dstAddr){
|
||||
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
|
||||
hdr.ethernet.dstAddr = dstAddr;
|
||||
}
|
||||
|
||||
/* This action just applies source routing */
|
||||
action srcRoute_nhop() {
|
||||
standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port;
|
||||
hdr.srcRoutes.pop_front(1);
|
||||
}
|
||||
|
||||
/* Runs if it is the destination ToR.
|
||||
* Control plane Gives the index of register for best path from a source ToR
|
||||
*/
|
||||
action hula_dst(bit<32> index) {
|
||||
meta.index = index;
|
||||
}
|
||||
|
||||
/* On reverse path, update nexthop to a destination ToR to the ingress port
|
||||
* where we receive hula packet
|
||||
*/
|
||||
action hula_set_nhop(bit<32> index) {
|
||||
dstindex_nhop_reg.write(index, (bit<16>)standard_metadata.ingress_port);
|
||||
}
|
||||
|
||||
/* Read next hop that is saved in hula_set_nhop action for data packets */
|
||||
action hula_get_nhop(bit<32> index){
|
||||
bit<16> tmp;
|
||||
dstindex_nhop_reg.read(tmp, index);
|
||||
standard_metadata.egress_spec = (bit<9>)tmp;
|
||||
}
|
||||
|
||||
/* Record best path at destination ToR */
|
||||
action change_best_path_at_dst(){
|
||||
srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth);
|
||||
srcindex_digest_reg.write(meta.index, hdr.hula.digest);
|
||||
}
|
||||
|
||||
/* At destination ToR, return packet to source by
|
||||
* - changing its hula direction
|
||||
* - send it to the port it came from
|
||||
*/
|
||||
action return_hula_to_src(){
|
||||
hdr.hula.dir = 1;
|
||||
standard_metadata.egress_spec = standard_metadata.ingress_port;
|
||||
}
|
||||
|
||||
/* On forward path:
|
||||
* - if destination ToR: run hula_dst to set the index based on srcAddr
|
||||
* - otherwise run srcRoute_nhop to perform source routing
|
||||
*/
|
||||
table hula_fwd {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: exact;
|
||||
hdr.ipv4.srcAddr: exact;
|
||||
}
|
||||
actions = {
|
||||
hula_dst;
|
||||
srcRoute_nhop;
|
||||
}
|
||||
default_action = srcRoute_nhop;
|
||||
size = TOR_NUM_1; // TOR_NUM + 1
|
||||
}
|
||||
|
||||
/* At each hop in reverse path
|
||||
* update next hop to destination ToR in registers.
|
||||
* index is set based on dstAddr
|
||||
*/
|
||||
table hula_bwd {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: lpm;
|
||||
}
|
||||
actions = {
|
||||
hula_set_nhop;
|
||||
}
|
||||
size = TOR_NUM;
|
||||
}
|
||||
|
||||
/* On reverse path:
|
||||
* - if source ToR (srcAddr = this switch) drop hula packet
|
||||
* - otherwise, just forward in the reverse path based on source routing
|
||||
*/
|
||||
table hula_src {
|
||||
key = {
|
||||
hdr.ipv4.srcAddr: exact;
|
||||
}
|
||||
actions = {
|
||||
drop;
|
||||
srcRoute_nhop;
|
||||
}
|
||||
default_action = srcRoute_nhop;
|
||||
size = 2;
|
||||
}
|
||||
|
||||
/* Get nexthop based on dstAddr using registers */
|
||||
table hula_nhop {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: lpm;
|
||||
}
|
||||
actions = {
|
||||
hula_get_nhop;
|
||||
drop;
|
||||
}
|
||||
default_action = drop;
|
||||
size = TOR_NUM;
|
||||
}
|
||||
|
||||
/* Set right dmac for packets going to hosts */
|
||||
table dmac {
|
||||
key = {
|
||||
standard_metadata.egress_spec : exact;
|
||||
}
|
||||
actions = {
|
||||
set_dmac;
|
||||
nop;
|
||||
}
|
||||
default_action = nop;
|
||||
size = 16;
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.hula.isValid()){
|
||||
if (hdr.hula.dir == 0){
|
||||
switch(hula_fwd.apply().action_run){
|
||||
|
||||
/* if hula_dst action ran, this is the destination ToR */
|
||||
hula_dst: {
|
||||
|
||||
/* if it is the destination ToR compare qdepth */
|
||||
qdepth_t old_qdepth;
|
||||
srcindex_qdepth_reg.read(old_qdepth, meta.index);
|
||||
|
||||
if (old_qdepth > hdr.hula.qdepth){
|
||||
change_best_path_at_dst();
|
||||
|
||||
/* only return hula packets that update best path */
|
||||
return_hula_to_src();
|
||||
}else{
|
||||
|
||||
/* update the best path even if it has gone worse
|
||||
* so that other paths can replace it later
|
||||
*/
|
||||
digest_t old_digest;
|
||||
srcindex_digest_reg.read(old_digest, meta.index);
|
||||
if (old_digest == hdr.hula.digest){
|
||||
srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth);
|
||||
}
|
||||
|
||||
drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
/* update routing table in reverse path */
|
||||
hula_bwd.apply();
|
||||
|
||||
/* drop if source ToR */
|
||||
hula_src.apply();
|
||||
}
|
||||
|
||||
}else if (hdr.ipv4.isValid()){
|
||||
bit<16> flow_hash;
|
||||
hash(
|
||||
flow_hash,
|
||||
HashAlgorithm.crc16,
|
||||
16w0,
|
||||
{ hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.udp.srcPort},
|
||||
32w65536);
|
||||
|
||||
/* TODO:
|
||||
* - Remove drop();
|
||||
* - Read nexthop port from flow_port_reg for the flow
|
||||
* using flow_hash into a temporary variable
|
||||
* - if port==0,
|
||||
* - apply hula_nhop table to get next hop for destination ToR
|
||||
* - write the next hop into the flow_port_reg register indexed by flow_hash
|
||||
* - else: write port into standard_metadata.egress_spec
|
||||
*/
|
||||
drop();
|
||||
|
||||
/* set the right dmac so that ping and iperf work */
|
||||
dmac.apply();
|
||||
}else {
|
||||
drop();
|
||||
}
|
||||
|
||||
if (hdr.ipv4.isValid()){
|
||||
update_ttl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** 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 {
|
||||
/* TODO:
|
||||
* if hula header is valid and this is forward path (hdr.hula.dir==0)
|
||||
* check whether the qdepth in hula is smaller than
|
||||
* (qdepth_t)standard_metadata.deq_qdepth
|
||||
* if so, then update hdr.hula.qdepth
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* 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.diffserv,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
*********************** D E P A R S E R *******************************
|
||||
*************************************************************************/
|
||||
|
||||
control MyDeparser(packet_out packet, in headers hdr) {
|
||||
apply {
|
||||
packet.emit(hdr.ethernet);
|
||||
packet.emit(hdr.hula);
|
||||
packet.emit(hdr.srcRoutes);
|
||||
packet.emit(hdr.ipv4);
|
||||
packet.emit(hdr.udp);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"program": "hula.p4",
|
||||
"language": "p4-16",
|
||||
"targets": {
|
||||
"multiswitch": {
|
||||
"auto-control-plane": true,
|
||||
"cli": true,
|
||||
"pcap_dump": true,
|
||||
"bmv2_log": true,
|
||||
"links": [["h1", "s1"], ["h2", "s2"], ["h3", "s3"], ["s1", "s11"], ["s1", "s22"], ["s2", "s11"], ["s2", "s22"], ["s11", "s3", "0", 1], ["s22", "s3", "0", 1]],
|
||||
"hosts": {
|
||||
"h1": {
|
||||
},
|
||||
"h2": {
|
||||
},
|
||||
"h3": {
|
||||
}
|
||||
},
|
||||
"switches": {
|
||||
"s1": {
|
||||
"entries": "s1-commands.txt"
|
||||
},
|
||||
"s2": {
|
||||
"entries": "s2-commands.txt"
|
||||
},
|
||||
"s3": {
|
||||
"entries": "s3-commands.txt"
|
||||
},
|
||||
"s11": {
|
||||
"entries": "s11-commands.txt"
|
||||
},
|
||||
"s22": {
|
||||
"entries": "s22-commands.txt"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
P4APPRUNNER=../../utils/p4apprunner.py
|
||||
mkdir -p build
|
||||
tar -czf build/p4app.tgz * --exclude='build'
|
||||
#cd build
|
||||
sudo python $P4APPRUNNER p4app.tgz --build-dir ./build
|
||||
@@ -1,16 +0,0 @@
|
||||
table_add hula_src drop 10.0.1.0 =>
|
||||
register_write dstindex_nhop_reg 0 1
|
||||
table_add hula_fwd hula_dst 10.0.1.0 10.0.1.0 => 0
|
||||
table_add hula_fwd hula_dst 10.0.1.0 10.0.2.0 => 1
|
||||
table_add hula_fwd hula_dst 10.0.1.0 10.0.3.0 => 2
|
||||
table_add dmac set_dmac 1 => 00:00:00:00:01:01
|
||||
|
||||
register_write srcindex_qdepth_reg 0 256
|
||||
register_write srcindex_qdepth_reg 1 256
|
||||
register_write srcindex_qdepth_reg 2 256
|
||||
table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2
|
||||
table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2
|
||||
@@ -1,6 +0,0 @@
|
||||
table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2
|
||||
table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2
|
||||
@@ -1,16 +0,0 @@
|
||||
table_add hula_src drop 10.0.2.0 =>
|
||||
register_write dstindex_nhop_reg 1 1
|
||||
table_add hula_fwd hula_dst 10.0.2.0 10.0.1.0 => 0
|
||||
table_add hula_fwd hula_dst 10.0.2.0 10.0.2.0 => 1
|
||||
table_add hula_fwd hula_dst 10.0.2.0 10.0.3.0 => 2
|
||||
table_add dmac set_dmac 1 => 00:00:00:00:02:02
|
||||
|
||||
register_write srcindex_qdepth_reg 0 256
|
||||
register_write srcindex_qdepth_reg 1 256
|
||||
register_write srcindex_qdepth_reg 2 256
|
||||
table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2
|
||||
table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2
|
||||
@@ -1,6 +0,0 @@
|
||||
table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2
|
||||
table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2
|
||||
@@ -1,16 +0,0 @@
|
||||
table_add hula_src drop 10.0.3.0 =>
|
||||
register_write dstindex_nhop_reg 2 1
|
||||
table_add hula_fwd hula_dst 10.0.3.0 10.0.1.0 => 0
|
||||
table_add hula_fwd hula_dst 10.0.3.0 10.0.2.0 => 1
|
||||
table_add hula_fwd hula_dst 10.0.3.0 10.0.3.0 => 2
|
||||
table_add dmac set_dmac 1 => 00:00:00:00:03:03
|
||||
|
||||
register_write srcindex_qdepth_reg 0 256
|
||||
register_write srcindex_qdepth_reg 1 256
|
||||
register_write srcindex_qdepth_reg 2 256
|
||||
table_add hula_bwd hula_set_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_bwd hula_set_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_bwd hula_set_nhop 10.0.3.0/24 => 2
|
||||
table_add hula_nhop hula_get_nhop 10.0.1.0/24 => 0
|
||||
table_add hula_nhop hula_get_nhop 10.0.2.0/24 => 1
|
||||
table_add hula_nhop hula_get_nhop 10.0.3.0/24 => 2
|
||||
@@ -1,449 +0,0 @@
|
||||
/* -*- P4_16 -*- */
|
||||
#include <core.p4>
|
||||
#include <v1model.p4>
|
||||
|
||||
const bit<16> TYPE_IPV4 = 0x800;
|
||||
const bit<16> TYPE_HULA = 0x2345;
|
||||
|
||||
#define MAX_HOPS 9
|
||||
#define TOR_NUM 32
|
||||
#define TOR_NUM_1 33
|
||||
|
||||
/*************************************************************************
|
||||
*********************** H E A D E R S ***********************************
|
||||
*************************************************************************/
|
||||
|
||||
typedef bit<9> egressSpec_t;
|
||||
typedef bit<48> macAddr_t;
|
||||
typedef bit<32> ip4Addr_t;
|
||||
typedef bit<15> qdepth_t;
|
||||
typedef bit<32> digest_t;
|
||||
|
||||
header ethernet_t {
|
||||
macAddr_t dstAddr;
|
||||
macAddr_t srcAddr;
|
||||
bit<16> etherType;
|
||||
}
|
||||
|
||||
header srcRoute_t {
|
||||
bit<1> bos;
|
||||
bit<15> port;
|
||||
}
|
||||
|
||||
header hula_t {
|
||||
/* 0 is forward path, 1 is the backward path */
|
||||
bit<1> dir;
|
||||
/* max qdepth seen so far in the forward path */
|
||||
qdepth_t qdepth;
|
||||
/* digest of the source routing list to uniquely identify each path */
|
||||
digest_t digest;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
header udp_t {
|
||||
bit<16> srcPort;
|
||||
bit<16> dstPort;
|
||||
bit<16> length_;
|
||||
bit<16> checksum;
|
||||
}
|
||||
|
||||
struct metadata {
|
||||
/* At destination ToR, this is the index of register
|
||||
that saves qdepth for the best path from each source ToR */
|
||||
bit<32> index;
|
||||
}
|
||||
|
||||
struct headers {
|
||||
ethernet_t ethernet;
|
||||
srcRoute_t[MAX_HOPS] srcRoutes;
|
||||
ipv4_t ipv4;
|
||||
udp_t udp;
|
||||
hula_t hula;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** 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_HULA : parse_hula;
|
||||
TYPE_IPV4 : parse_ipv4;
|
||||
default : accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_hula {
|
||||
packet.extract(hdr.hula);
|
||||
transition parse_srcRouting;
|
||||
}
|
||||
|
||||
state parse_srcRouting {
|
||||
packet.extract(hdr.srcRoutes.next);
|
||||
transition select(hdr.srcRoutes.last.bos) {
|
||||
1 : parse_ipv4;
|
||||
default : parse_srcRouting;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_ipv4 {
|
||||
packet.extract(hdr.ipv4);
|
||||
transition select(hdr.ipv4.protocol) {
|
||||
8w17: parse_udp;
|
||||
default: accept;
|
||||
}
|
||||
}
|
||||
|
||||
state parse_udp {
|
||||
packet.extract(hdr.udp);
|
||||
transition 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) {
|
||||
|
||||
/*
|
||||
* At destination ToR, saves the queue depth of the best path from
|
||||
* each source ToR
|
||||
*/
|
||||
register<qdepth_t>(TOR_NUM) srcindex_qdepth_reg;
|
||||
|
||||
/*
|
||||
* At destination ToR, saves the digest of the best path from
|
||||
* each source ToR
|
||||
*/
|
||||
register<digest_t>(TOR_NUM) srcindex_digest_reg;
|
||||
|
||||
/* At each hop, saves the next hop to reach each destination ToR */
|
||||
register<bit<16>>(TOR_NUM) dstindex_nhop_reg;
|
||||
|
||||
/* At each hop saves the next hop for each flow */
|
||||
register<bit<16>>(65536) flow_port_reg;
|
||||
|
||||
/* This action will drop packets */
|
||||
action drop() {
|
||||
mark_to_drop();
|
||||
}
|
||||
|
||||
action nop() {
|
||||
}
|
||||
|
||||
action update_ttl(){
|
||||
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
|
||||
}
|
||||
|
||||
action set_dmac(macAddr_t dstAddr){
|
||||
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
|
||||
hdr.ethernet.dstAddr = dstAddr;
|
||||
}
|
||||
|
||||
/* This action just applies source routing */
|
||||
action srcRoute_nhop() {
|
||||
standard_metadata.egress_spec = (bit<9>)hdr.srcRoutes[0].port;
|
||||
hdr.srcRoutes.pop_front(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Runs if it is the destination ToR.
|
||||
* Control plane Gives the index of register for best path from source ToR
|
||||
*/
|
||||
action hula_dst(bit<32> index) {
|
||||
meta.index = index;
|
||||
}
|
||||
|
||||
/*
|
||||
* In reverse path, update nexthop to a destination ToR to ingress port
|
||||
* where we receive hula packet
|
||||
*/
|
||||
action hula_set_nhop(bit<32> index) {
|
||||
dstindex_nhop_reg.write(index, (bit<16>)standard_metadata.ingress_port);
|
||||
}
|
||||
|
||||
/* Read next hop that is saved in hula_set_nhop action for data packets */
|
||||
action hula_get_nhop(bit<32> index){
|
||||
bit<16> tmp;
|
||||
dstindex_nhop_reg.read(tmp, index);
|
||||
standard_metadata.egress_spec = (bit<9>)tmp;
|
||||
}
|
||||
|
||||
/* Record best path at destination ToR */
|
||||
action change_best_path_at_dst(){
|
||||
srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth);
|
||||
srcindex_digest_reg.write(meta.index, hdr.hula.digest);
|
||||
}
|
||||
|
||||
/*
|
||||
* At destination ToR, return packet to source by
|
||||
* - changing its hula direction
|
||||
* - send it to the port it came from
|
||||
*/
|
||||
action return_hula_to_src(){
|
||||
hdr.hula.dir = 1;
|
||||
standard_metadata.egress_spec = standard_metadata.ingress_port;
|
||||
}
|
||||
|
||||
/*
|
||||
* In forward path:
|
||||
* - if destination ToR: run hula_dst to set the index based on srcAddr
|
||||
* - otherwise run srcRoute_nhop to perform source routing
|
||||
*/
|
||||
table hula_fwd {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: exact;
|
||||
hdr.ipv4.srcAddr: exact;
|
||||
}
|
||||
actions = {
|
||||
hula_dst;
|
||||
srcRoute_nhop;
|
||||
}
|
||||
default_action = srcRoute_nhop;
|
||||
size = TOR_NUM_1; // TOR_NUM + 1
|
||||
}
|
||||
|
||||
/*
|
||||
* At each hop in reverse path
|
||||
* update next hop to destination ToR in registers.
|
||||
* index is set based on dstAddr
|
||||
*/
|
||||
table hula_bwd {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: lpm;
|
||||
}
|
||||
actions = {
|
||||
hula_set_nhop;
|
||||
}
|
||||
size = TOR_NUM;
|
||||
}
|
||||
|
||||
/*
|
||||
* in reverse path:
|
||||
* - if source ToR (srcAddr = this switch) drop hula packet
|
||||
* - otherwise, just forward in the reverse path based on source routing
|
||||
*/
|
||||
table hula_src {
|
||||
key = {
|
||||
hdr.ipv4.srcAddr: exact;
|
||||
}
|
||||
actions = {
|
||||
drop;
|
||||
srcRoute_nhop;
|
||||
}
|
||||
default_action = srcRoute_nhop;
|
||||
size = 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* get nexthop based on dstAddr using registers
|
||||
*/
|
||||
table hula_nhop {
|
||||
key = {
|
||||
hdr.ipv4.dstAddr: lpm;
|
||||
}
|
||||
actions = {
|
||||
hula_get_nhop;
|
||||
drop;
|
||||
}
|
||||
default_action = drop;
|
||||
size = TOR_NUM;
|
||||
}
|
||||
|
||||
/*
|
||||
* set right dmac for packets going to hosts
|
||||
*/
|
||||
table dmac {
|
||||
key = {
|
||||
standard_metadata.egress_spec : exact;
|
||||
}
|
||||
actions = {
|
||||
set_dmac;
|
||||
nop;
|
||||
}
|
||||
default_action = nop;
|
||||
size = 16;
|
||||
}
|
||||
|
||||
apply {
|
||||
if (hdr.hula.isValid()){
|
||||
if (hdr.hula.dir == 0){
|
||||
switch(hula_fwd.apply().action_run){
|
||||
|
||||
/* if hula_dst action ran, this is the destination ToR */
|
||||
hula_dst: {
|
||||
|
||||
/* if it is the destination ToR compare qdepth */
|
||||
qdepth_t old_qdepth;
|
||||
srcindex_qdepth_reg.read(old_qdepth, meta.index);
|
||||
|
||||
if (old_qdepth > hdr.hula.qdepth){
|
||||
change_best_path_at_dst();
|
||||
|
||||
/* only return hula packets that update best path */
|
||||
return_hula_to_src();
|
||||
}else{
|
||||
|
||||
/* update the best path even if it has gone worse
|
||||
* so that other paths can replace it later
|
||||
*/
|
||||
digest_t old_digest;
|
||||
srcindex_digest_reg.read(old_digest, meta.index);
|
||||
if (old_digest == hdr.hula.digest){
|
||||
srcindex_qdepth_reg.write(meta.index, hdr.hula.qdepth);
|
||||
}
|
||||
|
||||
drop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}else {
|
||||
/* update routing table in reverse path */
|
||||
hula_bwd.apply();
|
||||
|
||||
/* drop if source ToR */
|
||||
hula_src.apply();
|
||||
}
|
||||
|
||||
}else if (hdr.ipv4.isValid()){
|
||||
bit<16> flow_hash;
|
||||
hash(
|
||||
flow_hash,
|
||||
HashAlgorithm.crc16,
|
||||
16w0,
|
||||
{ hdr.ipv4.srcAddr, hdr.ipv4.dstAddr, hdr.udp.srcPort},
|
||||
32w65536);
|
||||
|
||||
/* look into hula tables */
|
||||
bit<16> port;
|
||||
flow_port_reg.read(port, (bit<32>)flow_hash);
|
||||
|
||||
if (port == 0){
|
||||
/* if it is a new flow check hula paths */
|
||||
hula_nhop.apply();
|
||||
flow_port_reg.write((bit<32>)flow_hash, (bit<16>)standard_metadata.egress_spec);
|
||||
}else{
|
||||
/* old flows still use old path to avoid oscilation and packet reordering */
|
||||
standard_metadata.egress_spec = (bit<9>)port;
|
||||
}
|
||||
|
||||
/* set the right dmac so that ping and iperf work */
|
||||
dmac.apply();
|
||||
}else {
|
||||
drop();
|
||||
}
|
||||
|
||||
if (hdr.ipv4.isValid()){
|
||||
update_ttl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
**************** 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 {
|
||||
if (hdr.hula.isValid() && hdr.hula.dir == 0){
|
||||
|
||||
/* pick max qdepth in hula forward path */
|
||||
if (hdr.hula.qdepth < (qdepth_t)standard_metadata.deq_qdepth){
|
||||
|
||||
/* update queue length */
|
||||
hdr.hula.qdepth = (qdepth_t)standard_metadata.deq_qdepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
************* 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.diffserv,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** D E P A R S E R *******************************
|
||||
*************************************************************************/
|
||||
|
||||
control MyDeparser(packet_out packet, in headers hdr) {
|
||||
apply {
|
||||
packet.emit(hdr.ethernet);
|
||||
packet.emit(hdr.hula);
|
||||
packet.emit(hdr.srcRoutes);
|
||||
packet.emit(hdr.ipv4);
|
||||
packet.emit(hdr.udp);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
*********************** S W I T C H *******************************
|
||||
*************************************************************************/
|
||||
|
||||
V1Switch(
|
||||
MyParser(),
|
||||
MyVerifyChecksum(),
|
||||
MyIngress(),
|
||||
MyEgress(),
|
||||
MyComputeChecksum(),
|
||||
MyDeparser()
|
||||
) main;
|
||||
Reference in New Issue
Block a user