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