From 77383410128e8234e1e52e8e534196d5601efc3f Mon Sep 17 00:00:00 2001 From: sibanez12 Date: Wed, 25 Oct 2017 16:22:23 -0700 Subject: [PATCH] Basic Encapsulation Example (#64) * Adding initial implementation of basic_encap example * Updated basic_encap example to count the number of valid packets * Updated basic_encap example to put encapsulation layer after Ethernet header. * Added solution file for basic_encap example * Changed the name of the basic_encap example to basic_tunnel and called the new header myTunnel. Also changed the myTunnel field names slightly. --- .../exercises/basic_tunnel/README.md | 163 ++++++++++++++ .../exercises/basic_tunnel/basic_tunnel.p4 | 211 ++++++++++++++++++ .../exercises/basic_tunnel/myTunnel_header.py | 20 ++ .../exercises/basic_tunnel/p4app.json | 32 +++ .../exercises/basic_tunnel/receive.py | 43 ++++ P4D2_2017_Fall/exercises/basic_tunnel/run.sh | 5 + .../exercises/basic_tunnel/s1-commands.txt | 9 + .../exercises/basic_tunnel/s2-commands.txt | 9 + .../exercises/basic_tunnel/s3-commands.txt | 9 + P4D2_2017_Fall/exercises/basic_tunnel/send.py | 45 ++++ .../basic_tunnel/solution/basic_tunnel.p4 | 211 ++++++++++++++++++ 11 files changed, 757 insertions(+) create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/README.md create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/myTunnel_header.py create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/p4app.json create mode 100755 P4D2_2017_Fall/exercises/basic_tunnel/receive.py create mode 100755 P4D2_2017_Fall/exercises/basic_tunnel/run.sh create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/s1-commands.txt create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt create mode 100755 P4D2_2017_Fall/exercises/basic_tunnel/send.py create mode 100644 P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/README.md b/P4D2_2017_Fall/exercises/basic_tunnel/README.md new file mode 100644 index 0000000..e5203e8 --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/README.md @@ -0,0 +1,163 @@ +# Implementing Basic Forwarding + +## Introduction + +The objective of this exercise is to write a P4 program that +implements basic forwarding. To keep things simple, we will just +implement forwarding for IPv4. + +With IPv4 forwarding, the switch must perform the following actions +for every packet: (i) update the source and destination MAC addresses, +(ii) decrement the time-to-live (TTL) in the IP header, and (iii) +forward the packet out the appropriate port. + +Your switch will have a single table, which the control plane will +populate with static rules. Each rule will map an IP address to the +MAC address and output port for the next hop. 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, +`basic.p4`, which initially drops all packets. Your job will be to +extend this skeleton program to properly forward IPv4 packets. + +Before that, let's compile the incomplete `basic.p4` and bring +up a switch in Mininet to test its behavior. + +1. In your shell, run: + ```bash + ./run.sh + ``` + This will: + * compile `basic.p4`, and + * start a Mininet instance with three switches (`s1`, `s2`, `s3`) + configured in a triangle, each connected to one host (`h1`, `h2`, + and `h3`). + * The hosts are assigned IPs of `10.0.1.1`, `10.0.2.2`, etc. + +2. You should now see a Mininet command prompt. Open two terminals +for `h1` and `h2`, respectively: + ```bash + mininet> xterm h1 h2 + ``` +3. Each host includes a small Python-based messaging client and +server. In `h2`'s xterm, start the server: + ```bash + ./receive.py + ``` +4. In `h1`'s xterm, send a message to `h2`: + ```bash + ./send.py 10.0.2.2 "P4 is cool" + ``` + The message will not be received. +5. Type `exit` to leave each xterm and the Mininet command line. + +The message was not received because each switch is programmed +according to `basic.p4`, which drops all packets on arrival. +Your job is to extend this file so it forwards packets. + +### A note about the control plane + +A P4 program defines a packet-processing pipeline, but the rules +within each table are inserted 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, we have already implemented the the control plane +logic for you. 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 commands in the files +`sX-commands.txt` 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 L3 forwarding + +The `basic.p4` file contains a skeleton P4 program with key pieces of +logic replaced by `TODO` comments. Your implementation should follow +the structure given in this file---replace each `TODO` with logic +implementing the missing piece. + +A complete `basic.p4` will contain the following components: + +1. Header type definitions for Ethernet (`ethernet_t`) and IPv4 (`ipv4_t`). +2. **TODO:** Parsers for Ethernet and IPv4 that populate `ethernet_t` and `ipv4_t` fields. +3. An action to drop a packet, using `mark_to_drop()`. +4. **TODO:** An action (called `ipv4_forward`) that: + 1. Sets the egress port for the next hop. + 2. Updates the ethernet destination address with the address of the next hop. + 3. Updates the ethernet source address with the address of the switch. + 4. Decrements the TTL. +5. **TODO:** A control that: + 1. Defines a table that will read an IPv4 destination address, and + invoke either `drop` or `ipv4_forward`. + 2. An `apply` block that applies the table. +6. **TODO:** A deparser that selects the order + in which fields inserted into the outgoing packet. +7. A `package` instantiation supplied with the parser, control, and deparser. + > In general, a package also requires instances of checksum verification + > and recomputation controls. These are not necessary for this tutorial + > and are replaced with instantiations of empty controls. + +## Step 3: Run your solution + +Follow the instructions from Step 1. This time, your message from +`h1` should be delivered to `h2`. + +### Food for thought + +The "test suite" for your solution---sending a message from `h1` to +`h2`---is not very robust. What else should you test to be confident +of your implementation? + +> Although the Python `scapy` library is outside the scope of this tutorial, +> it can be used to generate packets for testing. The `send.py` file shows how +> to use it. + +Other questions to consider: + - How would you enhance your program to support next hops? + - Is this program enough to replace a router? What's missing? + +### Troubleshooting + +There are several problems that might manifest as you develop your program: + +1. `basic.p4` might fails to compile. In this case, `run.sh` will +report the error emitted from the compiler and halt. + +2. `basic.p4` might compile but fail to support the control plane +rules in the `s1-commands.txt` through `s3-command.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 `basic.p4` implementation. + +3. `basic.p4` might compile, and the control plane rules might be +installed, but the switch might not process packets in the desired +way. The `build/logs/.log` files contain detailed logs +that describing how each switch processes each packet. The output is +detailed and can help pinpoint logic errors in your implementation. + +#### Cleaning up Mininet + +In the latter two cases above, `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! Move on to the next +exercise: implementing the [scrambler](../scrambler)! diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 b/P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 new file mode 100644 index 0000000..a7339da --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/basic_tunnel.p4 @@ -0,0 +1,211 @@ +/* -*- P4_16 -*- */ +#include +#include + +const bit<16> TYPE_MYTUNNEL = 0x1212; +const bit<16> TYPE_IPV4 = 0x800; + +/************************************************************************* +*********************** H E A D E R S *********************************** +*************************************************************************/ + +typedef bit<9> egressSpec_t; +typedef bit<48> macAddr_t; +typedef bit<32> ip4Addr_t; + +header ethernet_t { + macAddr_t dstAddr; + macAddr_t srcAddr; + bit<16> etherType; +} + +header myTunnel_t { + bit<16> proto_id; + bit<16> dst_id; +} + +header ipv4_t { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> totalLen; + bit<16> identification; + bit<3> flags; + bit<13> fragOffset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdrChecksum; + ip4Addr_t srcAddr; + ip4Addr_t dstAddr; +} + +struct metadata { + /* empty */ +} + +struct headers { + ethernet_t ethernet; + myTunnel_t myTunnel; + ipv4_t ipv4; +} + +/************************************************************************* +*********************** 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_MYTUNNEL: parse_myTunnel; + TYPE_IPV4: parse_ipv4; + default: accept; + } + } + + state parse_myTunnel { + packet.extract(hdr.myTunnel); + transition select(hdr.myTunnel.proto_id) { + TYPE_IPV4: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + packet.extract(hdr.ipv4); + transition accept; + } + +} + +/************************************************************************* +************ C H E C K S U M V E R I F I C A T I O N ************* +*************************************************************************/ + +control MyVerifyChecksum(in headers hdr, inout metadata meta) { + apply { } +} + + +/************************************************************************* +************** I N G R E S S P R O C E S S I N G ******************* +*************************************************************************/ + +control MyIngress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + action drop() { + mark_to_drop(); + } + + action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { + standard_metadata.egress_spec = port; + hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; + hdr.ethernet.dstAddr = dstAddr; + hdr.ipv4.ttl = hdr.ipv4.ttl - 1; + } + + table ipv4_lpm { + key = { + hdr.ipv4.dstAddr: lpm; + } + actions = { + ipv4_forward; + drop; + NoAction; + } + size = 1024; + default_action = NoAction(); + } + + action myTunnel_forward(egressSpec_t port) { + standard_metadata.egress_spec = port; + } + + table myTunnel_exact { + key = { + hdr.myTunnel.dst_id: exact; + } + actions = { + myTunnel_forward; + drop; + } + size = 1024; + default_action = drop(); + } + + apply { + if (hdr.myTunnel.isValid()) { + myTunnel_exact.apply(); + } else if (hdr.ipv4.isValid()) { + ipv4_lpm.apply(); + } + } +} + +/************************************************************************* +**************** E G R E S S P R O C E S S I N G ******************* +*************************************************************************/ + +control MyEgress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + apply { } +} + +/************************************************************************* +************* C H E C K S U M C O M P U T A T I O N ************** +*************************************************************************/ + +control MyComputeChecksum(inout headers hdr, inout metadata meta) { + apply { + 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.myTunnel); + packet.emit(hdr.ipv4); + } +} + +/************************************************************************* +*********************** S W I T C H ******************************* +*************************************************************************/ + +V1Switch( +MyParser(), +MyVerifyChecksum(), +MyIngress(), +MyEgress(), +MyComputeChecksum(), +MyDeparser() +) main; diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/myTunnel_header.py b/P4D2_2017_Fall/exercises/basic_tunnel/myTunnel_header.py new file mode 100644 index 0000000..5a5922e --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/myTunnel_header.py @@ -0,0 +1,20 @@ + +from scapy.all import * +import sys, os + +TYPE_MYTUNNEL = 0x1212 +TYPE_IPV4 = 0x0800 + +class MyTunnel(Packet): + name = "MyTunnel" + fields_desc = [ + ShortField("pid", 0), + ShortField("dst_id", 0) + ] + def mysummary(self): + return self.sprintf("pid=%pid%, dst_id=%dst_id%") + + +bind_layers(Ether, MyTunnel, type=TYPE_MYTUNNEL) +bind_layers(MyTunnel, IP, pid=TYPE_IPV4) + diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/p4app.json b/P4D2_2017_Fall/exercises/basic_tunnel/p4app.json new file mode 100644 index 0000000..9080e0d --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/p4app.json @@ -0,0 +1,32 @@ +{ + "program": "basic_tunnel.p4", + "language": "p4-16", + "targets": { + "multiswitch": { + "auto-control-plane": true, + "cli": true, + "pcap_dump": true, + "bmv2_log": true, + "links": [["h1", "s1"], ["s1", "s2"], ["s1", "s3"], ["s3", "s2"], ["s2", "h2"], ["s3", "h3"]], + "hosts": { + "h1": { + }, + "h2": { + }, + "h3": { + } + }, + "switches": { + "s1": { + "entries": "s1-commands.txt" + }, + "s2": { + "entries": "s2-commands.txt" + }, + "s3": { + "entries": "s3-commands.txt" + } + } + } + } +} diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/receive.py b/P4D2_2017_Fall/exercises/basic_tunnel/receive.py new file mode 100755 index 0000000..e83dd5e --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/receive.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import sys +import struct +import os + +from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr +from scapy.all import Packet, IPOption +from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField +from scapy.all import IP, UDP, Raw +from scapy.layers.inet import _IPOption_HDR +from myTunnel_header import MyTunnel + +def get_if(): + ifs=get_if_list() + iface=None + for i in get_if_list(): + if "eth0" in i: + iface=i + break; + if not iface: + print "Cannot find eth0 interface" + exit(1) + return iface + +def handle_pkt(pkt): + if MyTunnel in pkt: + print "got a packet" + pkt.show2() +# hexdump(pkt) +# print "len(pkt) = ", len(pkt) + sys.stdout.flush() + + +def main(): + ifaces = filter(lambda i: 'eth' in i, os.listdir('/sys/class/net/')) + iface = ifaces[0] + print "sniffing on %s" % iface + sys.stdout.flush() + sniff(iface = iface, + prn = lambda x: handle_pkt(x)) + +if __name__ == '__main__': + main() diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/run.sh b/P4D2_2017_Fall/exercises/basic_tunnel/run.sh new file mode 100755 index 0000000..d5c1947 --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/run.sh @@ -0,0 +1,5 @@ +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/basic_tunnel/s1-commands.txt b/P4D2_2017_Fall/exercises/basic_tunnel/s1-commands.txt new file mode 100644 index 0000000..8b7ba2d --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/s1-commands.txt @@ -0,0 +1,9 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:00:01:01 1 +table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:02:00 2 +table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 + +table_set_default myTunnel_exact drop +table_add myTunnel_exact myTunnel_forward 1 => 1 +table_add myTunnel_exact myTunnel_forward 2 => 2 +table_add myTunnel_exact myTunnel_forward 3 => 3 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt b/P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt new file mode 100644 index 0000000..39c553b --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/s2-commands.txt @@ -0,0 +1,9 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2 +table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:00:02:02 1 +table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:03:03:00 3 + +table_set_default myTunnel_exact drop +table_add myTunnel_exact myTunnel_forward 1 => 2 +table_add myTunnel_exact myTunnel_forward 2 => 1 +table_add myTunnel_exact myTunnel_forward 3 => 3 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt b/P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt new file mode 100644 index 0000000..a6c2777 --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/s3-commands.txt @@ -0,0 +1,9 @@ +table_set_default ipv4_lpm drop +table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:03:00 2 +table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:03:00 3 +table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:00:00:03:03 1 + +table_set_default myTunnel_exact drop +table_add myTunnel_exact myTunnel_forward 1 => 2 +table_add myTunnel_exact myTunnel_forward 2 => 3 +table_add myTunnel_exact myTunnel_forward 3 => 1 diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/send.py b/P4D2_2017_Fall/exercises/basic_tunnel/send.py new file mode 100755 index 0000000..8621ac1 --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/send.py @@ -0,0 +1,45 @@ +#!/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, hexdump +from scapy.all import Packet +from scapy.all import Ether, IP, UDP, TCP +from myTunnel_header import MyTunnel + +def get_if(): + ifs=get_if_list() + iface=None # "h1-eth0" + for i in get_if_list(): + if "eth0" in i: + iface=i + break; + if not iface: + print "Cannot find eth0 interface" + exit(1) + return iface + +def main(): + + if len(sys.argv)<4: + print 'pass 2 arguments: ""' + exit(1) + + addr = socket.gethostbyname(sys.argv[1]) + dst_id = int(sys.argv[2]) + iface = get_if() + + print "sending on interface %s to id %s" % (iface, str(dst_id)) + pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff') + pkt = pkt / MyTunnel(dst_id=dst_id) / IP(dst=addr) / sys.argv[3] + pkt.show2() +# hexdump(pkt) +# print "len(pkt) = ", len(pkt) + sendp(pkt, iface=iface, verbose=False) + + +if __name__ == '__main__': + main() diff --git a/P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 b/P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 new file mode 100644 index 0000000..a7339da --- /dev/null +++ b/P4D2_2017_Fall/exercises/basic_tunnel/solution/basic_tunnel.p4 @@ -0,0 +1,211 @@ +/* -*- P4_16 -*- */ +#include +#include + +const bit<16> TYPE_MYTUNNEL = 0x1212; +const bit<16> TYPE_IPV4 = 0x800; + +/************************************************************************* +*********************** H E A D E R S *********************************** +*************************************************************************/ + +typedef bit<9> egressSpec_t; +typedef bit<48> macAddr_t; +typedef bit<32> ip4Addr_t; + +header ethernet_t { + macAddr_t dstAddr; + macAddr_t srcAddr; + bit<16> etherType; +} + +header myTunnel_t { + bit<16> proto_id; + bit<16> dst_id; +} + +header ipv4_t { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> totalLen; + bit<16> identification; + bit<3> flags; + bit<13> fragOffset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdrChecksum; + ip4Addr_t srcAddr; + ip4Addr_t dstAddr; +} + +struct metadata { + /* empty */ +} + +struct headers { + ethernet_t ethernet; + myTunnel_t myTunnel; + ipv4_t ipv4; +} + +/************************************************************************* +*********************** 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_MYTUNNEL: parse_myTunnel; + TYPE_IPV4: parse_ipv4; + default: accept; + } + } + + state parse_myTunnel { + packet.extract(hdr.myTunnel); + transition select(hdr.myTunnel.proto_id) { + TYPE_IPV4: parse_ipv4; + default: accept; + } + } + + state parse_ipv4 { + packet.extract(hdr.ipv4); + transition accept; + } + +} + +/************************************************************************* +************ C H E C K S U M V E R I F I C A T I O N ************* +*************************************************************************/ + +control MyVerifyChecksum(in headers hdr, inout metadata meta) { + apply { } +} + + +/************************************************************************* +************** I N G R E S S P R O C E S S I N G ******************* +*************************************************************************/ + +control MyIngress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + action drop() { + mark_to_drop(); + } + + action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { + standard_metadata.egress_spec = port; + hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; + hdr.ethernet.dstAddr = dstAddr; + hdr.ipv4.ttl = hdr.ipv4.ttl - 1; + } + + table ipv4_lpm { + key = { + hdr.ipv4.dstAddr: lpm; + } + actions = { + ipv4_forward; + drop; + NoAction; + } + size = 1024; + default_action = NoAction(); + } + + action myTunnel_forward(egressSpec_t port) { + standard_metadata.egress_spec = port; + } + + table myTunnel_exact { + key = { + hdr.myTunnel.dst_id: exact; + } + actions = { + myTunnel_forward; + drop; + } + size = 1024; + default_action = drop(); + } + + apply { + if (hdr.myTunnel.isValid()) { + myTunnel_exact.apply(); + } else if (hdr.ipv4.isValid()) { + ipv4_lpm.apply(); + } + } +} + +/************************************************************************* +**************** E G R E S S P R O C E S S I N G ******************* +*************************************************************************/ + +control MyEgress(inout headers hdr, + inout metadata meta, + inout standard_metadata_t standard_metadata) { + apply { } +} + +/************************************************************************* +************* C H E C K S U M C O M P U T A T I O N ************** +*************************************************************************/ + +control MyComputeChecksum(inout headers hdr, inout metadata meta) { + apply { + 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.myTunnel); + packet.emit(hdr.ipv4); + } +} + +/************************************************************************* +*********************** S W I T C H ******************************* +*************************************************************************/ + +V1Switch( +MyParser(), +MyVerifyChecksum(), +MyIngress(), +MyEgress(), +MyComputeChecksum(), +MyDeparser() +) main;