diff --git a/docker/scripts/mininet/single_switch_mininet.py b/docker/scripts/mininet/single_switch_mininet.py index 67f8035..bc50ee9 100755 --- a/docker/scripts/mininet/single_switch_mininet.py +++ b/docker/scripts/mininet/single_switch_mininet.py @@ -41,6 +41,8 @@ type=str, action="store", required=False) parser.add_argument('--pcap-dump', help='Dump packets on interfaces to pcap files', action="store_true") +parser.add_argument('--cli-path', help='path to control utility for switch', + type=str, action='store', required=False, default='simple_switch_CLI') parser.add_argument('--switch-config', help='simple_switch_CLI script to configure switch', type=str, action="store", required=False, default=False) parser.add_argument('--cli-message', help='Message to print before starting CLI', @@ -114,7 +116,7 @@ def main(): switch_config = config_file.read() print "Configuring switch..." - proc = Popen(["simple_switch_CLI"], stdin=PIPE) + proc = Popen([args.cli_path], stdin=PIPE) proc.communicate(input=switch_config) print "Configuration complete." diff --git a/docker/scripts/p4apprunner.py b/docker/scripts/p4apprunner.py index 30f35b7..9bc6128 100755 --- a/docker/scripts/p4apprunner.py +++ b/docker/scripts/p4apprunner.py @@ -22,6 +22,11 @@ import sys import tarfile +DEFAULT_BEHAVIORAL_EXE = 'simple_switch' + +def default_cli_path(behavioral_exe): + return '%s_CLI' % behavioral_exe + parser = argparse.ArgumentParser(description='p4apprunner') parser.add_argument('--build-dir', help='Directory to build in.', type=str, action='store', required=False, default='/tmp') @@ -92,6 +97,8 @@ def get_program_name(program_file): return os.path.basename(program_file).rstrip('.p4') def run_compile_bmv2(manifest): + compiler = manifest.target_config.get('compiler', 'p4c-bm2-ss') + if 'run-before-compile' in manifest.target_config: commands = manifest.target_config['run-before-compile'] if not isinstance(commands, list): @@ -121,7 +128,7 @@ def run_compile_bmv2(manifest): output_file = get_program_name(manifest.program_file) + '.json' compiler_args.append('"%s"' % manifest.program_file) compiler_args.append('-o "%s"' % output_file) - rv = run_command('p4c-bm2-ss %s' % ' '.join(compiler_args)) + rv = run_command('%s %s' % (compiler, ' '.join(compiler_args))) if 'run-after-compile' in manifest.target_config: commands = manifest.target_config['run-after-compile'] @@ -138,6 +145,8 @@ def run_compile_bmv2(manifest): return output_file def run_mininet(manifest): + print(manifest) + output_file = run_compile_bmv2(manifest) # Run the program using the BMV2 Mininet simple switch. @@ -187,7 +196,11 @@ def run_mininet(manifest): if 'switch-config' in manifest.target_config: switch_args.append('--switch-config "%s"' % manifest.target_config['switch-config']) - switch_args.append('--behavioral-exe "%s"' % 'simple_switch') + behavioral_exe = manifest.target_config.get('behavioral-exe', DEFAULT_BEHAVIORAL_EXE) + cli_path = manifest.target_config.get('cli-path', default_cli_path(behavioral_exe)) + + switch_args.append('--behavioral-exe "%s"' % behavioral_exe) + switch_args.append('--cli-path "%s"' % cli_path) switch_args.append('--json "%s"' % output_file) program = '"%s/mininet/single_switch_mininet.py"' % sys.path[0] @@ -220,12 +233,12 @@ def run_multiswitch(manifest): if model == 'bmv2': if args.json: json_file = os.path.abspath(args.json) else: json_file = run_compile_bmv2(manifest) - behavioral_exe = 'simple_switch' - switch_cli = 'simple_switch_CLI' else: log_error('Unrecognized model:', model) sys.exit(1) + behavioral_exe = manifest.target_config.get('behavioral-exe', DEFAULT_BEHAVIORAL_EXE) + cli_path = manifest.target_config.get('cli-path', default_cli_path(behavioral_exe)) script_args = [] script_args.append('--log-dir "/tmp/p4app_logs"') script_args.append('--manifest "%s"' % args.manifest) @@ -233,7 +246,7 @@ def run_multiswitch(manifest): if 'auto-control-plane' in manifest.target_config and manifest.target_config['auto-control-plane']: script_args.append('--auto-control-plane' ) script_args.append('--behavioral-exe "%s"' % behavioral_exe) - script_args.append('--cli-path "%s"' % switch_cli) + script_args.append('--cli-path "%s"' % cli_path) script_args.append('--json "%s"' % json_file) program = '"%s/mininet/multi_switch_mininet.py"' % sys.path[0] @@ -260,12 +273,14 @@ def run_stf(manifest): return rv def run_custom(manifest): + behavioral_exe = manifest.target_config.get('behavioral-exe', DEFAULT_BEHAVIORAL_EXE) + cli_path = manifest.target_config.get('cli-path', default_cli_path(behavioral_exe)) output_file = run_compile_bmv2(manifest) python_path = 'PYTHONPATH=$PYTHONPATH:/scripts/mininet/' script_args = [] - script_args.append('--behavioral-exe "%s"' % 'simple_switch') + script_args.append('--behavioral-exe "%s"' % behavioral_exe) script_args.append('--json "%s"' % output_file) - script_args.append('--cli "%s"' % 'simple_switch_CLI') + script_args.append('--cli "%s"' % cli_path) if not 'program' in manifest.target_config: log_error('No mininet program file provided.') sys.exit(1) diff --git a/examples/simple_switch_psa.p4app/README.md b/examples/simple_switch_psa.p4app/README.md new file mode 100644 index 0000000..df4e1e1 --- /dev/null +++ b/examples/simple_switch_psa.p4app/README.md @@ -0,0 +1,43 @@ +# Implementing a P4 Switch based on PSA + +Run this app. + +```bash +p4app run examples/simple_switch_psa.p4app +``` + +Start capture on h2 from another shell. + +```bash +p4app exec m h2 tcpdump +``` + +Send packet from h1 whose length was large than 100 bytes. + +``` +mininet> h1 python send.py +###[ Ethernet ]### + dst = 00:04:00:00:00:01 + src = 00:00:00:00:00:00 + type = 0x1234 +###[ Raw ]### + load = 'girl drives odd foolishly. girl runs stupid crazily. girl runs clueless foolishly. puppy runs stupid crazily. ' +. +Sent 1 packets. +``` + +See captured packet and find that the packet was truncated to 64 bytes. + +```bash +$ p4app exec m h2 tcpdump +tcpdump: verbose output suppressed, use -v or -vv for full protocol decode +listening on h2-eth0, link-type EN10MB (Ethernet), capture size 262144 bytes +14:51:48.709887 00:00:00:00:00:00 (oui Ethernet) > 00:04:00:00:00:01 (oui Unknown), ethertype Unknown (0x1234), length 124: + 0x0000: 6769 726c 2064 7269 7665 7320 6f64 6420 girl.drives.odd. + 0x0010: 666f 6f6c 6973 686c 792e 2067 6972 6c20 foolishly..girl. + 0x0020: 7275 6e73 2073 7475 7069 6420 6372 617a runs.stupid.craz + 0x0030: 696c 792e 2067 6972 6c20 7275 6e73 2063 ily..girl.runs.c + 0x0040: 6c75 656c 6573 7320 666f 6f6c 6973 686c lueless.foolishl + 0x0050: 792e 2070 7570 7079 2072 756e 7320 7374 y..puppy.runs.st + 0x0060: 7570 6964 2063 7261 7a69 6c79 2e20 upid.crazily.. +``` diff --git a/examples/simple_switch_psa.p4app/p4app.json b/examples/simple_switch_psa.p4app/p4app.json new file mode 100644 index 0000000..cef284d --- /dev/null +++ b/examples/simple_switch_psa.p4app/p4app.json @@ -0,0 +1,13 @@ +{ + "program": "simple_switch.p4", + "language": "p4-16", + "targets": { + "mininet": { + "compiler": "p4c-bm2-psa", + "behavioral-exe": "psa_switch", + "cli-path": "psa_switch_CLI", + "num-hosts": 2, + "switch-config": "simple_switch.config" + } + } +} diff --git a/examples/simple_switch_psa.p4app/send.py b/examples/simple_switch_psa.p4app/send.py new file mode 100644 index 0000000..774a5c4 --- /dev/null +++ b/examples/simple_switch_psa.p4app/send.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +import random + +from scapy.all import sendp +from scapy.all import Ether + + +def random_sentence(): + nouns = ("puppy", "car", "rabbit", "girl", "monkey") + verbs = ("runs", "hits", "jumps", "drives", "barfs") + adv = ("crazily. ", "dutifully. ", "foolishly. ", "merrily. ", "occasionally. ") + adj = ("adorable", "clueless", "dirty", "odd", "stupid") + l = [nouns, verbs, adj, adv] + sentence = ' '.join([random.choice(i) for i in l]) + while len(sentence) < 100: + sentence += ' '.join([random.choice(i) for i in l]) + return sentence + +def main(): + payload = random_sentence() + pkt = Ether(dst='00:04:00:00:00:01', type=0x1234) / payload + pkt.show() + sendp(pkt) + +if __name__ == '__main__': + main() diff --git a/examples/simple_switch_psa.p4app/simple_switch.config b/examples/simple_switch_psa.p4app/simple_switch.config new file mode 100644 index 0000000..24aa086 --- /dev/null +++ b/examples/simple_switch_psa.p4app/simple_switch.config @@ -0,0 +1,2 @@ +table_add arp_table set_fwd_port 00:04:00:00:00:00 => 1 +table_add arp_table set_fwd_port 00:04:00:00:00:01 => 2 diff --git a/examples/simple_switch_psa.p4app/simple_switch.p4 b/examples/simple_switch_psa.p4app/simple_switch.p4 new file mode 100644 index 0000000..54002ac --- /dev/null +++ b/examples/simple_switch_psa.p4app/simple_switch.p4 @@ -0,0 +1,139 @@ +#include +#include + +typedef bit<48> EthernetAddress; + +header ethernet_t { + EthernetAddress dstAddr; + EthernetAddress srcAddr; + bit<16> etherType; +} + +struct empty_metadata_t { +} + +struct metadata_t { +} + +struct headers_t { + ethernet_t ethernet; +} + +parser IngressParserImpl( + packet_in pkt, + out headers_t hdr, + inout metadata_t user_meta, + in psa_ingress_parser_input_metadata_t istd, + in empty_metadata_t resubmit_meta, + in empty_metadata_t recirculate_meta +) { + state start { + pkt.extract(hdr.ethernet); + transition accept; + } +} + +control cIngress( + inout headers_t hdr, + inout metadata_t user_meta, + in psa_ingress_input_metadata_t istd, + inout psa_ingress_output_metadata_t ostd) +{ + action set_fwd_port(PortId_t port) { + send_to_port(ostd, port); + } + + table arp_table { + key = { + hdr.ethernet.dstAddr: exact; + } + actions = { + set_fwd_port; + } + size = 24; + } + + apply { + arp_table.apply(); + } +} + +parser EgressParserImpl( + packet_in buffer, + out headers_t hdr, + inout metadata_t user_meta, + in psa_egress_parser_input_metadata_t istd, + in empty_metadata_t normal_meta, + in empty_metadata_t clone_i2e_meta, + in empty_metadata_t clone_e2e_meta +) { + state start { + transition accept; + } +} + +control cEgress( + inout headers_t hdr, + inout metadata_t user_meta, + in psa_egress_input_metadata_t istd, + inout psa_egress_output_metadata_t ostd +) { + apply { } +} + +control CommonDeparserImpl(packet_out packet, + inout headers_t hdr) +{ + apply { + packet.emit(hdr.ethernet); + } +} + +control IngressDeparserImpl( + packet_out buffer, + out empty_metadata_t clone_e2e_meta, + out empty_metadata_t recirculate_meta, + out empty_metadata_t normal_meta, + inout headers_t hdr, + in metadata_t meta, + in psa_ingress_output_metadata_t istd +) { + CommonDeparserImpl() cp; + apply { + cp.apply(buffer, hdr); + } +} + +control EgressDeparserImpl( + packet_out buffer, + out empty_metadata_t clone_e2e_meta, + out empty_metadata_t recirculate_meta, + inout headers_t hdr, + in metadata_t meta, + in psa_egress_output_metadata_t istd, + in psa_egress_deparser_input_metadata_t edstd +) { + CommonDeparserImpl() cp; + apply { + cp.apply(buffer, hdr); + } +} + +IngressPipeline( + IngressParserImpl(), + cIngress(), + IngressDeparserImpl() +) ip; + +EgressPipeline( + EgressParserImpl(), + cEgress(), + EgressDeparserImpl() +) ep; + +PSA_Switch( + ip, + PacketReplicationEngine(), + ep, + BufferingQueueingEngine() +) main;