From 0557b61f2f17e0c4e4a373d6ff66dbb824da3e02 Mon Sep 17 00:00:00 2001 From: Robert Soule Date: Fri, 3 Nov 2017 20:30:11 -0700 Subject: [PATCH] Removed Hula --- P4D2_2017_Fall/exercises/hula/README.md | 300 ------------ P4D2_2017_Fall/exercises/hula/generatehula.py | 82 ---- P4D2_2017_Fall/exercises/hula/hula.p4 | 433 ----------------- P4D2_2017_Fall/exercises/hula/p4app.json | 39 -- P4D2_2017_Fall/exercises/hula/run.sh | 5 - P4D2_2017_Fall/exercises/hula/s1-commands.txt | 16 - .../exercises/hula/s11-commands.txt | 6 - P4D2_2017_Fall/exercises/hula/s2-commands.txt | 16 - .../exercises/hula/s22-commands.txt | 6 - P4D2_2017_Fall/exercises/hula/s3-commands.txt | 16 - .../exercises/hula/solution/hula.p4 | 449 ------------------ 11 files changed, 1368 deletions(-) delete mode 100644 P4D2_2017_Fall/exercises/hula/README.md delete mode 100755 P4D2_2017_Fall/exercises/hula/generatehula.py delete mode 100644 P4D2_2017_Fall/exercises/hula/hula.p4 delete mode 100644 P4D2_2017_Fall/exercises/hula/p4app.json delete mode 100755 P4D2_2017_Fall/exercises/hula/run.sh delete mode 100644 P4D2_2017_Fall/exercises/hula/s1-commands.txt delete mode 100644 P4D2_2017_Fall/exercises/hula/s11-commands.txt delete mode 100644 P4D2_2017_Fall/exercises/hula/s2-commands.txt delete mode 100644 P4D2_2017_Fall/exercises/hula/s22-commands.txt delete mode 100644 P4D2_2017_Fall/exercises/hula/s3-commands.txt delete mode 100644 P4D2_2017_Fall/exercises/hula/solution/hula.p4 diff --git a/P4D2_2017_Fall/exercises/hula/README.md b/P4D2_2017_Fall/exercises/hula/README.md deleted file mode 100644 index b4e5c3e..0000000 --- a/P4D2_2017_Fall/exercises/hula/README.md +++ /dev/null @@ -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/.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/-.pcap` also contains the pcap of packets on each -interface. Use `tcpdump -r -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! diff --git a/P4D2_2017_Fall/exercises/hula/generatehula.py b/P4D2_2017_Fall/exercises/hula/generatehula.py deleted file mode 100755 index 0acacf5..0000000 --- a/P4D2_2017_Fall/exercises/hula/generatehula.py +++ /dev/null @@ -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() diff --git a/P4D2_2017_Fall/exercises/hula/hula.p4 b/P4D2_2017_Fall/exercises/hula/hula.p4 deleted file mode 100644 index 8faa0ed..0000000 --- a/P4D2_2017_Fall/exercises/hula/hula.p4 +++ /dev/null @@ -1,433 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -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(TOR_NUM) srcindex_qdepth_reg; - - /* At destination ToR, saves the digest of the best path from - * each source ToR - */ - register(TOR_NUM) srcindex_digest_reg; - - /* At each hop, saves the next hop to reach each destination ToR */ - register>(TOR_NUM) dstindex_nhop_reg; - - /* At each hop saves the next hop for each flow */ - register>(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; diff --git a/P4D2_2017_Fall/exercises/hula/p4app.json b/P4D2_2017_Fall/exercises/hula/p4app.json deleted file mode 100644 index c5c6562..0000000 --- a/P4D2_2017_Fall/exercises/hula/p4app.json +++ /dev/null @@ -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" - } - - } - } - } -} diff --git a/P4D2_2017_Fall/exercises/hula/run.sh b/P4D2_2017_Fall/exercises/hula/run.sh deleted file mode 100755 index d5c1947..0000000 --- a/P4D2_2017_Fall/exercises/hula/run.sh +++ /dev/null @@ -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 diff --git a/P4D2_2017_Fall/exercises/hula/s1-commands.txt b/P4D2_2017_Fall/exercises/hula/s1-commands.txt deleted file mode 100644 index 2ca3bf8..0000000 --- a/P4D2_2017_Fall/exercises/hula/s1-commands.txt +++ /dev/null @@ -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 diff --git a/P4D2_2017_Fall/exercises/hula/s11-commands.txt b/P4D2_2017_Fall/exercises/hula/s11-commands.txt deleted file mode 100644 index f63d939..0000000 --- a/P4D2_2017_Fall/exercises/hula/s11-commands.txt +++ /dev/null @@ -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 diff --git a/P4D2_2017_Fall/exercises/hula/s2-commands.txt b/P4D2_2017_Fall/exercises/hula/s2-commands.txt deleted file mode 100644 index b5508f6..0000000 --- a/P4D2_2017_Fall/exercises/hula/s2-commands.txt +++ /dev/null @@ -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 diff --git a/P4D2_2017_Fall/exercises/hula/s22-commands.txt b/P4D2_2017_Fall/exercises/hula/s22-commands.txt deleted file mode 100644 index f63d939..0000000 --- a/P4D2_2017_Fall/exercises/hula/s22-commands.txt +++ /dev/null @@ -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 diff --git a/P4D2_2017_Fall/exercises/hula/s3-commands.txt b/P4D2_2017_Fall/exercises/hula/s3-commands.txt deleted file mode 100644 index 5600d49..0000000 --- a/P4D2_2017_Fall/exercises/hula/s3-commands.txt +++ /dev/null @@ -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 diff --git a/P4D2_2017_Fall/exercises/hula/solution/hula.p4 b/P4D2_2017_Fall/exercises/hula/solution/hula.p4 deleted file mode 100644 index e8ea288..0000000 --- a/P4D2_2017_Fall/exercises/hula/solution/hula.p4 +++ /dev/null @@ -1,449 +0,0 @@ -/* -*- P4_16 -*- */ -#include -#include - -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(TOR_NUM) srcindex_qdepth_reg; - - /* - * At destination ToR, saves the digest of the best path from - * each source ToR - */ - register(TOR_NUM) srcindex_digest_reg; - - /* At each hop, saves the next hop to reach each destination ToR */ - register>(TOR_NUM) dstindex_nhop_reg; - - /* At each hop saves the next hop for each flow */ - register>(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;