├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── agent ├── launch.sh ├── mantis_ctl.c ├── pi_setup.py └── run.sh ├── compile_p4r.sh ├── examples ├── dos.p4r ├── failover_tstamp.p4r ├── field_arg.p4r ├── figure1.p4r ├── figure4.p4r ├── figure5.p4r ├── figure6.p4r ├── mbl_table.p4r └── table_add_del_mod.p4r ├── frontend.l ├── frontend.y ├── include ├── ast_nodes.h ├── ast_nodes_p4.h ├── ast_nodes_p4r.h ├── compile.h ├── find_nodes.h └── helper.h ├── src ├── ast │ ├── ast_nodes_p4.cpp │ └── ast_nodes_p4r.cpp └── compile │ ├── compile.cpp │ ├── compile_c.cpp │ ├── compile_c.h │ ├── compile_const.h │ ├── compile_p4.cpp │ ├── compile_p4.h │ └── find_nodes.cpp ├── tutorial.md └── util ├── benign.dpdk ├── malicious.dpdk └── p4_14_compile.sh /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | **/.vscode 3 | frontend.tab.c 4 | frontend.tab.h 5 | frontend 6 | lex.yy.c 7 | out 8 | agent/context 9 | **/*.o 10 | **/zlog-cfg-cur 11 | **/bf_drivers.log* 12 | agent/mantis_ctl -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 University of Pennsylvania | Distributed Systems Lab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | target = frontend 2 | 3 | CPP_SRC=$(wildcard src/**/*.cpp) 4 | CPP_H=$(wildcard include/*.h) 5 | CPP_FLAGS=-Wno-deprecated-register 6 | 7 | all: $(target) 8 | 9 | $(target).tab.c $(target).tab.h: $(target).y 10 | bison -d $(target).y 11 | 12 | lex.yy.c: $(target).l $(target).tab.h 13 | flex $(target).l 14 | 15 | $(target): $(CPP_SRC) $(CPP_H) lex.yy.c $(target).tab.c $(target).tab.h 16 | g++ -Wno-format -g -rdynamic -o $(target) $(target).tab.c lex.yy.c $(CPP_SRC) -std=c++11 $(CPP_FLAGS) $(PRINT_FLAGS) 17 | 18 | clean: 19 | rm -f $(target) $(target).tab.c lex.yy.c $(target).tab.h 20 | rm -rf $(target).dSYM/ 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Mantis 2 | 3 | ### Description 4 | 5 | P4R frontend is a lightweight flex-bison based compiler that converts a P4R program into two files: 6 | 7 | 1. a tofino-compatible P4-14 program targeting Wedge100BF-32X Tofino switch: `_mantis.p4` 8 | 2. a prologue/dialogue implementation in C: `p4r.c` 9 | 10 | The compiler first transforms P4R code into a syntax tree, then *interprets* the syntax tree with multiple passes that adds new code and transforms the existing code. 11 | Finally, it dumps the P4, C from the syntax tree to separate output files. 12 | The frontend generated C file is later forwarded to C preprocessor to render the actual implementation. 13 | Later Mantis will generate and load the shared object and link it to the run time agent. 14 | 15 | ### Prerequisites 16 | 17 | * Flex, Bison, g++ (version >=4.9) 18 | * Barefoot SDE (optional, if further compile the generated malleable P4 code to tofino ASIC or its simulation model or launch an agent on switch onboard CPU, the full implemention is tested on 9.0.0) 19 | 20 | ### Contents 21 | 22 | - `examples/`: Sample p4r programs 23 | - `src/`: Source code for P4R frontend 24 | - `ast/`: AST nodes for P4 and P4R 25 | - `compile/`: P4/C compilation passes 26 | - `include/`: Header files included by bison parser 27 | - `frontend.l`: flex tokenizer 28 | - `frontend.y`: bison grammar parser 29 | - `agent/`: Mantis agent related 30 | 31 | ### How to Run 32 | 33 | Point `SDE` variable to the SDE root directory, e.g., 34 | 35 | ``` 36 | export SDE="/home/mantis/bf-sde-9.0.0" 37 | ``` 38 | 39 | #### Frontend 40 | 41 | `compile_p4r.sh` wraps the usage of frontend: `./compile_p4r.sh [-vv] [-o output_dir] input_file` 42 | 43 | Arguments to the script are: 44 | 45 | - ```input_file```: file path for input p4r 46 | - ```-o```: optional flag to specify the target output directory, by default, the generated files will be at `out/` directory 47 | - ```-vv```: optional flag to view info/verbose output 48 | 49 | For example, to compile `examples/dos.p4r` to the default `out/` directory with verbose flag: 50 | ``` 51 | sudo -E ./compile_p4r.sh -vv examples/dos.p4r 52 | ``` 53 | 54 | `compile_p4r.sh` also links the compilation of the malleable P4 code at the end, one could comment out the last section when a tofino switch/similator is not available. 55 | 56 | #### Agent 57 | 58 | * `launch.sh` wraps the launch of a Mantis controller instance: `sudo -E ./launch.sh ` 59 | * `run.sh` wraps the generation of the shared object and the activation of reaction loops: `sudo -E ./run.sh ` 60 | 61 | E.g., for `dos.p4r` example 62 | 63 | ``` 64 | # Set SDE variable for each session 65 | # Terminal 1 66 | sudo -E ./launch.sh dos_mantis 67 | # Terminal 2, AFTER the completion of ./launch.sh warming up, around 10s 68 | sudo -E ./run.sh dos_mantis ../out/p4r.c 69 | ``` 70 | 71 | ### Further Questions 72 | 73 | A practical [tutorial](https://github.com/eniac/Mantis/blob/master/tutorial.md) is attached, for more details, please refer to the paper [Mantis: Reactive Programmable Switches](https://dl.acm.org/doi/10.1145/3387514.3405870) (SIGCOMM '20). 74 | 75 | Feel free to post [issues](https://github.com/eniac/Mantis/issues) if any question arises. 76 | -------------------------------------------------------------------------------- /agent/launch.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | usage() { echo "Usage: sudo -E $0 " 1>&2; exit 1; } 4 | 5 | if [ -z $SDE ]; then 6 | echo "ERROR: SDE Environment variable is not set" 7 | exit 1 8 | fi 9 | 10 | if [ $# -lt 1 ]; then 11 | usage 12 | fi 13 | 14 | PROG=mantis_ctl 15 | rm ${PROG} 16 | gcc -I${SDE}/pkgsrc/bf-drivers/include -I${SDE}/install/include -Wall -Wno-missing-field-initializers -Werror -Wshadow -g -O2 -std=c99 -o ${PROG} ${PROG}.c -ldriver -lbfsys -lbfutils -lbf_switchd_lib -lm -ldl -lpthread -L${SDE}/install/lib -Wl,-rpath -Wl,${SDE}/install/lib 17 | 18 | export SDE_INSTALL=$SDE"/install" 19 | sudo ./${PROG} $SDE_INSTALL $SDE_INSTALL/share/p4/targets/tofino/$1.conf -------------------------------------------------------------------------------- /agent/mantis_ctl.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | #include 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | #define MAX_HDL_SIZE 10000 20 | 21 | bool sleep_before = true; 22 | bool loop_flag = false; 23 | void sigusr1_handler(int signum) { 24 | printf("Signal %d\n", signum); 25 | sleep_before = false; 26 | if(loop_flag) { 27 | loop_flag = false; 28 | } 29 | else { 30 | loop_flag = true; 31 | } 32 | } 33 | 34 | char* p4rlib_name = "./context/p4r.so"; 35 | char* pd_prologue_name = "pd_prologue"; 36 | char* pd_dialogue_name = "pd_dialogue"; 37 | 38 | void * p4rlib = NULL; 39 | int (*pd_prologue_hdl)(uint32_t sess_hdl, dev_target_t pipe_mgr_dev_tgt, uint32_t* handlers); 40 | int (*pd_dialogue_hdl)(uint32_t sess_hdl, dev_target_t pipe_mgr_dev_tgt, uint32_t* handlers); 41 | 42 | void load_p4r(){ 43 | printf("Loading...\n"); 44 | if (p4rlib != NULL){ 45 | dlclose(p4rlib); 46 | } 47 | p4rlib = dlopen(p4rlib_name, RTLD_LAZY); 48 | if (!p4rlib){ 49 | printf(".so not loaded\n"); 50 | printf("Tried to open %s\n", p4rlib_name); 51 | char * errorstr = dlerror(); 52 | printf("--%s--\n", errorstr); 53 | exit(1); 54 | } 55 | pd_prologue_hdl = dlsym(p4rlib, pd_prologue_name); 56 | if (!pd_prologue_hdl){ 57 | printf("pd_prologue not loaded\n"); 58 | exit(1); 59 | } 60 | pd_dialogue_hdl = dlsym(p4rlib, pd_dialogue_name); 61 | if (!pd_dialogue_hdl){ 62 | printf("pd_dialogue not loaded\n"); 63 | exit(1); 64 | } 65 | } 66 | 67 | bool redefine_loop = false; 68 | void sigusr2_handler(int signum) { 69 | printf("Signal %d\n", signum); 70 | redefine_loop = true; 71 | } 72 | 73 | void* mantis_thd_func() { 74 | 75 | printf("Launch mantis thread... [PID: %ld]\n", (long)getpid()); 76 | printf("---------------------\n"); 77 | 78 | while(sleep_before) { 79 | usleep(100000); 80 | } 81 | 82 | struct sched_param params; 83 | 84 | params.sched_priority = sched_get_priority_max(SCHED_FIFO); 85 | if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶ms) != 0) { 86 | printf("Failed to set priority\n"); 87 | } 88 | 89 | uint32_t sess_hdl = 0; 90 | uint32_t status_tmp = 0; 91 | status_tmp = pipe_mgr_init(); 92 | if(status_tmp!=0) { 93 | printf("ERROR: Status code: %u\n", status_tmp); 94 | exit(1); 95 | } 96 | status_tmp = pipe_mgr_client_init(&sess_hdl); 97 | if(status_tmp!=0) { 98 | printf("ERROR: Status code: %u\n", status_tmp); 99 | exit(1); 100 | } 101 | dev_target_t pipe_mgr_dev_tgt; 102 | pipe_mgr_dev_tgt.device_id = 0; 103 | pipe_mgr_dev_tgt.dev_pipe_id = 0xffff; 104 | 105 | uint32_t* handlers = (uint32_t*)malloc(sizeof(uint32_t) * MAX_HDL_SIZE); 106 | memset(handlers, 0, sizeof(uint32_t) * MAX_HDL_SIZE); 107 | 108 | load_p4r(); 109 | if(!pd_prologue_hdl(sess_hdl, pipe_mgr_dev_tgt, handlers)) { 110 | printf("Failed initialization\n"); 111 | } 112 | 113 | printf("=== Reaction starts === \n"); 114 | 115 | while(loop_flag) { 116 | if(redefine_loop) { 117 | load_p4r(); 118 | redefine_loop = false; 119 | } 120 | if(!pd_dialogue_hdl(sess_hdl, pipe_mgr_dev_tgt, handlers)) { 121 | printf("Failed dialogue\n"); 122 | break; 123 | } 124 | } 125 | 126 | if (handlers) free(handlers); 127 | 128 | return 0; 129 | } 130 | 131 | int main(int argc, char **argv) { 132 | 133 | struct sigaction sa_usr1; 134 | sa_usr1.sa_handler=&sigusr1_handler; 135 | sa_usr1.sa_flags=0; 136 | if(sigaction(SIGUSR1, &sa_usr1, NULL)!=0) { 137 | exit(1); 138 | } 139 | struct sigaction sa_usr2; 140 | sa_usr2.sa_handler=&sigusr2_handler; 141 | sa_usr2.sa_flags=0; 142 | if(sigaction(SIGUSR2, &sa_usr2, NULL)!=0) { 143 | exit(1); 144 | } 145 | 146 | bf_switchd_context_t *switchd_ctx; 147 | if ((switchd_ctx = (bf_switchd_context_t *) calloc(1, sizeof(bf_switchd_context_t))) == NULL) { 148 | printf("Cannot Allocate switchd context\n"); 149 | exit(1); 150 | } 151 | switchd_ctx->install_dir = argv[1]; 152 | switchd_ctx->conf_file = argv[2]; 153 | // Set to false in case one prefers interactive commands 154 | switchd_ctx->running_in_background = false; 155 | 156 | bf_switchd_lib_init(switchd_ctx); 157 | 158 | pthread_t mantis_thd; 159 | pthread_create(&mantis_thd, NULL, &mantis_thd_func, NULL); 160 | 161 | pthread_join(mantis_thd, NULL); 162 | 163 | return 0; 164 | } 165 | -------------------------------------------------------------------------------- /agent/pi_setup.py: -------------------------------------------------------------------------------- 1 | # Example scripts for program independent set up 2 | 3 | import argparse 4 | import importlib 5 | import json 6 | import os 7 | import signal 8 | import sys 9 | import time 10 | 11 | SDE = os.getenv('SDE') 12 | 13 | sys.path.append(SDE+"/install/lib/python2.7/site-packages/tofino") 14 | import pal_rpc.pal as pal_i 15 | from pal_rpc.ttypes import * 16 | import conn_mgr_pd_rpc.conn_mgr as conn_mgr_i 17 | from res_pd_rpc.ttypes import * 18 | sys.path.append(SDE+"/install/lib/python2.7/site-packages") 19 | # For type conversions 20 | from ptf.thriftutils import * 21 | 22 | from thrift.transport import TSocket 23 | from thrift.transport import TTransport 24 | from thrift.protocol import TBinaryProtocol 25 | from thrift.protocol import TMultiplexedProtocol 26 | 27 | 28 | transport = TSocket.TSocket('localhost', 9090) 29 | transport = TTransport.TBufferedTransport(transport) 30 | bprotocol = TBinaryProtocol.TBinaryProtocol(transport) 31 | transport.open() 32 | 33 | pal_protocol = TMultiplexedProtocol.TMultiplexedProtocol(bprotocol, "pal") 34 | pal = pal_i.Client(pal_protocol) 35 | 36 | try: 37 | 38 | # Example port configuration 39 | pal.pal_port_add(0, 144, pal_port_speed_t.BF_SPEED_10G, pal_fec_type_t.BF_FEC_TYP_NONE) 40 | pal.pal_port_enable(0, 144) 41 | 42 | pal.pal_port_add(0, 155, pal_port_speed_t.BF_SPEED_25G, pal_fec_type_t.BF_FEC_TYP_NONE) 43 | pal.pal_port_enable(0, 155) 44 | 45 | pal.pal_port_add(0, 153, pal_port_speed_t.BF_SPEED_10G, pal_fec_type_t.BF_FEC_TYP_NONE) 46 | pal.pal_port_enable(0, 153) 47 | 48 | pal.pal_port_add(0, 161, pal_port_speed_t.BF_SPEED_10G, pal_fec_type_t.BF_FEC_TYP_NONE) 49 | pal.pal_port_enable(0, 161) 50 | 51 | pal.pal_port_add(0, 163, pal_port_speed_t.BF_SPEED_10G, pal_fec_type_t.BF_FEC_TYP_NONE) 52 | pal.pal_port_enable(0, 163) 53 | print("Port configuration suceeds") 54 | 55 | except: 56 | print("Port configuration fails") -------------------------------------------------------------------------------- /agent/run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | usage() { echo "Usage: sudo -E $0 " 1>&2; exit 1; } 4 | 5 | if [ -z $SDE ]; then 6 | echo "ERROR: SDE Environment variable is not set" 7 | exit 1 8 | fi 9 | 10 | if [ $# -lt 2 ]; then 11 | usage 12 | fi 13 | 14 | name=$(basename $1 .p4) 15 | 16 | export CURR=$(pwd) 17 | export CONTEXT_PATH=$CURR"/context/" 18 | export PATH=$PATH:$CONTEXT_PATH/bin 19 | 20 | set -e 21 | 22 | mkdir -p ${CONTEXT_PATH} 23 | 24 | { echo "======Install stub apis to mantis folder======"; } 2> /dev/null 25 | python ${SDE}/pkgsrc/bf-drivers/pd_api_gen/generate_tofino_pd.py --context_json $SDE/install/share/tofinopd/${name}/context.json -o tmp 26 | sed -i "1s/.*/#include \"pd.h\"/" tmp/src/pd.c 27 | sed -i 's/p4_pd_dev_target_t/dev_target_t/g' tmp/src/pd.c 28 | sed -i 's/p4_pd_dev_target_t/dev_target_t/g' tmp/pd/pd.h 29 | cp tmp/src/pd.c ${CONTEXT_PATH} 30 | cp tmp/pd/pd.h ${CONTEXT_PATH} 31 | rm -rf tmp 32 | 33 | { echo "======Example program independent set up: set up ports for connectivities======"; } 2> /dev/null 34 | python pi_setup.py 35 | 36 | { echo "======Make sure the shared object is update-to-date======"; } 2> /dev/null 37 | cp $2 ${CONTEXT_PATH}"p4r.c" 38 | cd ${CONTEXT_PATH} 39 | gcc -w -c -fPIC p4r.c -std=c99 -o p4r.o -I$SDE/pkgsrc/bf-drivers/include/ 40 | gcc -shared -std=c99 -o p4r.so p4r.o 41 | 42 | { echo "======Trigger prologue======"; } 2> /dev/null 43 | mantis_pid=$(ps -a | grep "mantis_ctl" | tr -s " " | cut -d " " -f 1) 44 | if [ -z ${mantis_pid} ] 45 | then 46 | mantis_pid=$(ps -a | grep "mantis_ctl" | tr -s " " | cut -d " " -f 2) 47 | fi 48 | sudo kill -USR1 ${mantis_pid} 49 | -------------------------------------------------------------------------------- /compile_p4r.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # The script takes p4r code and generates .p4 malleable data plane and .c agent code 4 | 5 | set -e 6 | set -euo pipefail 7 | 8 | 9 | output_path=out/ 10 | verbose=0 11 | 12 | usage() { echo "Usage: $0 [-vv] [-o output_dir] input_file" 1>&2; exit 1; } 13 | while getopts ":o:v" opt 14 | do 15 | case "${opt}" in 16 | o ) output_path=${OPTARG};; 17 | v ) verbose=$(($verbose + 1));; 18 | \? ) usage;; 19 | esac 20 | done 21 | shift $((OPTIND-1)) 22 | 23 | 24 | if [ $# -lt 1 ]; then 25 | usage 26 | fi 27 | 28 | # Parse input file 29 | input_file=$1 30 | infile_prefix=$(basename -- "$input_file") 31 | infile_prefix="${infile_prefix%.*}" 32 | 33 | # Parse output director. Ensures that output_path ends with a slash 34 | [[ "${output_path}" != */ ]] && output_path="${output_path}/" 35 | mkdir -p ${output_path} 36 | 37 | output_base="${output_path}${infile_prefix}" 38 | output_c_fn="${output_base}_mantis.c" 39 | output_p4_fn="${output_base}_mantis.p4" 40 | output_include_fn="${output_base}_mantis.include" 41 | 42 | # Parse verbosity 43 | if [ $verbose -eq 1 ]; then 44 | export PRINT_FLAGS=-DINFO 45 | elif [ $verbose -ge 2 ]; then 46 | export PRINT_FLAGS=-DVERBOSE 47 | fi 48 | 49 | 50 | set -x 51 | 52 | { echo "==============Synthesize .p4 and .c files=============="; } 2> /dev/null 53 | { echo "Output base: ${output_base}"; } 2> /dev/null 54 | make clean 55 | make -j4 56 | ./frontend -i ${input_file} -o ${output_base} 57 | 58 | { echo "==============Run preprocesser and install the agent implementation=============="; } 2> /dev/null 59 | echo "#include \"pd.h\"" >> ${output_include_fn} && cat ${output_include_fn} > ${output_path}"/p4r.c" && g++ -E ${output_c_fn} | sed 's/_MANTIS_NL_\s*;/_MANTIS_NL_/g' | sed 's/_MANTIS_NL_/\n /g' | grep "^[^#]" >> ${output_path}"/p4r.c" 60 | rm ${output_include_fn} ${output_c_fn} 61 | 62 | { echo "==============Compile output p4 to tofino target with p4c=============="; } 2> /dev/null 63 | output_namebase=$(basename ${output_base}) 64 | abs_output_p4_path=$(cd ${output_path}; pwd)"/"${output_namebase}"_mantis.p4" 65 | ./util/p4_14_compile.sh ${abs_output_p4_path} 66 | -------------------------------------------------------------------------------- /examples/dos.p4r: -------------------------------------------------------------------------------- 1 | // A simple use case for dos mitigation 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type ethernet_t { 9 | fields { 10 | dstAddr : 48; 11 | srcAddr : 48; 12 | etherType : 16; 13 | } 14 | } 15 | 16 | header ethernet_t ethernet; 17 | 18 | header_type vlan_tag_t { 19 | fields { 20 | pri : 3; 21 | cfi : 1; 22 | vlan_id : 12; 23 | etherType : 16; 24 | } 25 | } 26 | 27 | header vlan_tag_t vlan_tag; 28 | 29 | header_type ipv4_t { 30 | fields { 31 | version : 4; 32 | ihl : 4; 33 | diffserv : 8; 34 | totalLen : 16; 35 | identification : 16; 36 | flags : 3; 37 | fragOffset : 13; 38 | ttl : 8; 39 | protocol : 8; 40 | hdrChecksum : 16; 41 | srcAddr : 32; 42 | dstAddr : 32; 43 | } 44 | } 45 | 46 | header ipv4_t ipv4; 47 | 48 | header_type udp_t { 49 | fields { 50 | srcPort : 16; 51 | dstPort : 16; 52 | hdr_length : 16; 53 | checksum : 16; 54 | } 55 | } 56 | 57 | header udp_t udp; 58 | 59 | header_type tcp_t { 60 | fields { 61 | srcPort : 16; 62 | dstPort : 16; 63 | seqNo : 32; 64 | ackNo : 32; 65 | dataOffset : 4; 66 | res : 4; 67 | flags : 8; 68 | window : 16; 69 | checksum : 16; 70 | urgentPtr : 16; 71 | } 72 | } 73 | 74 | header tcp_t tcp; 75 | 76 | #define UDP_TYPE 17 77 | #define TCP_TYPE 6 78 | 79 | parser start { 80 | return parse_ethernet; 81 | } 82 | 83 | parser parse_ethernet { 84 | extract(ethernet); 85 | return select(latest.etherType) { 86 | 0x8100 : parse_vlan_tag; 87 | 0x800 : parse_ipv4; 88 | } 89 | } 90 | 91 | parser parse_vlan_tag { 92 | extract(vlan_tag); 93 | return select(latest.etherType) { 94 | 0x800 : parse_ipv4; 95 | default : ingress; 96 | } 97 | } 98 | 99 | parser parse_ipv4 { 100 | extract(ipv4); 101 | return select(latest.fragOffset, latest.protocol) { 102 | UDP_TYPE : parse_udp; 103 | TCP_TYPE : parse_tcp; 104 | default: ingress; 105 | } 106 | } 107 | 108 | parser parse_udp { 109 | extract(udp); 110 | return ingress; 111 | } 112 | 113 | parser parse_tcp { 114 | extract(tcp); 115 | return ingress; 116 | } 117 | 118 | field_list ipv4_field_list { 119 | ipv4.version; 120 | ipv4.ihl; 121 | ipv4.diffserv; 122 | ipv4.totalLen; 123 | ipv4.identification; 124 | ipv4.flags; 125 | ipv4.fragOffset; 126 | ipv4.ttl; 127 | ipv4.protocol; 128 | ipv4.srcAddr; 129 | ipv4.dstAddr; 130 | } 131 | 132 | field_list_calculation ipv4_chksum_calc { 133 | input { 134 | ipv4_field_list; 135 | } 136 | algorithm : csum16; 137 | output_width: 16; 138 | } 139 | 140 | calculated_field ipv4.hdrChecksum { 141 | update ipv4_chksum_calc; 142 | } 143 | 144 | field_list udp_checksum_list { 145 | udp.srcPort; 146 | udp.dstPort; 147 | udp.hdr_length; 148 | payload; 149 | } 150 | 151 | field_list_calculation udp_checksum_calc { 152 | input { 153 | udp_checksum_list; 154 | } 155 | algorithm : csum16; 156 | output_width : 16; 157 | } 158 | 159 | calculated_field udp.checksum { 160 | update udp_checksum_calc; 161 | } 162 | 163 | action ai_nop() { 164 | } 165 | 166 | action ai_set_egr_port(egress_port) { 167 | add_to_field(ipv4.ttl, -1); 168 | modify_field(ig_intr_md_for_tm.ucast_egress_port, egress_port); 169 | } 170 | 171 | action ai_drop_ipv4() { 172 | drop(); 173 | } 174 | 175 | @pragma stage 0 176 | table ti_ipv4_forwarding { 177 | reads { 178 | ipv4.dstAddr : ternary; 179 | ipv4.srcAddr : ternary; 180 | } 181 | actions { 182 | ai_nop; 183 | ai_set_egr_port; 184 | } 185 | size : 1024; 186 | } 187 | 188 | @pragma stage 0 189 | malleable table ti_blocklist { 190 | reads { 191 | ipv4.srcAddr : ternary; 192 | } 193 | actions { 194 | ai_nop; 195 | ai_drop_ipv4; 196 | } 197 | } 198 | 199 | register ri_sample { 200 | width : 64; 201 | instance_count : 1; 202 | } 203 | 204 | blackbox stateful_alu bi_sample{ 205 | reg : ri_sample; 206 | // For source IP 207 | update_hi_1_value : ipv4.srcAddr; 208 | // For packet counters 209 | update_lo_1_value : register_lo + 1; 210 | } 211 | 212 | action ai_sample() { 213 | bi_sample.execute_stateful_alu(0); 214 | } 215 | 216 | @pragma stage 1 217 | table ti_sample { 218 | actions {ai_sample;} 219 | default_action: ai_sample(); 220 | } 221 | 222 | control ingress { 223 | apply(ti_ipv4_forwarding); 224 | apply(ti_blocklist); 225 | apply(ti_sample); 226 | } 227 | 228 | control egress { 229 | } 230 | 231 | // P4R code 232 | init_block my_init { 233 | ti_ipv4_forwarding_add_ai_set_egr_port(0, 0xa01010d, 0xffffffff, 0xa010102, 0x0, 2, 0x9a); 234 | ti_ipv4_forwarding_add_ai_set_egr_port(1, 0xa010103, 0xffffffff, 0xa010102, 0x0, 2, 0x9b); 235 | ti_ipv4_forwarding_add_ai_set_egr_port(2, 0xa010102, 0xffffffff, 0xa010103, 0x0, 2, 0x90); 236 | } 237 | 238 | reaction my_reaction(reg ri_sample){ 239 | // For benchmark only 240 | #include 241 | #include 242 | 243 | #define MAX_FLOW_NUM 100000 244 | // Prevent new flows being detected immediately 245 | #define SPURIOUS_THRESHOLD_US 50 246 | // Packet per second (assume 550B packets and 1Gbps threshold) 247 | #define THRESHOLD_PPS 227272 248 | 249 | static int to_append_id = 0; 250 | 251 | static struct timeval tp; 252 | static long curr_ts; 253 | 254 | static long first_seen_time[MAX_FLOW_NUM] = {0}; 255 | static uint32_t flow_sizes[MAX_FLOW_NUM] = {0}; 256 | static uint32_t src_ips[MAX_FLOW_NUM] = {0}; 257 | static uint32_t prev_count = 0; 258 | static bool is_blocklist[MAX_FLOW_NUM]={false}; 259 | 260 | uint32_t tmp_count = 0; 261 | 262 | static int search_i = 0; 263 | 264 | static int blocklist_entry_id = 3; 265 | 266 | // Packet count diff 267 | tmp_count = ri_sample[0].f1 - prev_count; 268 | 269 | if(tmp_count>0) { 270 | for(search_i=to_append_id-1; search_i>=0; search_i--) { 271 | if(src_ips[search_i]==ri_sample[0].f0) { 272 | break; 273 | } 274 | } 275 | if(search_i==-1) { 276 | flow_sizes[to_append_id] = tmp_count; 277 | src_ips[to_append_id] = ri_sample[0].f0; 278 | 279 | gettimeofday(&tp, NULL); 280 | curr_ts = tp.tv_sec*1000000 + tp.tv_usec; 281 | first_seen_time[to_append_id] = curr_ts; 282 | printf("New source ip: %X\n", ri_sample[0].f0); 283 | 284 | to_append_id += 1; 285 | } 286 | else { 287 | flow_sizes[search_i] += tmp_count; 288 | 289 | gettimeofday(&tp, NULL); 290 | curr_ts = tp.tv_sec*1000000 + tp.tv_usec; 291 | double rate = flow_sizes[search_i]*1000000.0/(curr_ts-first_seen_time[search_i]); 292 | if(rate>THRESHOLD_PPS) { 293 | if(!is_blocklist[search_i]) { 294 | if((curr_ts-first_seen_time[search_i])>=SPURIOUS_THRESHOLD_US) { 295 | printf("Block: %X, latency: %lu us\n", src_ips[search_i], curr_ts-first_seen_time[search_i]); 296 | 297 | ti_blocklist_add_ai_drop_ipv4(10, src_ips[search_i], 0xffffffff, 2); 298 | 299 | gettimeofday(&tp, NULL); 300 | curr_ts = tp.tv_sec*1000000 + tp.tv_usec; 301 | 302 | is_blocklist[search_i] = true; 303 | } 304 | } 305 | } 306 | } 307 | } 308 | 309 | prev_count = ri_sample[0].f1; 310 | 311 | } -------------------------------------------------------------------------------- /examples/failover_tstamp.p4r: -------------------------------------------------------------------------------- 1 | // A simple failover detector that monitors dataplane timestamp and heartbeat packet counts 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type ethernet_t { 9 | fields { 10 | dstAddr : 48; 11 | srcAddr : 48; 12 | etherType : 16; 13 | } 14 | } 15 | 16 | header_type ipv4_t { 17 | fields { 18 | version : 4; 19 | ihl : 4; 20 | diffserv : 8; 21 | totalLen : 16; 22 | identification : 16; 23 | flags : 3; 24 | fragOffset : 13; 25 | ttl : 8; 26 | protocol : 8; 27 | hdrChecksum : 16; 28 | srcAddr : 32; 29 | dstAddr : 32; 30 | } 31 | } 32 | 33 | header_type udp_t { 34 | fields { 35 | srcPort : 16; 36 | dstPort : 16; 37 | hdr_length : 16; 38 | checksum : 16; 39 | } 40 | } 41 | 42 | header ethernet_t ethernet; 43 | header ipv4_t ipv4; 44 | header udp_t udp; 45 | 46 | #define UDP_TYPE 17 47 | 48 | parser start { 49 | return parse_ethernet; 50 | } 51 | 52 | parser parse_ethernet { 53 | extract(ethernet); 54 | return select(latest.etherType) { 55 | 0x800 : parse_ipv4; 56 | } 57 | } 58 | 59 | parser parse_ipv4 { 60 | extract(ipv4); 61 | return select(latest.fragOffset, latest.protocol) { 62 | UDP_TYPE : parse_udp; 63 | default: ingress; 64 | } 65 | } 66 | 67 | parser parse_udp { 68 | extract(udp); 69 | return ingress; 70 | } 71 | 72 | field_list ipv4_field_list { 73 | ipv4.version; 74 | ipv4.ihl; 75 | ipv4.diffserv; 76 | ipv4.totalLen; 77 | ipv4.identification; 78 | ipv4.flags; 79 | ipv4.fragOffset; 80 | ipv4.ttl; 81 | ipv4.protocol; 82 | ipv4.srcAddr; 83 | ipv4.dstAddr; 84 | } 85 | 86 | field_list_calculation ipv4_chksum_calc { 87 | input { 88 | ipv4_field_list; 89 | } 90 | algorithm : csum16; 91 | output_width: 16; 92 | } 93 | 94 | calculated_field ipv4.hdrChecksum { 95 | update ipv4_chksum_calc; 96 | } 97 | 98 | field_list udp_checksum_list { 99 | udp.srcPort; 100 | udp.dstPort; 101 | udp.hdr_length; 102 | payload; 103 | } 104 | 105 | field_list_calculation udp_checksum_calc { 106 | input { 107 | udp_checksum_list; 108 | } 109 | algorithm : csum16; 110 | output_width : 16; 111 | } 112 | 113 | calculated_field udp.checksum { 114 | update udp_checksum_calc; 115 | } 116 | 117 | action ai_nop() { 118 | } 119 | 120 | action ai_set_egr_port(egress_port) { 121 | add_to_field(ipv4.ttl, -1); 122 | modify_field(ig_intr_md_for_tm.ucast_egress_port, egress_port); 123 | } 124 | 125 | action ai_drop_ipv4() { 126 | drop(); 127 | } 128 | 129 | table ti_ipv4_forwarding { 130 | reads { 131 | ipv4.dstAddr : ternary; 132 | ipv4.srcAddr : ternary; 133 | } 134 | actions { 135 | ai_nop; 136 | ai_set_egr_port; 137 | } 138 | size : 1024; 139 | } 140 | 141 | control ingress { 142 | apply(ti_ipv4_forwarding); 143 | apply(ti_inc_pkt_counter); 144 | apply(ti_record_ingress_tstamp); 145 | } 146 | 147 | register ri_ingress_tstamp { 148 | width : 32; 149 | instance_count : 1; 150 | } 151 | 152 | blackbox stateful_alu bi_record_ingress_tstamp { 153 | reg : ri_ingress_tstamp; 154 | 155 | update_lo_1_value : ig_intr_md.ingress_mac_tstamp; 156 | } 157 | 158 | action ai_record_ingress_tstamp(){ 159 | bi_record_ingress_tstamp.execute_stateful_alu(0); 160 | } 161 | 162 | table ti_record_ingress_tstamp { 163 | actions {ai_record_ingress_tstamp;} 164 | default_action: ai_record_ingress_tstamp(); 165 | } 166 | 167 | register ri_pkt_counter { 168 | width : 32; 169 | // 16, 32, 64, 128 170 | instance_count : 128; 171 | } 172 | 173 | blackbox stateful_alu bi_inc_pkt_counter{ 174 | reg : ri_pkt_counter; 175 | 176 | update_lo_1_value : register_lo + 1; 177 | } 178 | 179 | action ai_inc_pkt_counter(){ 180 | // w.l.o.g., if using 181 | // bi_inc_pkt_counter.execute_stateful_alu(ig_intr_md.ingress_port); 182 | // one needs to map the array indices to neighbor device IDs in the reaction 183 | bi_inc_pkt_counter.execute_stateful_alu(0); 184 | } 185 | 186 | table ti_inc_pkt_counter { 187 | actions {ai_inc_pkt_counter;} 188 | default_action: ai_inc_pkt_counter(); 189 | } 190 | 191 | control egress { 192 | } 193 | 194 | init_block my_init { 195 | ti_ipv4_forwarding_add_ai_set_egr_port(0, 0xa01010d, 0xffffffff, 0xa010102, 0x0, 2, 0x9a); 196 | ti_ipv4_forwarding_add_ai_set_egr_port(1, 0xa010103, 0xffffffff, 0xa010102, 0x0, 2, 0x9b); 197 | ti_ipv4_forwarding_add_ai_set_egr_port(2, 0xa010102, 0xffffffff, 0xa010103, 0x0, 2, 0x90); 198 | } 199 | 200 | reaction my_reaction(reg ri_pkt_counter, reg ri_ingress_tstamp[0:1]){ 201 | // For benchmarks only 202 | #include 203 | #include 204 | 205 | // range: 16,32,64,128 206 | #define NUM_PORTS 128 207 | #define ETA 0.2 208 | 209 | int num_ports = NUM_PORTS; 210 | 211 | static int consecutive_k = 2; 212 | int i = 0; 213 | int j = 0; 214 | 215 | static int initialized=0; 216 | 217 | static uint32_t prev_tstamp=0; 218 | 219 | static uint32_t prev_pkt_ctrs[NUM_PORTS]; 220 | static bool is_failure[NUM_PORTS]; 221 | static int num_failed_confirm[NUM_PORTS]; 222 | if(initialized==0) { 223 | for(i = 0; iprev_tstamp) { 240 | delta = (unsigned int)(ETA*(ri_ingress_tstamp[0]-prev_tstamp)/1000); 241 | } else { 242 | delta = 10000000; // No packet comming in, considered failed so a large delta 243 | } 244 | 245 | prev_tstamp = ri_ingress_tstamp[0]; 246 | 247 | for(i=0; iprev_pkt_ctrs[i] && ri_pkt_counter[i]-prev_pkt_ctrs[i]>=delta) { 249 | is_failure[i] = false; 250 | num_failed_confirm[i] = 0; 251 | } 252 | else { 253 | if(!is_failure[i]) { 254 | num_failed_confirm[i] += 1; 255 | if(num_failed_confirm[i]>=consecutive_k) { 256 | // Reroute if the connectivity failure is detected to the stable link, artibrary logic is allowed 257 | is_failure[i] = true; 258 | ti_ipv4_forwarding_mod_ai_set_egr_port(1, 0x9a); 259 | 260 | gettimeofday(&tp, NULL); 261 | curr_ts = tp.tv_sec*1000000 + tp.tv_usec; 262 | printf ( "[%d] %d<%d reroute: %lu \n" , i , ri_pkt_counter[i]-prev_pkt_ctrs[i], delta, curr_ts ) ; 263 | } 264 | } 265 | } 266 | prev_pkt_ctrs[i] = ri_pkt_counter[i]; 267 | } 268 | } -------------------------------------------------------------------------------- /examples/field_arg.p4r: -------------------------------------------------------------------------------- 1 | // A simple example showing the usage of field argument in reaction 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type my_header_t { 9 | fields { 10 | foo : 8; 11 | bar : 8; 12 | baz : 32; 13 | } 14 | } 15 | 16 | header my_header_t hdr; 17 | 18 | parser start { 19 | return parse_header; 20 | } 21 | 22 | parser parse_header { 23 | extract(hdr); 24 | return ingress; 25 | } 26 | 27 | control ingress { 28 | apply(my_table); 29 | } 30 | 31 | table my_table { 32 | reads { 33 | hdr.foo : exact; 34 | } 35 | actions { my_action;} 36 | } 37 | 38 | action my_action(baz) { 39 | modify_field(hdr.bar, baz); 40 | } 41 | 42 | control egress { 43 | } 44 | 45 | // P4R code 46 | reaction my_reaction(ing hdr.foo, ing hdr.bar, ing hdr.baz) { 47 | #include 48 | printf("Print meas %x, %x, %x\n", hdr.foo, hdr.bar, hdr.baz); 49 | } 50 | -------------------------------------------------------------------------------- /examples/figure1.p4r: -------------------------------------------------------------------------------- 1 | // A simple example in Figure 1 of the paper 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type my_header_t { 9 | fields { 10 | foo : 8; 11 | bar : 8; 12 | baz : 32; 13 | } 14 | } 15 | header my_header_t hdr; 16 | 17 | parser start { 18 | return parse_header; 19 | } 20 | parser parse_header { 21 | extract(hdr); 22 | return ingress; 23 | } 24 | 25 | control ingress { 26 | apply(table_var); 27 | } 28 | 29 | action drop() { 30 | } 31 | 32 | register re_qdepths { 33 | width : 32; 34 | instance_count : 128; 35 | } 36 | 37 | blackbox stateful_alu be_update_qdepths { 38 | reg : re_qdepths; 39 | update_lo_1_value : eg_intr_md.enq_qdepth; 40 | } 41 | 42 | action ae_update_qdepths() { 43 | be_update_qdepths.execute_stateful_alu(eg_intr_md.egress_port); 44 | } 45 | 46 | table te_update_qdepths { 47 | actions {ae_update_qdepths;} 48 | default_action: ae_update_qdepths(); 49 | } 50 | 51 | control egress { 52 | apply(te_update_qdepths); 53 | } 54 | 55 | // P4R code 56 | malleable value value_var { 57 | width : 16; 58 | init : 1; 59 | } 60 | 61 | malleable field field_var { 62 | width : 32; 63 | init : hdr.foo; 64 | alts { hdr.foo, hdr.bar } 65 | } 66 | 67 | table table_var { 68 | reads { ${field_var} : exact; } 69 | actions { my_action; drop; } 70 | } 71 | 72 | action my_action() { 73 | add(${field_var}, hdr.baz, ${value_var}); 74 | } 75 | 76 | reaction my_reaction(reg re_qdepths[1:10]) { 77 | uint16_t current_max = 0, max_port = 0; 78 | for (int i = 1; i <= 10; ++i) { 79 | if (re_qdepths[i] > current_max) { 80 | current_max = re_qdepths[i]; 81 | max_port = i; 82 | } 83 | } 84 | ${value_var} = max_port; 85 | } -------------------------------------------------------------------------------- /examples/figure4.p4r: -------------------------------------------------------------------------------- 1 | // A simple example in Figure 4 of the paper 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type my_header_t { 9 | fields { 10 | foo : 8; 11 | bar : 8; 12 | } 13 | } 14 | header my_header_t hdr; 15 | 16 | parser start { 17 | return parse_header; 18 | } 19 | 20 | parser parse_header { 21 | extract(hdr); 22 | return ingress; 23 | } 24 | 25 | control ingress { 26 | apply(table_foo); 27 | } 28 | 29 | // P4R code 30 | 31 | malleable value value_var { 32 | width : 16; 33 | init : 1; 34 | } 35 | 36 | table table_foo { 37 | reads { hdr.foo : exact; } 38 | actions { my_action; drop; } 39 | } 40 | 41 | action my_action() { 42 | add(hdr.foo, hdr.bar, ${value_var}); 43 | } 44 | 45 | action drop() { 46 | } 47 | 48 | control egress { 49 | } 50 | 51 | reaction my_reaction() { 52 | static bool flip = true; 53 | if(flip) { 54 | ${value_var} = 2; 55 | flip = false; 56 | } else { 57 | ${value_var} = 3; 58 | flip = true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/figure5.p4r: -------------------------------------------------------------------------------- 1 | // A simple example in Figure 5 of the paper 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type my_header_t { 9 | fields { 10 | foo : 32; 11 | bar : 32; 12 | qux : 32; 13 | } 14 | } 15 | header my_header_t hdr; 16 | 17 | 18 | parser start { 19 | return parse_header; 20 | } 21 | parser parse_header { 22 | extract(hdr); 23 | return ingress; 24 | } 25 | 26 | control ingress { 27 | apply(my_table); 28 | } 29 | 30 | action drop() { 31 | } 32 | 33 | control egress { 34 | } 35 | 36 | table my_table { 37 | reads { 38 | hdr.qux : exact; 39 | } 40 | actions { my_action; drop; } 41 | } 42 | 43 | // P4R code 44 | malleable field write_var { 45 | width : 32; 46 | init : hdr.foo; 47 | alts { hdr.foo, hdr.bar } 48 | } 49 | 50 | action my_action(baz) { 51 | modify_field(${write_var}, baz); 52 | } 53 | -------------------------------------------------------------------------------- /examples/figure6.p4r: -------------------------------------------------------------------------------- 1 | // A simple example in Figure 6 of the paper 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type my_header_t { 9 | fields { 10 | foo : 32; 11 | bar : 32; 12 | baz : 32; 13 | qux : 32; 14 | } 15 | } 16 | header my_header_t hdr; 17 | 18 | parser start { 19 | return parse_header; 20 | } 21 | parser parse_header { 22 | extract(hdr); 23 | return ingress; 24 | } 25 | 26 | control ingress { 27 | apply(my_table); 28 | } 29 | 30 | control egress { 31 | } 32 | 33 | action drop() { 34 | } 35 | 36 | // P4R code 37 | malleable field read_var { 38 | width : 32; 39 | init : hdr.foo; 40 | alts { hdr.foo, hdr.bar } 41 | } 42 | 43 | table my_table { 44 | reads { ${read_var} : exact; } 45 | actions { my_action; drop; } 46 | } 47 | 48 | action my_action() { 49 | add(hdr.qux, hdr.baz, ${read_var}); 50 | } 51 | -------------------------------------------------------------------------------- /examples/mbl_table.p4r: -------------------------------------------------------------------------------- 1 | // A simple example of mbl table manipulations 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type ethernet_t { 9 | fields { 10 | dstAddr :48; 11 | srcAddr : 48; 12 | etherType : 16; 13 | } 14 | } 15 | 16 | header_type ipv4_t { 17 | fields { 18 | version : 4; 19 | ihl : 4; 20 | diffserv : 8; 21 | totalLen : 16; 22 | identification : 16; 23 | flags : 3; 24 | fragOffset : 13; 25 | ttl : 8; 26 | protocol : 8; 27 | hdrChecksum : 16; 28 | srcAddr : 32; 29 | dstAddr : 32; 30 | } 31 | } 32 | 33 | header_type udp_t { 34 | fields { 35 | srcPort : 16; 36 | dstPort : 16; 37 | hdr_length : 16; 38 | checksum : 16; 39 | } 40 | } 41 | 42 | header ethernet_t ethernet; 43 | header ipv4_t ipv4; 44 | header udp_t udp; 45 | 46 | #define UDP_TYPE 17 47 | 48 | parser start { 49 | return parse_ethernet; 50 | } 51 | 52 | parser parse_ethernet { 53 | extract(ethernet); 54 | return select(latest.etherType) { 55 | 0x800 : parse_ipv4; 56 | } 57 | } 58 | 59 | parser parse_ipv4 { 60 | extract(ipv4); 61 | return select(latest.fragOffset, latest.protocol) { 62 | UDP_TYPE : parse_udp; 63 | default: ingress; 64 | } 65 | } 66 | 67 | parser parse_udp { 68 | extract(udp); 69 | return ingress; 70 | } 71 | 72 | action ai_nop() { 73 | } 74 | 75 | action ai_set_egr_port(egress_port) { 76 | modify_field(ig_intr_md_for_tm.ucast_egress_port, egress_port); 77 | } 78 | 79 | action ai_drop_ipv4() { 80 | drop(); 81 | } 82 | 83 | malleable table ti_var_table { 84 | reads { 85 | ipv4.dstAddr : ternary; 86 | } 87 | actions { 88 | ai_nop; 89 | ai_set_egr_port; 90 | } 91 | size : 512; 92 | } 93 | 94 | control ingress { 95 | apply(ti_var_table); 96 | } 97 | 98 | control egress { 99 | } 100 | 101 | // P4R code 102 | init_block global_init { 103 | // Same syntax as if non-mbl table, Mantis handles the isolation underneath 104 | ti_var_table_add_ai_set_egr_port(0, 0xa01010d, 0xffffffff, 0x1, 0x90); 105 | ti_var_table_add_ai_set_egr_port(1, 0xa01010d, 0xffffffff, 0x1, 0x90); 106 | } 107 | 108 | reaction my_reaction() { 109 | 110 | static bool flip = true; 111 | 112 | if (flip) { 113 | ti_var_table_mod_ai_set_egr_port(1, 0x89); 114 | flip = false; 115 | } else { 116 | ti_var_table_mod_ai_set_egr_port(1, 0x90); 117 | flip = true; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /examples/table_add_del_mod.p4r: -------------------------------------------------------------------------------- 1 | // A simple example showing the syntax of add/delete/modify non-mbl table entries in prologue 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | header_type ethernet_t { 9 | fields { 10 | dstAddr :48; 11 | srcAddr : 48; 12 | etherType : 16; 13 | } 14 | } 15 | 16 | header_type ipv4_t { 17 | fields { 18 | version : 4; 19 | ihl : 4; 20 | diffserv : 8; 21 | totalLen : 16; 22 | identification : 16; 23 | flags : 3; 24 | fragOffset : 13; 25 | ttl : 8; 26 | protocol : 8; 27 | hdrChecksum : 16; 28 | srcAddr : 32; 29 | dstAddr : 32; 30 | } 31 | } 32 | 33 | header_type udp_t { 34 | fields { 35 | srcPort : 16; 36 | dstPort : 16; 37 | hdr_length : 16; 38 | checksum : 16; 39 | } 40 | } 41 | 42 | header ethernet_t ethernet; 43 | header ipv4_t ipv4; 44 | header udp_t udp; 45 | 46 | #define UDP_TYPE 17 47 | 48 | parser start { 49 | return parse_ethernet; 50 | } 51 | 52 | parser parse_ethernet { 53 | extract(ethernet); 54 | return select(latest.etherType) { 55 | 0x800 : parse_ipv4; 56 | } 57 | } 58 | 59 | parser parse_ipv4 { 60 | extract(ipv4); 61 | return select(latest.fragOffset, latest.protocol) { 62 | UDP_TYPE : parse_udp; 63 | default: ingress; 64 | } 65 | } 66 | 67 | parser parse_udp { 68 | extract(udp); 69 | return ingress; 70 | } 71 | 72 | action ai_nop() { 73 | } 74 | 75 | action ai_set_egr_port(egress_port) { 76 | modify_field(ig_intr_md_for_tm.ucast_egress_port, egress_port); 77 | } 78 | 79 | action ai_drop_ipv4() { 80 | drop(); 81 | } 82 | 83 | table ti_ipv4_forwarding { 84 | reads { 85 | ipv4.dstAddr : ternary; 86 | ipv4.srcAddr : ternary; 87 | } 88 | actions { 89 | ai_nop; 90 | ai_set_egr_port; 91 | } 92 | size : 1024; 93 | } 94 | 95 | control ingress { 96 | apply(ti_ipv4_forwarding); 97 | } 98 | 99 | control egress { 100 | } 101 | 102 | // P4R code 103 | init_block global_init { 104 | // One could manipulate non-mbl table in prologue to set up static rules 105 | // Note that Mantis also supports reconfiguration of non-mbl table entries in dialogue, but isolation mechanism is not provided. One may still use it in prologue when it is certain at most 1 update will be activated per dialogue to save the isolation overhead. 106 | 107 | // Add a simple entry with index 0 and corresponding match arguments, priority (since ternary) and action arguments 108 | ti_ipv4_forwarding_add_ai_set_egr_port(0, 0xa01010d, 0xffffffff, 0xa010102, 0x0, 0x1, 0x90); 109 | // Modify the entry 0 with another egress port 110 | ti_ipv4_forwarding_mod_ai_set_egr_port(0, 0x90); 111 | // Delete entry 0 112 | ti_ipv4_forwarding_del_ai_set_egr_port(0); 113 | 114 | // Add new entry 1 115 | ti_ipv4_forwarding_add_ai_set_egr_port(1, 0xa01010e, 0xffffffff, 0xa010102, 0x0, 0x1, 0x90); 116 | } 117 | -------------------------------------------------------------------------------- /frontend.l: -------------------------------------------------------------------------------- 1 | %option noyywrap 2 | %option yylineno 3 | 4 | /* Currently only support Tofino P4_14 */ 5 | %s BMV2_S TOFINO_S 6 | 7 | %{ 8 | #include 9 | #include 10 | #include "include/ast_nodes.h" // Must be before tab.h 11 | #include "include/ast_nodes_p4.h" 12 | #include "include/ast_nodes_p4r.h" 13 | #include "frontend.tab.h" 14 | 15 | %} 16 | 17 | 18 | %% 19 | %{ 20 | // Copied verbatim at the very front of yylex() 21 | extern int with_tofino; 22 | extern int done_init; 23 | if (!done_init) 24 | { 25 | if(with_tofino) { 26 | BEGIN TOFINO_S; 27 | done_init=1; // necessary 28 | return START_TOFINO; 29 | } else { 30 | BEGIN BMV2_S; 31 | done_init=1; 32 | return START_BMV2; 33 | } 34 | } 35 | %} 36 | 37 | "#".* { 38 | /* Includes are just copied to output */ 39 | // Currently assumes no p4r include 40 | yylval.sval = strdup(yytext); 41 | return INCLUDE; 42 | } 43 | 44 | "@".* { 45 | /* For tofino compiler pragma only */ 46 | yylval.sval = strdup(yytext); 47 | return PRAGMA; 48 | } 49 | 50 | "//".* { /* comments,do nothing */ } 51 | [/][*][^*]*[*]+([^*/][^*]*[*]+)*[/] { /* comments,do nothing */ } 52 | [ \t\r]+ { /* whitespace*/ } 53 | [\n] { /* newline */ } 54 | 55 | /* Reserved characters */ 56 | "{" {return L_BRACE;} 57 | "}" {return R_BRACE;} 58 | "(" {return L_PAREN;} 59 | ")" {return R_PAREN;} 60 | ";" {return SEMICOLON;} 61 | ":" {return COLON;} 62 | "," {return COMMA;} 63 | "." {return PERIOD;} 64 | "$" {return DOLLAR;} 65 | "[" {return L_BRACKET;} 66 | "]" {return R_BRACKET;} 67 | "/" {return SLASH;} 68 | 69 | /* Reserved words - variables */ 70 | "malleable" {return P4R_MALLEABLE;} 71 | "table" {return TABLE;} 72 | "value" {return VALUE;} 73 | "field" {return FIELD;} 74 | "width" {return WIDTH;} 75 | "init" {return INIT;} 76 | "alts" {return ALTS;} 77 | "reads" {return READS;} 78 | "actions" {return ACTIONS;} 79 | 80 | /* Reserved words - initialization */ 81 | "init_block" {return P4R_INIT_BLOCK;} 82 | 83 | "register" {return REGISTER;} 84 | /* Reserved words - reactions */ 85 | "reaction" {return P4R_REACTION;} 86 | /* Note that tofino blackbox also uses reg as keyword */ 87 | "reg" {return REACTION_ARG_REG;} 88 | "ing" {return REACTION_ARG_ING;} 89 | "egr" {return REACTION_ARG_EGR;} 90 | 91 | /* Reserved words - P4 */ 92 | "header" {return HEADER;} 93 | "metadata" {return METADATA;} 94 | "header_type" {return HEADER_TYPE;} 95 | "fields" {return FIELDS;} 96 | "exact" {return EXACT;} 97 | "ternary" {return TERNARY;} 98 | "action" {return ACTION;} 99 | 100 | /* Parsed identifier word in P4 code. */ 101 | [A-Za-z_][A-Za-z0-9_]* { 102 | yylval.sval = strdup(yytext); 103 | return IDENTIFIER; 104 | } 105 | 106 | /* Integer */ 107 | [-]?[0-9]+ { 108 | yylval.sval = strdup(yytext); 109 | return INTEGER; 110 | } 111 | 112 | [^{}\/()\[\]:;,\.$ \t\n]+ { 113 | yylval.sval = strdup(yytext); 114 | return STRING; 115 | } 116 | 117 | 118 | %% -------------------------------------------------------------------------------- /frontend.y: -------------------------------------------------------------------------------- 1 | // C declarations 2 | %{ 3 | #define YYDEBUG 1 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "include/ast_nodes.h" 18 | #include "include/ast_nodes_p4.h" 19 | #include "include/ast_nodes_p4r.h" 20 | #include "include/compile.h" 21 | #include "include/find_nodes.h" 22 | #include "include/helper.h" 23 | 24 | // Only support tofino target 25 | int with_tofino=1; 26 | int done_init=0; 27 | 28 | extern int yylex(); 29 | extern int yyparse(); 30 | extern FILE* yyin; 31 | extern int yylineno; 32 | extern char* yytext; 33 | void yyerror(const char* s); 34 | 35 | using namespace std; 36 | 37 | char* in_fn = NULL; 38 | char* out_fn_base = NULL; 39 | std::string p4_out_fn, c_out_fn; 40 | FILE* in_file; 41 | 42 | // From: https://stackoverflow.com/questions/865668/how-to-parse-command-line-arguments-in-c 43 | char* getCmdOption(char ** begin, char ** end, const std::string & option) 44 | { 45 | char ** itr = std::find(begin, end, option); 46 | if (itr != end && ++itr != end) 47 | { 48 | return *itr; 49 | } 50 | return 0; 51 | } 52 | 53 | bool cmdOptionExists(char** begin, char** end, const std::string& option) 54 | { 55 | return std::find(begin, end, option) != end; 56 | } 57 | 58 | void parseArgs(int argc, char* argv[]){ 59 | in_fn = getCmdOption(argv, argv+argc, "-i"); 60 | out_fn_base = getCmdOption(argv, argv+argc, "-o"); 61 | if ((in_fn == NULL) || (out_fn_base == NULL)){ 62 | cout << "expected arguments: " 63 | << argv[0] 64 | << " -i -o " 65 | << endl; 66 | exit(0); 67 | } 68 | 69 | in_file = fopen(in_fn, "r"); 70 | if (in_file == 0) { 71 | PANIC("Input P4R file not found"); 72 | } 73 | 74 | p4_out_fn = string(string(out_fn_base) + string("_mantis.p4")); 75 | c_out_fn = string(string(out_fn_base) + string("_mantis.c")); 76 | } 77 | 78 | std::vector node_array; 79 | AstNode* root; 80 | %} 81 | 82 | %union { 83 | AstNode* aval; 84 | std::string* pval; 85 | char* sval; 86 | } 87 | 88 | // One could extend tofino-specifc P4 syntax to support other variants by branching 89 | // bmv2 target currently not supported 90 | %token START_TOFINO START_BMV2 91 | 92 | // Parsed tokens 93 | %token L_BRACE "{" 94 | %token R_BRACE "}" 95 | %token L_PAREN "(" 96 | %token R_PAREN ")" 97 | %token SEMICOLON ";" 98 | %token COLON ":" 99 | %token COMMA "," 100 | %token PERIOD "." 101 | %token DOLLAR "$" 102 | %token L_BRACKET "[" 103 | %token R_BRACKET "]" 104 | %token SLASH "/" 105 | 106 | // Parsed keywords 107 | %token P4R_MALLEABLE 108 | %token TABLE 109 | %token VALUE 110 | %token FIELD 111 | %token WIDTH 112 | %token INIT 113 | %token ALTS 114 | %token READS 115 | %token EXACT 116 | %token TERNARY 117 | %token ACTIONS 118 | %token HEADER_TYPE 119 | %token HEADER 120 | %token METADATA 121 | %token FIELDS 122 | %token ACTION 123 | 124 | %token BLACKBOX 125 | 126 | %token P4R_INIT_BLOCK 127 | 128 | %token P4R_REACTION 129 | %token REGISTER 130 | %token REACTION_ARG_REG 131 | %token REACTION_ARG_ING 132 | %token REACTION_ARG_EGR 133 | 134 | // Parsed words 135 | %token INCLUDE 136 | %token PRAGMA 137 | %token IDENTIFIER 138 | %token STRING 139 | %token INTEGER 140 | 141 | 142 | %type rootTofino; 143 | %type rootBmv2; 144 | 145 | // Nonterminals 146 | %type inputTofino 147 | %type inputBmv2 148 | %type p4ExprTofino 149 | %type p4ExprBmv2 150 | %type p4rExpr 151 | %type include 152 | %type nameList 153 | %type keyWord 154 | %type body 155 | %type opts 156 | %type p4rMalleable 157 | %type varWidth 158 | %type varValueInit 159 | %type varFieldInit 160 | %type varAlts 161 | %type fieldList 162 | %type field 163 | %type varRef 164 | %type bodyWord 165 | %type p4rInitBlock 166 | %type p4rReaction 167 | %type reactionArgs 168 | %type reactionArg 169 | 170 | %type registerDecl 171 | 172 | %type tableDecl 173 | %type tableReads 174 | %type tableReadStmt 175 | %type tableReadStmts 176 | %type tableActions 177 | %type tableActionStmt 178 | %type tableActionStmts 179 | 180 | %type headerTypeDeclaration 181 | %type headerDecBody 182 | %type headerInstance 183 | %type metadataInstance 184 | %type fieldDec 185 | %type fieldDecList 186 | 187 | %type actionFunctionDeclaration 188 | %type actionParamList 189 | %type actionParam 190 | %type actionStatements 191 | %type actionStatement 192 | %type argList 193 | %type arg 194 | 195 | // Leaves 196 | %type name 197 | %type specialChar 198 | %type integer 199 | 200 | %type includes 201 | 202 | // Define start state 203 | %start root 204 | 205 | 206 | %% 207 | /*===================================== 208 | = GRAMMAR = 209 | =====================================*/ 210 | 211 | root: 212 | START_TOFINO rootTofino 213 | | START_BMV2 rootBmv2 214 | ; 215 | 216 | 217 | rootTofino : 218 | inputTofino { 219 | root = $1; 220 | } 221 | ; 222 | 223 | rootBmv2 : 224 | inputBmv2 { 225 | root = $1; 226 | } 227 | 228 | inputTofino : 229 | /* epsilon */ { 230 | $$=NULL; 231 | } 232 | | inputTofino include { 233 | AstNode* rv = new InputNode($1, $2); 234 | node_array.push_back(rv); 235 | $$=rv; 236 | } 237 | | inputTofino p4rExpr { 238 | AstNode* rv = new InputNode($1, $2); 239 | node_array.push_back(rv); 240 | $$=rv; 241 | PRINT_VERBOSE("----- parsed P4R Expr ------ \n"); 242 | PRINT_VERBOSE("%s\n", $2 -> toString().c_str()); 243 | PRINT_VERBOSE("---------------------------\n"); 244 | } 245 | | inputTofino p4ExprTofino { 246 | AstNode* rv = new InputNode($1, $2); 247 | node_array.push_back(rv); 248 | $$=rv; 249 | PRINT_VERBOSE("----- parsed P4 Expr ------ \n"); 250 | PRINT_VERBOSE("%s\n", $2 -> toString().c_str()); 251 | PRINT_VERBOSE("---------------------------\n"); 252 | } 253 | ; 254 | 255 | inputBmv2 : 256 | /* epsilon */ { 257 | $$=NULL; 258 | } 259 | | inputBmv2 include { 260 | AstNode* rv = new InputNode($1, $2); 261 | node_array.push_back(rv); 262 | $$=rv; 263 | } 264 | | inputBmv2 p4rExpr { 265 | AstNode* rv = new InputNode($1, $2); 266 | node_array.push_back(rv); 267 | $$=rv; 268 | PRINT_VERBOSE("- Parsed REACTIVE P4 Expr -- \n"); 269 | PRINT_VERBOSE("%s\n", $2 -> toString().c_str()); 270 | PRINT_VERBOSE("---------------------------\n"); 271 | } 272 | | inputBmv2 p4ExprBmv2 { 273 | AstNode* rv = new InputNode($1, $2); 274 | node_array.push_back(rv); 275 | $$=rv; 276 | PRINT_VERBOSE("----- parsed P4 Expr ------ \n"); 277 | PRINT_VERBOSE("%s\n", $2 -> toString().c_str()); 278 | PRINT_VERBOSE("---------------------------\n"); 279 | } 280 | ; 281 | 282 | // Include statements 283 | include : 284 | INCLUDE { 285 | string* strVal = new string(string($1)); 286 | AstNode* rv = new IncludeNode(strVal, IncludeNode::P4); 287 | node_array.push_back(rv); 288 | $$=rv; 289 | free($1); 290 | } 291 | ; 292 | 293 | 294 | /*===================================== 295 | = P4 expressions = 296 | =====================================*/ 297 | 298 | registerDecl : 299 | REGISTER name "{" body "}" { 300 | AstNode* rv = new P4RegisterNode($2, $4); 301 | node_array.push_back(rv); 302 | $$=rv; 303 | } 304 | ; 305 | 306 | tableDecl : 307 | TABLE name "{" tableReads tableActions body "}" { 308 | auto rv = new TableNode($2, $4, $5, 309 | $6->toString(), ""); 310 | node_array.push_back(rv); 311 | $$=rv; 312 | } 313 | | TABLE name "{" tableActions body "}" { 314 | // Reads are optional 315 | auto rv = new TableNode($2, NULL, $4, 316 | $5->toString(), ""); 317 | node_array.push_back(rv); 318 | $$=rv; 319 | } 320 | | PRAGMA TABLE name "{" tableReads tableActions body "}" { 321 | auto rv = new TableNode($3, $5, $6, 322 | $7->toString(), $1); 323 | node_array.push_back(rv); 324 | $$=rv; 325 | } 326 | | PRAGMA TABLE name "{" tableActions body "}" { 327 | auto rv = new TableNode($3, NULL, $5, 328 | $6->toString(), $1); 329 | node_array.push_back(rv); 330 | $$=rv; 331 | } 332 | ; 333 | 334 | tableReads : 335 | READS "{" tableReadStmts "}" { 336 | $$=$3; 337 | } 338 | ; 339 | 340 | tableReadStmts : 341 | /* empty */ { 342 | $$=new TableReadStmtsNode(); 343 | } 344 | | tableReadStmts tableReadStmt { 345 | TableReadStmtsNode* rv = dynamic_cast($1); 346 | TableReadStmtNode* trs = dynamic_cast($2); 347 | rv->push_back(trs); 348 | node_array.push_back(rv); 349 | $$=rv; 350 | } 351 | ; 352 | 353 | tableReadStmt : 354 | field ":" EXACT ";" { 355 | AstNode* rv = new TableReadStmtNode(TableReadStmtNode::EXACT, $1); 356 | node_array.push_back(rv); 357 | $$=rv; 358 | } 359 | | field ":" TERNARY ";" { 360 | AstNode* rv = new TableReadStmtNode(TableReadStmtNode::TERNARY, $1); 361 | node_array.push_back(rv); 362 | $$=rv; 363 | } 364 | | varRef ":" EXACT ";" { 365 | AstNode* rv = new TableReadStmtNode(TableReadStmtNode::EXACT, $1); 366 | node_array.push_back(rv); 367 | $$=rv; 368 | } 369 | | varRef ":" TERNARY ";" { 370 | AstNode* rv = new TableReadStmtNode(TableReadStmtNode::TERNARY, $1); 371 | node_array.push_back(rv); 372 | $$=rv; 373 | } 374 | ; 375 | 376 | tableActions : 377 | ACTIONS "{" tableActionStmts "}" { 378 | $$=$3; 379 | } 380 | ; 381 | 382 | tableActionStmts : 383 | /* empty */ { 384 | $$=new TableActionStmtsNode(); 385 | } 386 | | tableActionStmts tableActionStmt { 387 | TableActionStmtsNode* rv = dynamic_cast($1); 388 | TableActionStmtNode* tas = dynamic_cast($2); 389 | rv->push_back(tas); 390 | node_array.push_back(rv); 391 | $$=rv; 392 | } 393 | ; 394 | 395 | tableActionStmt : 396 | name ";" { 397 | AstNode* rv = new TableActionStmtNode($1); 398 | node_array.push_back(rv); 399 | $$=rv; 400 | } 401 | ; 402 | 403 | headerTypeDeclaration : 404 | HEADER_TYPE name "{" headerDecBody body "}" { 405 | AstNode* rv = new HeaderTypeDeclarationNode($2, $4, $5); 406 | node_array.push_back(rv); 407 | $$=rv; 408 | } 409 | ; 410 | 411 | headerDecBody : 412 | FIELDS "{" fieldDecList fieldDec "}" { 413 | FieldDecsNode* rv = dynamic_cast($3); 414 | FieldDecNode* fd = dynamic_cast($4); 415 | rv->push_back(fd); 416 | $$=rv; 417 | } 418 | ; 419 | 420 | fieldDec : 421 | name ":" integer ";" { 422 | AstNode* rv = new FieldDecNode($1, $3); 423 | $$=rv; 424 | } 425 | ; 426 | 427 | fieldDecList : 428 | /* empty */ { 429 | $$=new FieldDecsNode(); 430 | } 431 | | fieldDecList fieldDec { 432 | FieldDecsNode* rv = dynamic_cast($1); 433 | FieldDecNode* fd = dynamic_cast($2); 434 | rv->push_back(fd); 435 | $$=rv; 436 | } 437 | ; 438 | 439 | headerInstance : 440 | HEADER name name ";" { 441 | HeaderInstanceNode* rv = new HeaderInstanceNode($2, $3); 442 | node_array.push_back(rv); 443 | $$=rv; 444 | } 445 | ; 446 | 447 | metadataInstance : 448 | METADATA name name ";" { 449 | MetadataInstanceNode* rv = new MetadataInstanceNode($2, $3); 450 | node_array.push_back(rv); 451 | $$=rv; 452 | } 453 | ; 454 | 455 | actionFunctionDeclaration : 456 | ACTION name "(" actionParamList ")" "{" actionStatements "}" { 457 | ActionNode* rv = new ActionNode($2, $4, $7); 458 | node_array.push_back(rv); 459 | $$=rv; 460 | } 461 | ; 462 | 463 | actionParamList : 464 | /* empty */ { 465 | $$=new ActionParamsNode(); 466 | } 467 | | actionParam { 468 | ActionParamsNode* rv = new ActionParamsNode(); 469 | ActionParamNode* ap = dynamic_cast($1); 470 | rv->push_back(ap); 471 | $$=rv; 472 | } 473 | | actionParamList "," actionParam { 474 | ActionParamsNode* rv = dynamic_cast($1); 475 | ActionParamNode* ap = dynamic_cast($3); 476 | rv->push_back(ap); 477 | $$=rv; 478 | } 479 | ; 480 | 481 | actionParam : 482 | field { 483 | AstNode* rv = new ActionParamNode($1); 484 | node_array.push_back(rv); 485 | $$=rv; 486 | } 487 | | varRef { 488 | AstNode* rv = new ActionParamNode($1); 489 | node_array.push_back(rv); 490 | $$=rv; 491 | } 492 | | name { 493 | AstNode* rv = new ActionParamNode($1); 494 | node_array.push_back(rv); 495 | $$=rv; 496 | } 497 | ; 498 | 499 | actionStatements : 500 | /* empty */ { 501 | AstNode* rv = new ActionStmtsNode(); 502 | node_array.push_back(rv); 503 | $$=rv; 504 | } 505 | | actionStatements actionStatement { 506 | ActionStmtsNode* rv = dynamic_cast($1); 507 | ActionStmtNode* as = dynamic_cast($2); 508 | rv->push_back(as); 509 | $$=rv; 510 | } 511 | ; 512 | 513 | actionStatement : 514 | name "(" argList ")" ";" { 515 | ActionStmtNode* rv = new ActionStmtNode($1, $3, ActionStmtNode::NAME_ARGLIST, NULL, NULL); 516 | node_array.push_back(rv); 517 | $$=rv; 518 | } 519 | /* e.g., bi.execute_stateful_alu(eg_intr_md.egress_port) or index */ 520 | | name "." name "(" argList ")" ";" { 521 | ActionStmtNode* rv = new ActionStmtNode($1, $5, ActionStmtNode::PROG_EXEC, $3, NULL); 522 | node_array.push_back(rv); 523 | $$=rv; 524 | } 525 | ; 526 | 527 | argList : 528 | /* empty */ { 529 | ArgsNode* rv = new ArgsNode(); 530 | node_array.push_back(rv); 531 | $$=rv; 532 | } 533 | | arg { 534 | ArgsNode* rv = new ArgsNode(); 535 | BodyWordNode* bw = dynamic_cast($1); 536 | rv->push_back(bw); 537 | node_array.push_back(rv); 538 | $$=rv; 539 | } 540 | | argList "," arg { 541 | ArgsNode* rv = dynamic_cast($1); 542 | BodyWordNode* bw = dynamic_cast($3); 543 | rv->push_back(bw); 544 | $$=rv; 545 | } 546 | ; 547 | 548 | arg : 549 | varRef { 550 | AstNode* rv = new BodyWordNode(BodyWordNode::VARREF, $1); 551 | node_array.push_back(rv); 552 | $$=rv; 553 | } 554 | | name { 555 | AstNode* rv = new BodyWordNode(BodyWordNode::NAME, $1); 556 | node_array.push_back(rv); 557 | $$=rv; 558 | } 559 | | field { 560 | AstNode* rv = new BodyWordNode(BodyWordNode::FIELD, $1); 561 | node_array.push_back(rv); 562 | $$=rv; 563 | } 564 | | integer { 565 | AstNode* rv = new BodyWordNode(BodyWordNode::INTEGER, $1); 566 | node_array.push_back(rv); 567 | $$=rv; 568 | } 569 | ; 570 | 571 | /* 572 | General form of P4-14 declarations: 573 | keyWord name [ name ] ["(" opts ")"] [ [";"] | "{" body "}"] 574 | */ 575 | 576 | p4ExprTofino : 577 | // Parsed statements. 578 | tableDecl { 579 | node_array.push_back($1); 580 | $$=$1; 581 | } 582 | | headerTypeDeclaration { 583 | node_array.push_back($1); 584 | $$=$1; 585 | } 586 | | headerInstance { 587 | node_array.push_back($1); 588 | $$=$1; 589 | } 590 | | metadataInstance { 591 | node_array.push_back($1); 592 | $$=$1; 593 | } 594 | | actionFunctionDeclaration { 595 | node_array.push_back($1); 596 | $$=$1; 597 | } 598 | | registerDecl { 599 | // necessary for mirroring measurement register 600 | node_array.push_back($1); 601 | $$=$1; 602 | } 603 | // Generic statements. 604 | | keyWord name ";" { 605 | AstNode* rv = new P4ExprNode($1, $2, NULL, NULL, NULL); 606 | node_array.push_back(rv); 607 | $$=rv; 608 | } 609 | | keyWord name "{" body "}" { 610 | AstNode* rv = new P4ExprNode($1, $2, NULL, NULL, $4); 611 | node_array.push_back(rv); 612 | $$=rv; 613 | } 614 | /* name2, no opts */ 615 | | keyWord name name ";" { 616 | AstNode* rv = new P4ExprNode($1, $2, $3, NULL, NULL); 617 | node_array.push_back(rv); 618 | $$=rv; 619 | } 620 | | keyWord name name "{" body "}" { 621 | AstNode* rv = new P4ExprNode($1, $2, $3, NULL, $5); 622 | node_array.push_back(rv); 623 | $$=rv; 624 | } 625 | /* To parse e.g., "calculated_field ipv4.hdrChecksum" */ 626 | | keyWord name "." name "{" body "}" { 627 | AstNode* rv = new P4ExprNode($1, $2, $4, NULL, $6); 628 | node_array.push_back(rv); 629 | $$=rv; 630 | } 631 | /* no name2, opts */ 632 | | keyWord name "(" opts ")" ";" { 633 | AstNode* rv = new P4ExprNode($1, $2, NULL, $4, NULL); 634 | node_array.push_back(rv); 635 | $$=rv; 636 | } 637 | | keyWord name "(" opts ")" "{" body "}" { 638 | AstNode* rv = new P4ExprNode($1, $2, NULL, $4, $7); 639 | node_array.push_back(rv); 640 | $$=rv; 641 | } 642 | /* name2, opts */ 643 | | keyWord name name "(" opts ")" ";" { 644 | AstNode* rv = new P4ExprNode($1, $2, $3, $5, NULL); 645 | node_array.push_back(rv); 646 | $$=rv; 647 | } 648 | | keyWord name name "(" opts ")" "{" body "}" { 649 | AstNode* rv = new P4ExprNode($1, $2, $3, $5, $8); 650 | node_array.push_back(rv); 651 | $$=rv; 652 | } 653 | ; 654 | 655 | p4ExprBmv2 : 656 | // Parsed statements. 657 | tableDecl { 658 | node_array.push_back($1); 659 | $$=$1; 660 | } 661 | | headerTypeDeclaration { 662 | node_array.push_back($1); 663 | $$=$1; 664 | } 665 | | headerInstance { 666 | node_array.push_back($1); 667 | $$=$1; 668 | } 669 | | actionFunctionDeclaration { 670 | node_array.push_back($1); 671 | $$=$1; 672 | } 673 | // Generic statements. 674 | | keyWord name ";" { 675 | AstNode* rv = new P4ExprNode($1, $2, NULL, NULL, NULL); 676 | node_array.push_back(rv); 677 | $$=rv; 678 | } 679 | | keyWord name "{" body "}" { 680 | AstNode* rv = new P4ExprNode($1, $2, NULL, NULL, $4); 681 | node_array.push_back(rv); 682 | $$=rv; 683 | } 684 | /* name2, no opts */ 685 | | keyWord name name ";" { 686 | AstNode* rv = new P4ExprNode($1, $2, $3, NULL, NULL); 687 | node_array.push_back(rv); 688 | $$=rv; 689 | } 690 | | keyWord name name "{" body "}" { 691 | AstNode* rv = new P4ExprNode($1, $2, $3, NULL, $5); 692 | node_array.push_back(rv); 693 | $$=rv; 694 | } 695 | /* no name2, opts */ 696 | | keyWord name "(" opts ")" ";" { 697 | AstNode* rv = new P4ExprNode($1, $2, NULL, $4, NULL); 698 | node_array.push_back(rv); 699 | $$=rv; 700 | } 701 | | keyWord name "(" opts ")" "{" body "}" { 702 | AstNode* rv = new P4ExprNode($1, $2, NULL, $4, $7); 703 | node_array.push_back(rv); 704 | $$=rv; 705 | } 706 | /* name2, opts */ 707 | | keyWord name name "(" opts ")" ";" { 708 | AstNode* rv = new P4ExprNode($1, $2, $3, $5, NULL); 709 | node_array.push_back(rv); 710 | $$=rv; 711 | } 712 | | keyWord name name "(" opts ")" "{" body "}" { 713 | AstNode* rv = new P4ExprNode($1, $2, $3, $5, $8); 714 | node_array.push_back(rv); 715 | $$=rv; 716 | } 717 | ; 718 | 719 | // KeyWord should be a list of P4 keyWords. 720 | // For now its a generic identifier. 721 | keyWord : 722 | IDENTIFIER { 723 | std::string* newStr = new string(string($1)); 724 | AstNode* rv = new KeywordNode(newStr); 725 | node_array.push_back(rv); 726 | $$=rv; 727 | } 728 | ; 729 | 730 | // A name is a valid P4 identifier 731 | name : 732 | IDENTIFIER { 733 | std::string* newStr = new string(string($1)); 734 | AstNode* rv = new NameNode(newStr); 735 | node_array.push_back(rv); 736 | $$=rv; 737 | } 738 | ; 739 | 740 | // Options are empty or a list of names 741 | opts : 742 | /* empty */ { 743 | AstNode* rv = new OptsNode(NULL); 744 | node_array.push_back(rv); 745 | $$=rv; 746 | } 747 | | nameList { 748 | AstNode* rv = new OptsNode($1); 749 | node_array.push_back(rv); 750 | $$=rv; 751 | } 752 | ; 753 | 754 | // A name list is a comma-separated list of names 755 | nameList : 756 | name { 757 | AstNode* rv = new NameListNode(NULL, $1); 758 | node_array.push_back(rv); 759 | $$=rv; 760 | } 761 | | nameList "," name { 762 | AstNode* rv = new NameListNode($1, $3); 763 | node_array.push_back(rv); 764 | $$=rv; 765 | } 766 | ; 767 | 768 | 769 | // A body is an unparsed block of code in any whitespace-insensitive language 770 | // e.g., P4, C, C++ 771 | body : 772 | /* empty */ { 773 | AstNode* empty = new EmptyNode(); 774 | $$ = empty; 775 | } 776 | | body bodyWord { 777 | AstNode* rv = new BodyNode($1, NULL, $2); 778 | node_array.push_back(rv); 779 | $$=rv; 780 | } 781 | | body "{" body "}" { 782 | AstNode* rv = new BodyNode($1, $3, NULL); 783 | node_array.push_back(rv); 784 | $$=rv; 785 | } 786 | ; 787 | 788 | // A string can be an identifier, word, or parsed special character. 789 | bodyWord : 790 | varRef { 791 | AstNode* rv = new BodyWordNode(BodyWordNode::VARREF, $1); 792 | node_array.push_back(rv); 793 | $$=rv; 794 | } 795 | | name { 796 | AstNode* rv = new BodyWordNode(BodyWordNode::NAME, $1); 797 | node_array.push_back(rv); 798 | $$=rv; 799 | } 800 | | integer { 801 | AstNode* rv = new BodyWordNode(BodyWordNode::INTEGER, $1); 802 | node_array.push_back(rv); 803 | $$=rv; 804 | } 805 | | specialChar { 806 | AstNode* rv = new BodyWordNode(BodyWordNode::SPECIAL, $1); 807 | node_array.push_back(rv); 808 | $$=rv; 809 | } 810 | | STRING { 811 | AstNode* sv = new StrNode(new string($1)); 812 | AstNode* rv = new BodyWordNode(BodyWordNode::STRING, sv); 813 | node_array.push_back(rv); 814 | $$=rv; 815 | free($1); 816 | } 817 | // Better to set up blackbox declaration itself 818 | | REACTION_ARG_REG { 819 | AstNode* sv = new StrNode(new string("reg")); 820 | AstNode* rv = new BodyWordNode(BodyWordNode::STRING, sv); 821 | node_array.push_back(rv); 822 | $$=rv; 823 | } 824 | ; 825 | 826 | varRef : 827 | "$" "{" name "}" { 828 | AstNode* rv = new MblRefNode($3); 829 | node_array.push_back(rv); 830 | $$=rv; 831 | } 832 | ; 833 | 834 | specialChar: 835 | L_PAREN { 836 | string* newStr = new string("("); 837 | AstNode* rv = new SpecialCharNode(newStr); 838 | node_array.push_back(rv); 839 | $$=rv;} 840 | | R_PAREN { 841 | string* newStr = new string(")"); 842 | AstNode* rv = new SpecialCharNode(newStr); 843 | node_array.push_back(rv); 844 | $$=rv;} 845 | | L_BRACKET { 846 | string* newStr = new string("["); 847 | AstNode* rv = new SpecialCharNode(newStr); 848 | node_array.push_back(rv); 849 | $$=rv;} 850 | | R_BRACKET { 851 | string* newStr = new string("]"); 852 | AstNode* rv = new SpecialCharNode(newStr); 853 | node_array.push_back(rv); 854 | $$=rv;} 855 | | SEMICOLON { 856 | string* newStr = new string(";"); 857 | AstNode* rv = new SpecialCharNode(newStr); 858 | node_array.push_back(rv); 859 | $$=rv;} 860 | | COLON { 861 | string* newStr = new string(":"); 862 | AstNode* rv = new SpecialCharNode(newStr); 863 | node_array.push_back(rv); 864 | $$=rv;} 865 | | COMMA { 866 | string* newStr = new string(","); 867 | AstNode* rv = new SpecialCharNode(newStr); 868 | node_array.push_back(rv); 869 | $$=rv;} 870 | | PERIOD { 871 | string* newStr = new string("."); 872 | AstNode* rv = new SpecialCharNode(newStr); 873 | node_array.push_back(rv); 874 | $$=rv;} 875 | | WIDTH { 876 | string* newStr = new string("width"); 877 | AstNode* rv = new SpecialCharNode(newStr); 878 | node_array.push_back(rv); 879 | $$=rv;} 880 | | SLASH { 881 | string* newStr = new string("/"); 882 | AstNode* rv = new SpecialCharNode(newStr); 883 | node_array.push_back(rv); 884 | $$=rv;} 885 | ; 886 | 887 | integer : 888 | INTEGER { 889 | string* strVal = new string(string($1)); 890 | AstNode* rv = new IntegerNode(strVal); 891 | node_array.push_back(rv); 892 | $$=rv; 893 | free($1); 894 | } 895 | ; 896 | 897 | /*===== End of P4 expressions ======*/ 898 | 899 | 900 | 901 | /*===================================== 902 | = P4R expressions = 903 | =====================================*/ 904 | 905 | p4rExpr : 906 | p4rInitBlock { 907 | AstNode* rv = new P4RExprNode($1); 908 | node_array.push_back(rv); 909 | $$=rv; 910 | } 911 | | 912 | p4rReaction { 913 | AstNode* rv = new P4RExprNode($1); 914 | node_array.push_back(rv); 915 | $$=rv; 916 | } 917 | | p4rMalleable { 918 | AstNode* rv = new P4RExprNode($1); 919 | node_array.push_back(rv); 920 | $$=rv; 921 | } 922 | ; 923 | 924 | /************** Reactions **************/ 925 | 926 | p4rInitBlock : 927 | P4R_INIT_BLOCK name "{" body "}" { 928 | AstNode* rv = new P4RInitBlockNode($2, $4); 929 | node_array.push_back(rv); 930 | $$=rv; 931 | } 932 | ; 933 | 934 | p4rReaction : 935 | P4R_REACTION name "(" reactionArgs ")" "{" includes body "}" { 936 | AstNode* rv = new P4RReactionNode($2, $4, $8); 937 | node_array.push_back(rv); 938 | $$=rv; 939 | } 940 | ; 941 | 942 | includes : 943 | /* empty */ { 944 | AstNode* empty = new EmptyNode(); 945 | $$ = empty; 946 | } 947 | | includes INCLUDE { 948 | string* strVal = new string(string($2)); 949 | AstNode* rv = new IncludeNode(strVal, IncludeNode::C); 950 | node_array.push_back(rv); 951 | $$ = rv; 952 | } 953 | ; 954 | 955 | 956 | 957 | reactionArgs : 958 | /* empty */ { 959 | $$=new ReactionArgsNode(); 960 | } 961 | | reactionArg { 962 | ReactionArgsNode* rv = new ReactionArgsNode(); 963 | ReactionArgNode* ra = dynamic_cast($1); 964 | rv->push_back(ra); 965 | node_array.push_back(rv); 966 | $$=rv; 967 | } 968 | | reactionArgs "," reactionArg { 969 | ReactionArgsNode* rv = dynamic_cast($1); 970 | ReactionArgNode* ra = dynamic_cast($3); 971 | rv->push_back(ra); 972 | node_array.push_back(rv); 973 | $$=rv; 974 | } 975 | ; 976 | 977 | reactionArg : 978 | REACTION_ARG_ING field { 979 | AstNode* rv = new ReactionArgNode(ReactionArgNode::INGRESS_FIELD, $2, NULL, NULL); 980 | node_array.push_back(rv); 981 | $$=rv; 982 | } 983 | | REACTION_ARG_EGR field { 984 | AstNode* rv = new ReactionArgNode(ReactionArgNode::EGRESS_FIELD, $2, NULL, NULL); 985 | node_array.push_back(rv); 986 | $$=rv; 987 | } 988 | | REACTION_ARG_ING varRef { 989 | AstNode* rv = new ReactionArgNode(ReactionArgNode::INGRESS_MBL_FIELD, $2, NULL, NULL); 990 | node_array.push_back(rv); 991 | $$=rv; 992 | } 993 | | REACTION_ARG_EGR varRef { 994 | AstNode* rv = new ReactionArgNode(ReactionArgNode::EGRESS_MBL_FIELD, $2, NULL, NULL); 995 | node_array.push_back(rv); 996 | $$=rv; 997 | } 998 | // Better to treat reg as a token parsed with blackbox 999 | | REACTION_ARG_REG name { 1000 | AstNode* rv = new ReactionArgNode(ReactionArgNode::REGISTER, $2, NULL, NULL); 1001 | node_array.push_back(rv); 1002 | $$=rv; 1003 | } 1004 | | REACTION_ARG_REG name "[" integer ":" integer "]" { 1005 | AstNode* rv = new ReactionArgNode(ReactionArgNode::REGISTER, $2, $4, $6); 1006 | node_array.push_back(rv); 1007 | $$=rv; 1008 | } 1009 | ; 1010 | 1011 | /************** Malleable **************/ 1012 | 1013 | p4rMalleable : 1014 | P4R_MALLEABLE VALUE name "{" varWidth varValueInit "}" { 1015 | AstNode* rv = new P4RMalleableValueNode($3, $5, $6); 1016 | node_array.push_back(rv); 1017 | $$=rv; 1018 | } 1019 | | P4R_MALLEABLE FIELD name "{" varWidth varFieldInit varAlts "}" { 1020 | AstNode* rv = new P4RMalleableFieldNode($3, $5, $6, $7); 1021 | node_array.push_back(rv); 1022 | $$=rv; 1023 | } 1024 | | P4R_MALLEABLE tableDecl { 1025 | AstNode* rv = new P4RMalleableTableNode($2, ""); 1026 | node_array.push_back(rv); 1027 | $$=rv; 1028 | } 1029 | | PRAGMA P4R_MALLEABLE tableDecl { 1030 | AstNode* rv = new P4RMalleableTableNode($3, $1); 1031 | node_array.push_back(rv); 1032 | $$=rv; 1033 | } 1034 | ; 1035 | 1036 | varWidth : 1037 | WIDTH ":" integer ";" { 1038 | AstNode* rv = new VarWidthNode($3); 1039 | node_array.push_back(rv); 1040 | $$=rv; 1041 | } 1042 | ; 1043 | 1044 | varValueInit : 1045 | INIT ":" integer ";" { 1046 | AstNode* rv = new VarInitNode($3); 1047 | node_array.push_back(rv); 1048 | $$=rv; 1049 | } 1050 | ; 1051 | 1052 | varFieldInit : 1053 | INIT ":" integer ";" { 1054 | AstNode* rv = new VarInitNode($3); 1055 | node_array.push_back(rv); 1056 | $$=rv; 1057 | } 1058 | | INIT ":" field ";" { 1059 | AstNode* rv = new VarInitNode($3); 1060 | node_array.push_back(rv); 1061 | $$=rv; 1062 | } 1063 | ; 1064 | 1065 | varAlts : 1066 | ALTS "{" fieldList "}" { 1067 | AstNode* rv = new VarAltNode($3); 1068 | node_array.push_back(rv); 1069 | $$=rv; 1070 | } 1071 | ; 1072 | 1073 | // One or more fields names. 1074 | fieldList : 1075 | /* empty */ { 1076 | FieldsNode* rv = new FieldsNode(); 1077 | node_array.push_back(rv); 1078 | $$=rv; 1079 | } 1080 | | field { 1081 | FieldsNode* rv = new FieldsNode(); 1082 | FieldNode* fld = dynamic_cast($1); 1083 | rv->push_back(fld); 1084 | node_array.push_back(rv); 1085 | $$=rv; 1086 | } 1087 | | fieldList "," field { 1088 | FieldsNode* rv = dynamic_cast($1); 1089 | FieldNode* fld = dynamic_cast($3); 1090 | rv->push_back(fld); 1091 | node_array.push_back(rv); 1092 | $$=rv; 1093 | } 1094 | ; 1095 | 1096 | field : 1097 | name "." name { 1098 | AstNode* rv = new FieldNode($1, $3); 1099 | node_array.push_back(rv); 1100 | $$=rv; 1101 | } 1102 | ; 1103 | 1104 | /* END NEW GRAMMAR */ 1105 | 1106 | %% 1107 | 1108 | 1109 | void handler(int sig); 1110 | 1111 | int main(int argc, char* argv[]) { 1112 | signal(SIGSEGV, handler); 1113 | 1114 | parseArgs(argc, argv); 1115 | 1116 | yyin = in_file; 1117 | yyparse(); 1118 | fclose(in_file); 1119 | 1120 | PRINT_VERBOSE("Number of syntax tree nodes: %d\n", node_array.size()); 1121 | 1122 | vector newP4Nodes = compileP4Code(&node_array); 1123 | 1124 | vector reactions; 1125 | findAndRemoveReactions(&reactions, node_array); 1126 | 1127 | ofstream os; 1128 | os.open(p4_out_fn); 1129 | os << root->toString() << endl << endl; 1130 | for (auto n : newP4Nodes) { 1131 | os << n->toString(); 1132 | } 1133 | os.close(); 1134 | 1135 | vector cNodes = compileCCode(node_array, out_fn_base); 1136 | 1137 | os.open(c_out_fn); 1138 | 1139 | PRINT_VERBOSE("Number of C nodes: %d\n", cNodes.size()); 1140 | 1141 | for (auto node : cNodes){ 1142 | os << node -> toString() << endl; 1143 | os << "\n" << endl; 1144 | } 1145 | os.close(); 1146 | 1147 | return 0; 1148 | } 1149 | 1150 | 1151 | 1152 | void yyerror(const char* s) { 1153 | printf("Line %d: %s when parsing '%s'\n", yylineno, s, yytext); 1154 | exit(1); 1155 | } 1156 | 1157 | // Crash dump handler: https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes 1158 | void handler(int sig) { 1159 | void* array[10]; 1160 | size_t size; 1161 | 1162 | // get void*'s for all entries on the stack 1163 | size = backtrace(array, 10); 1164 | 1165 | // print out all the frames to stderr 1166 | fprintf(stderr, "Error: signal %d:\n", sig); 1167 | backtrace_symbols_fd(array, size, STDERR_FILENO); 1168 | exit(1); 1169 | } 1170 | -------------------------------------------------------------------------------- /include/ast_nodes.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef AST_NODES_H 17 | #define AST_NODES_H 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | 29 | 30 | // Base syntax tree node 31 | class AstNode { 32 | public: 33 | bool valid_ = true; 34 | string nodeType_ = string("base"); 35 | AstNode* parent_ = NULL; 36 | bool removed_ = false; 37 | 38 | virtual string toString() { 39 | return ""; 40 | } 41 | }; 42 | 43 | class EmptyNode : public AstNode { 44 | public: 45 | string* emptyStr_ = new string(""); 46 | EmptyNode() { 47 | nodeType_ = typeid(*this).name(); 48 | valid_ = false; 49 | } 50 | string toString() { 51 | return *(emptyStr_); 52 | } 53 | }; 54 | 55 | // A P4 identifier 56 | class NameNode : public AstNode { 57 | public: 58 | string* word_; 59 | NameNode(string* word) { 60 | nodeType_ = typeid(*this).name(); 61 | word_ = word; 62 | } 63 | NameNode* deepCopy() { 64 | return new NameNode(new string(*word_)); 65 | } 66 | string toString() { 67 | return *(word_); 68 | } 69 | }; 70 | 71 | class StrNode : public AstNode { 72 | public: 73 | string* word_; 74 | StrNode(string* word) { 75 | nodeType_ = typeid(*this).name(); 76 | word_ = word; 77 | } 78 | string toString() { 79 | return *(word_); 80 | } 81 | }; 82 | 83 | class IntegerNode : public AstNode { 84 | public: 85 | string* word_; 86 | IntegerNode(string* word) { 87 | nodeType_ = typeid(*this).name(); 88 | word_ = word; 89 | } 90 | string toString() { 91 | return *(word_); 92 | } 93 | }; 94 | 95 | class SpecialCharNode : public AstNode { 96 | public: 97 | string* word_; 98 | SpecialCharNode(string* word) { 99 | nodeType_ = typeid(*this).name(); 100 | word_ = word; 101 | } 102 | string toString() { 103 | return *(word_); 104 | } 105 | }; 106 | 107 | class IncludeNode : public AstNode { 108 | public: 109 | enum MacroType {P4, C}; 110 | 111 | string* line_; 112 | MacroType macrotype_; 113 | IncludeNode(string* line, MacroType macrotype) { 114 | nodeType_ = typeid(*this).name(); 115 | line_ = line; 116 | macrotype_ = macrotype; 117 | } 118 | 119 | string toString() { 120 | return *(line_); 121 | } 122 | }; 123 | 124 | class InputNode : public AstNode { 125 | public: 126 | InputNode* next_; 127 | AstNode* expression_; 128 | 129 | InputNode(AstNode* next, AstNode* expression) { 130 | nodeType_ = typeid(*this).name(); 131 | next_ = dynamic_cast(next); 132 | expression_ = expression; 133 | expression_->parent_ = this; 134 | } 135 | string toString() { 136 | ostringstream oss; 137 | if (next_) { 138 | oss << next_->toString() << "\n"; 139 | } 140 | oss << expression_->toString(); 141 | return oss.str(); 142 | } 143 | }; 144 | 145 | #endif -------------------------------------------------------------------------------- /include/ast_nodes_p4.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef AST_NODES_P4_H 17 | #define AST_NODES_P4_H 18 | 19 | #include "ast_nodes.h" 20 | 21 | 22 | template 23 | class ListNode : public AstNode { 24 | public: 25 | ListNode() { 26 | list_ = new std::vector(); 27 | } 28 | void push_back(T* node) { 29 | list_->push_back(node); 30 | node->parent_ = this; 31 | } 32 | std::vector* list_; 33 | }; 34 | 35 | // An identifier, word, int, or special char 36 | class BodyWordNode : public AstNode { 37 | public: 38 | enum WordType { VARREF, NAME, FIELD, STRING, INTEGER, SPECIAL }; 39 | 40 | BodyWordNode(WordType wordType, AstNode* contents); 41 | BodyWordNode* deepCopy(); 42 | std::string toString(); 43 | 44 | const WordType wordType_; 45 | AstNode* contents_; 46 | 47 | int indent_=1; 48 | }; 49 | 50 | class BodyNode : public AstNode { 51 | public: 52 | BodyNode(AstNode* bodyOuter, AstNode* bodyInner, AstNode* str); 53 | std::string toString(); 54 | 55 | BodyNode* bodyOuter_; 56 | BodyNode* bodyInner_; 57 | BodyWordNode* str_; 58 | 59 | int indent_=1; 60 | }; 61 | 62 | class KeywordNode : public AstNode { 63 | public: 64 | KeywordNode(std::string* word); 65 | std::string toString(); 66 | 67 | std::string* word_; 68 | }; 69 | 70 | class P4RegisterNode : public AstNode { 71 | public: 72 | P4RegisterNode(AstNode* name, AstNode* body); 73 | std::string toString(); 74 | 75 | AstNode* name_; 76 | BodyNode* body_; 77 | 78 | int width_; 79 | int instanceCount_; 80 | }; 81 | 82 | class P4ExprNode : public AstNode { 83 | public: 84 | P4ExprNode(AstNode* keyword, AstNode* name1, AstNode* name2, 85 | AstNode* opts, AstNode* body); 86 | std::string toString(); 87 | 88 | KeywordNode* keyword_; 89 | NameNode* name1_; 90 | AstNode* name2_; 91 | AstNode* opts_; 92 | BodyNode* body_; 93 | }; 94 | 95 | class OptsNode : public AstNode { 96 | public: 97 | OptsNode(AstNode* nameList); 98 | std::string toString(); 99 | 100 | AstNode* nameList_; 101 | }; 102 | 103 | class NameListNode : public AstNode { 104 | public: 105 | NameListNode(AstNode* nameList, AstNode* name); 106 | std::string toString(); 107 | 108 | AstNode *nameList_, *name_; 109 | }; 110 | 111 | class TableReadStmtNode : public AstNode { 112 | public: 113 | enum MatchType { EXACT, TERNARY }; 114 | 115 | TableReadStmtNode(MatchType matchType, AstNode* field); 116 | std::string toString(); 117 | 118 | MatchType matchType_; 119 | AstNode* field_; 120 | }; 121 | 122 | class TableReadStmtsNode : public ListNode { 123 | public: 124 | TableReadStmtsNode(); 125 | std::string toString(); 126 | }; 127 | 128 | class TableActionStmtNode : public AstNode { 129 | public: 130 | TableActionStmtNode(AstNode* name); 131 | std::string toString(); 132 | 133 | NameNode* name_; 134 | }; 135 | 136 | class TableActionStmtsNode : public ListNode { 137 | public: 138 | TableActionStmtsNode(); 139 | std::string toString(); 140 | }; 141 | 142 | // A P4 table 143 | class TableNode : public AstNode { 144 | public: 145 | TableNode(AstNode* name, AstNode* reads, AstNode* actions, 146 | std::string options, std::string pragma); 147 | std::string toString(); 148 | void transformPragma(); 149 | 150 | NameNode* name_; 151 | TableReadStmtsNode* reads_; 152 | TableActionStmtsNode* actions_; 153 | // size, default action, etc. 154 | vector options_; 155 | 156 | std::string pragma_; 157 | bool pragmaTransformed_; 158 | 159 | // for malleable table, parser gives TableNode as well 160 | // need to keep this meta data to indicate if the table node is variable 161 | bool isMalleable_; 162 | }; 163 | 164 | class FieldDecNode : public AstNode { 165 | public: 166 | FieldDecNode(AstNode* name, AstNode* size); 167 | std::string toString(); 168 | 169 | NameNode* name_; 170 | IntegerNode* size_; 171 | }; 172 | 173 | class FieldDecsNode : public ListNode { 174 | public: 175 | FieldDecsNode(); 176 | std::string toString(); 177 | }; 178 | 179 | class HeaderTypeDeclarationNode : public AstNode { 180 | public: 181 | HeaderTypeDeclarationNode(AstNode* name, AstNode* field_decs, 182 | AstNode* other_stmts); 183 | std::string toString(); 184 | 185 | NameNode* name_; 186 | FieldDecsNode* field_decs_; 187 | AstNode* other_stmts_; 188 | }; 189 | 190 | class HeaderInstanceNode : public AstNode { 191 | public: 192 | HeaderInstanceNode(AstNode* type, AstNode* name); 193 | std::string toString(); 194 | 195 | NameNode* type_; 196 | NameNode* name_; 197 | }; 198 | 199 | class MetadataInstanceNode : public AstNode { 200 | public: 201 | MetadataInstanceNode(AstNode* type, AstNode* name); 202 | std::string toString(); 203 | 204 | NameNode* type_; 205 | NameNode* name_; 206 | }; 207 | 208 | class ArgsNode : public ListNode { 209 | public: 210 | ArgsNode(); 211 | ArgsNode* deepCopy(); 212 | std::string toString(); 213 | }; 214 | 215 | class ActionParamNode : public AstNode { 216 | public: 217 | ActionParamNode(AstNode* param); 218 | ActionParamNode* deepCopy(); 219 | std::string toString(); 220 | 221 | AstNode* param_; 222 | }; 223 | 224 | class ActionParamsNode : public ListNode { 225 | public: 226 | ActionParamsNode(); 227 | ActionParamsNode* deepCopy(); 228 | std::string toString(); 229 | }; 230 | 231 | class ActionStmtNode : public AstNode { 232 | public: 233 | enum ActionStmtType { NAME_ARGLIST, PROG_EXEC }; 234 | 235 | ActionStmtNode(AstNode* name1, AstNode* args, ActionStmtType type, AstNode* name2, AstNode* index); 236 | ActionStmtNode* deepCopy(); 237 | std::string toString(); 238 | 239 | ActionStmtType type_; 240 | 241 | NameNode* name1_; 242 | NameNode* name2_; 243 | IntegerNode* index_; 244 | ArgsNode* args_; 245 | }; 246 | 247 | class ActionStmtsNode : public ListNode { 248 | public: 249 | ActionStmtsNode(); 250 | ActionStmtsNode* deepCopy(); 251 | std::string toString(); 252 | }; 253 | 254 | class ActionNode : public AstNode { 255 | public: 256 | ActionNode(AstNode* name, AstNode* params, AstNode* stmts); 257 | ActionNode* duplicateAction(const std::string& name); 258 | std::string toString(); 259 | 260 | NameNode* name_; 261 | ActionParamsNode* params_; 262 | ActionStmtsNode* stmts_; 263 | }; 264 | 265 | #endif -------------------------------------------------------------------------------- /include/ast_nodes_p4r.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef AST_NODES_P4R_H 17 | #define AST_NODES_P4R_H 18 | 19 | #include "ast_nodes.h" 20 | #include "ast_nodes_p4.h" 21 | 22 | /*======================================== 23 | = p4r expressions. = 24 | ========================================*/ 25 | class P4RExprNode : public AstNode { 26 | public: 27 | P4RExprNode(AstNode* varOrReaction); 28 | std::string toString(); 29 | 30 | AstNode *varOrReaction_; 31 | }; 32 | 33 | 34 | /** 35 | * 36 | * Malleabless and subnodes 37 | * 38 | */ 39 | class VarWidthNode : public AstNode { 40 | public: 41 | VarWidthNode(AstNode* val); 42 | std::string toString(); 43 | 44 | IntegerNode *val_; 45 | }; 46 | 47 | class VarInitNode : public AstNode { 48 | public: 49 | VarInitNode(AstNode* val); 50 | std::string toString(); 51 | 52 | AstNode *val_; 53 | }; 54 | 55 | class P4RSettableMalleableNode : public AstNode { 56 | public: 57 | enum MalleableType { VALUE, FIELD }; 58 | 59 | P4RSettableMalleableNode(std::string nodeType, AstNode* name, 60 | AstNode* varWidth, MalleableType MalleableType); 61 | 62 | const MalleableType malleableType_; 63 | NameNode* name_; 64 | VarWidthNode* varWidth_; 65 | }; 66 | 67 | class P4RMalleableValueNode : public P4RSettableMalleableNode { 68 | public: 69 | P4RMalleableValueNode(AstNode* name, AstNode* varWidth, AstNode* varInit); 70 | std::string toString(); 71 | 72 | AstNode* varInit_; 73 | }; 74 | 75 | class FieldNode : public AstNode { 76 | public: 77 | FieldNode(AstNode* headerName, AstNode* fieldName); 78 | std::string toString(); 79 | 80 | NameNode* headerName_; 81 | NameNode* fieldName_; 82 | }; 83 | 84 | class FieldsNode : public ListNode { 85 | public : 86 | FieldsNode(); 87 | std::string toString(); 88 | }; 89 | 90 | class VarAltNode : public AstNode { 91 | public: 92 | VarAltNode(AstNode* fieldList); 93 | std::string toString(); 94 | 95 | FieldsNode* fields_; 96 | }; 97 | 98 | class P4RMalleableFieldNode : public P4RSettableMalleableNode { 99 | public: 100 | P4RMalleableFieldNode(AstNode* name, AstNode* varWidth, 101 | AstNode* varInit, AstNode* varAlts); 102 | std::string toString(); 103 | 104 | // during prologue/dialogue, each alternative corresponds to a unique value in pointer metadata 105 | int mapAltToInt(std::string alt); 106 | 107 | AstNode* varInit_; 108 | VarAltNode* varAlts_; 109 | }; 110 | 111 | class P4RMalleableTableNode : public AstNode { 112 | public: 113 | P4RMalleableTableNode(AstNode* table, std::string pragma); 114 | std::string toString(); 115 | void transformPragma(); 116 | 117 | TableNode* table_; 118 | bool pragmaTransformed_; 119 | std::string pragma_; 120 | }; 121 | 122 | 123 | /** 124 | * 125 | * Reaction and subnodes. 126 | * 127 | */ 128 | class ReactionArgNode : public AstNode { 129 | public: 130 | enum ArgType { INGRESS_FIELD, EGRESS_FIELD, INGRESS_MBL_FIELD, EGRESS_MBL_FIELD, REGISTER }; 131 | 132 | ReactionArgNode(const ArgType& argType, AstNode* arg, AstNode* index1, AstNode* index2); 133 | std::string toString(); 134 | 135 | const ArgType argType_; 136 | AstNode* arg_; 137 | IntegerNode* index1_; 138 | IntegerNode* index2_; 139 | }; 140 | 141 | class ReactionArgsNode : public ListNode { 142 | public: 143 | ReactionArgsNode(); 144 | std::string toString(); 145 | }; 146 | 147 | class P4RReactionNode : public AstNode { 148 | public: 149 | P4RReactionNode(AstNode* name, AstNode* args, AstNode* body); 150 | std::string toString(); 151 | 152 | NameNode* name_; 153 | ReactionArgsNode* args_; 154 | BodyNode* body_; 155 | }; 156 | 157 | class P4RInitBlockNode : public AstNode { 158 | public: 159 | P4RInitBlockNode(AstNode* name, AstNode* body); 160 | std::string toString(); 161 | 162 | NameNode* name_; 163 | BodyNode* body_; 164 | }; 165 | 166 | class MblRefNode : public AstNode { 167 | public: 168 | MblRefNode(AstNode* name); 169 | void transform(const std::string &transformedHeader, 170 | const std::string &transformedField); 171 | MblRefNode* deepCopy(); 172 | std::string toString(); 173 | 174 | bool transformed_ = false; 175 | bool inReaction_ = false; 176 | std::string transformedHeader_; 177 | std::string transformedField_; 178 | NameNode* name_; 179 | }; 180 | 181 | /*===== End of p4r expressions ======*/ 182 | 183 | 184 | /*============================================================ 185 | = Custom nodes to add transformations = 186 | ============================================================*/ 187 | 188 | class UnanchoredNode : public AstNode { 189 | // A block of code that is not anchored anywhere in the syntax tree 190 | public: 191 | UnanchoredNode(std::string* newCode, std::string* objType, 192 | std::string* objName); 193 | std::string toString(); 194 | 195 | std::string *codeBlob_; // The P4 code 196 | std::string *objType_; // The type of the P4 object (ex: table, metadata) 197 | std::string *objName_; // A P4 object to invoke 198 | }; 199 | 200 | /*===== End of Custom nodes to add transformations ======*/ 201 | 202 | 203 | #endif -------------------------------------------------------------------------------- /include/compile.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef COMPILE_H 17 | #define COMPILE_H 18 | 19 | #include 20 | 21 | #include "ast_nodes.h" 22 | #include "ast_nodes_p4r.h" 23 | 24 | using namespace std; 25 | 26 | vector compileP4Code(vector* nodeArray); 27 | 28 | vector compileCCode(std::vector nodeArray, char * outFnBase); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /include/find_nodes.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef FIND_NODES_H 17 | #define FIND_NODES_H 18 | 19 | #include 20 | #include 21 | 22 | #include "ast_nodes.h" 23 | #include "ast_nodes_p4.h" 24 | #include "ast_nodes_p4r.h" 25 | 26 | 27 | bool typeContains(AstNode* n, const char* type); 28 | 29 | bool p4KeywordMatches(P4ExprNode* n, const char* type); 30 | 31 | P4ExprNode* findIngress(const std::vector& astNodes); 32 | 33 | P4ExprNode* findEgress(const std::vector& astNodes); 34 | 35 | vector findBlackbox(const std::vector& astNodes); 36 | 37 | void findAndRemoveMalleables( 38 | unordered_map* varValues, 39 | unordered_map* varFields, 40 | unordered_map* varTables, 41 | const vector& astNodes); 42 | 43 | void findMalleableRefs(vector* mblRefs, 44 | const vector& astNodes); 45 | 46 | unordered_map findTableActionStmts( 47 | const vector& astNodes, const string& actionName); 48 | 49 | bool findTableReadStmt(const TableNode& table, const string& fieldName); 50 | 51 | void findAndTransformMblRefsInAction(ActionNode* action, 52 | vector* varRefs, 53 | const string& varName, 54 | const string& altHeader, 55 | const string& altField); 56 | 57 | vector findAllAlts(const P4RMalleableFieldNode& malleable); 58 | 59 | P4RInitBlockNode* findInitBlock(std::vector astNodes); 60 | 61 | P4RReactionNode* findReaction(std::vector astNodes); 62 | 63 | vector findP4RegisterNode(const vector& astNodes); 64 | 65 | vector findReactionArgs(const vector& astNodes); 66 | 67 | typedef unordered_map*> HeaderDecsMap; 69 | HeaderDecsMap findHeaderDecs(const vector& astNodes); 70 | 71 | vector > findAllReactionArgSizes( 72 | const vector& reactionArgs, 73 | const HeaderDecsMap& headerDecsMap, 74 | const unordered_map& varValues, 75 | const unordered_map& varFields); 76 | 77 | void findAndRemoveReactions(vector* reactions, 78 | const vector& astNodes); 79 | 80 | bool findTblInIng(string tableName, const vector& astNodes); 81 | 82 | bool findRegargInIng(ReactionArgNode* regarg, std::vector astNodes); 83 | 84 | int findRegargWidth(ReactionArgNode* regarg, std::vector nodeArray); 85 | 86 | #endif -------------------------------------------------------------------------------- /include/helper.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPER_H 2 | #define HELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifdef VERBOSE 13 | #define PRINT_VERBOSE(msg, args...) fprintf(stdout, "VERBOSE: %s %d %s: " msg, __FILE__, __LINE__, __func__, ##args) 14 | #else 15 | #define PRINT_VERBOSE(msg, args...) 16 | #endif 17 | 18 | #if defined(INFO) || defined(VERBOSE) 19 | #define PRINT_INFO(msg, args...) fprintf(stdout, "INFO: %s %d %s: " msg, __FILE__, __LINE__, __func__, ##args) 20 | #else 21 | #define PRINT_INFO(msg, args...) 22 | #endif 23 | 24 | #define PANIC(msg, args...) {\ 25 | fprintf(stderr, "PANIC: %s %d %s: " msg, __FILE__, __LINE__, __func__, ##args);\ 26 | exit(1); } 27 | 28 | #endif // HELPER_H 29 | -------------------------------------------------------------------------------- /src/ast/ast_nodes_p4.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include "../../include/ast_nodes_p4.h" 17 | #include "../../include/ast_nodes_p4r.h" 18 | 19 | using namespace std; 20 | 21 | static string operator*(std::string str, int count) 22 | { 23 | string ret; 24 | for(auto i = 0; iparent_ = this; 36 | } 37 | 38 | BodyWordNode* BodyWordNode::deepCopy() { 39 | AstNode* newContents = contents_; 40 | if (wordType_ == WordType::VARREF) { 41 | newContents = dynamic_cast(contents_)->deepCopy(); 42 | } 43 | 44 | auto newNode = new BodyWordNode(wordType_, newContents); 45 | return newNode; 46 | } 47 | 48 | string BodyWordNode::toString() { 49 | ostringstream oss; 50 | if (wordType_ == WordType::INTEGER || wordType_ == WordType::SPECIAL) { 51 | oss << contents_->toString(); 52 | } else if (dynamic_cast(parent_) != NULL && dynamic_cast(parent_)->bodyOuter_ != NULL && 53 | dynamic_cast(parent_)->bodyOuter_->str_ != NULL && dynamic_cast(parent_)->bodyOuter_->str_->wordType_ == WordType::SPECIAL && 54 | dynamic_cast(parent_)->bodyOuter_->str_->contents_->toString() != ";" ) { 55 | oss << contents_->toString(); 56 | } else if (dynamic_cast(parent_) != NULL && dynamic_cast(parent_)->bodyOuter_ != NULL && 57 | dynamic_cast(parent_)->bodyOuter_->str_ != NULL && dynamic_cast(parent_)->bodyOuter_->str_->wordType_ == WordType::INTEGER) { 58 | oss << contents_->toString(); 59 | } else { 60 | oss << string(" ")*indent_ << contents_->toString(); 61 | } 62 | if (contents_->toString() == ";") { 63 | oss << "\n"; 64 | } 65 | return oss.str(); 66 | } 67 | 68 | BodyNode::BodyNode(AstNode* bodyOuter, AstNode* bodyInner, AstNode* str) { 69 | 70 | nodeType_ = typeid(*this).name(); 71 | bodyOuter_ = dynamic_cast(bodyOuter); 72 | if (bodyOuter_) bodyOuter_->parent_ = this; 73 | bodyInner_ = dynamic_cast(bodyInner); 74 | if (bodyInner_) { 75 | bodyInner_->parent_ = this; 76 | } 77 | str_ = dynamic_cast(str); 78 | if (str_) { 79 | str_->parent_ = this; 80 | } 81 | } 82 | 83 | string BodyNode::toString() { 84 | ostringstream oss; 85 | // Called from the root BodyNode 86 | if (bodyOuter_) { 87 | bodyOuter_->indent_ = indent_; 88 | oss << bodyOuter_->toString(); 89 | } 90 | if (str_) { 91 | str_->indent_ = indent_; 92 | oss << str_->toString(); 93 | return oss.str(); 94 | } else if (bodyInner_) { 95 | bodyInner_->indent_ = indent_ + 1; 96 | oss << string(" ")*indent_ << "{\n" 97 | << bodyInner_->toString() 98 | << string(" ")*indent_ << "}\n"; 99 | return oss.str(); 100 | } else { 101 | assert(false); 102 | } 103 | } 104 | 105 | P4RegisterNode::P4RegisterNode(AstNode* name, AstNode* body) { 106 | nodeType_ = typeid(*this).name(); 107 | name_ = dynamic_cast(name); 108 | if(body->nodeType_.find("EmptyNode")!=string::npos) { 109 | body_ = new BodyNode(NULL, NULL, new BodyWordNode(BodyWordNode::STRING, new StrNode(new string("")))); 110 | width_ = -1; 111 | instanceCount_ = -1; 112 | } else { 113 | body_ = dynamic_cast(body); 114 | std::stringstream ss(body_->toString()); 115 | std::string item; 116 | while (std::getline(ss, item, ';')) 117 | { 118 | std::stringstream ss_(item); 119 | std::string item_; 120 | while (std::getline(ss_, item_, ':')) { 121 | boost::algorithm::trim(item_); 122 | if(item_.compare("width")==0) { 123 | std::getline(ss_, item_, ':'); 124 | boost::algorithm::trim(item_); 125 | width_ = stoi(item_); 126 | } else if (item_.compare("instance_count")==0) { 127 | std::getline(ss_, item_, ':'); 128 | boost::algorithm::trim(item_); 129 | instanceCount_ = stoi(item_); 130 | } 131 | } 132 | } 133 | } 134 | if (body_) body_->parent_ = this; 135 | } 136 | 137 | string P4RegisterNode::toString() { 138 | ostringstream oss; 139 | oss << "register " 140 | << name_->toString() 141 | << " {\n" 142 | << " " << body_->toString() 143 | << "}\n\n"; 144 | return oss.str(); 145 | } 146 | 147 | P4ExprNode::P4ExprNode(AstNode* keyword, AstNode* name1, AstNode* name2, 148 | AstNode* opts, AstNode* body) { 149 | nodeType_ = typeid(*this).name(); 150 | keyword_ = dynamic_cast(keyword); 151 | name1_ = dynamic_cast(name1); 152 | if (name1_) name1_->parent_ = this; 153 | name2_ = name2; 154 | if (name2_) name2_->parent_ = this; 155 | opts_ = opts; 156 | if (opts_) opts_->parent_ = this; 157 | if(body->nodeType_.find("EmptyNode")!=string::npos) { 158 | body_ = new BodyNode(NULL, NULL, new BodyWordNode(BodyWordNode::STRING, new StrNode(new string("")))); 159 | } else { 160 | body_ = dynamic_cast(body); 161 | } 162 | if (body_) body_->parent_ = this; 163 | } 164 | 165 | string P4ExprNode::toString() { 166 | ostringstream oss; 167 | oss << keyword_->toString() << " " 168 | << name1_->toString() << " "; 169 | 170 | // Append name2 if its present 171 | if (name2_) { 172 | if (keyword_->toString().compare("calculated_field")==0) { 173 | oss << ". " 174 | << name2_->toString(); 175 | } else { 176 | oss << name2_->toString(); 177 | } 178 | } 179 | 180 | // Append opts if they're present 181 | if (opts_) { 182 | oss << "(" << opts_->toString() << ") "; 183 | } 184 | 185 | // Append body if its present. 186 | if (body_) { 187 | string res = body_->toString(); 188 | oss << "{\n" << res << "}\n"; 189 | } else { 190 | // If there's no body, its a statement that 191 | // ends with a semicolon 192 | oss << ";"; 193 | } 194 | return oss.str(); 195 | } 196 | 197 | KeywordNode::KeywordNode(string* word) { 198 | nodeType_ = typeid(*this).name(); 199 | word_ = word; 200 | } 201 | 202 | string KeywordNode::toString() { 203 | return *word_; 204 | } 205 | 206 | OptsNode::OptsNode(AstNode* nameList) { 207 | nodeType_ = typeid(*this).name(); 208 | nameList_ = nameList; 209 | } 210 | 211 | string OptsNode::toString() { 212 | if (nameList_) { 213 | return nameList_->toString(); 214 | } 215 | return ""; 216 | } 217 | 218 | NameListNode::NameListNode(AstNode* nameList, AstNode* name) { 219 | nodeType_ = typeid(*this).name(); 220 | nameList_ = nameList; 221 | name_ = name; 222 | } 223 | 224 | string NameListNode::toString() { 225 | if (nameList_) { 226 | ostringstream oss; 227 | oss << nameList_->toString() 228 | << ", " 229 | << name_->toString(); 230 | return oss.str(); 231 | } else { 232 | return name_->toString(); 233 | } 234 | 235 | } 236 | 237 | TableReadStmtNode::TableReadStmtNode(MatchType matchType, AstNode* field) { 238 | nodeType_ = typeid(*this).name(); 239 | matchType_ = matchType; 240 | field_ = field; 241 | field_->parent_ = this; 242 | } 243 | 244 | string TableReadStmtNode::toString() { 245 | ostringstream oss; 246 | oss << " " << field_->toString() << " : "; 247 | switch(matchType_) { 248 | case EXACT: oss << "exact"; break; 249 | case TERNARY: oss << "ternary"; break; 250 | } 251 | oss << ";\n"; 252 | return oss.str(); 253 | } 254 | 255 | TableReadStmtsNode::TableReadStmtsNode() { 256 | nodeType_ = typeid(*this).name(); 257 | } 258 | 259 | string TableReadStmtsNode::toString() { 260 | ostringstream oss; 261 | for (auto rsn : *list_) { 262 | oss << rsn->toString(); 263 | } 264 | return oss.str(); 265 | } 266 | 267 | TableActionStmtNode::TableActionStmtNode(AstNode* name) { 268 | nodeType_ = typeid(*this).name(); 269 | name_ = dynamic_cast(name); 270 | } 271 | 272 | string TableActionStmtNode::toString() { 273 | ostringstream oss; 274 | oss << " " << name_->toString() << ";\n"; 275 | return oss.str(); 276 | } 277 | 278 | TableActionStmtsNode::TableActionStmtsNode() { 279 | nodeType_ = typeid(*this).name(); 280 | } 281 | 282 | string TableActionStmtsNode::toString() { 283 | ostringstream oss; 284 | for (auto asn : *list_) { 285 | oss << asn->toString(); 286 | } 287 | return oss.str(); 288 | } 289 | 290 | TableNode::TableNode(AstNode* name, AstNode* reads, AstNode* actions, 291 | string options, string pragma) { 292 | nodeType_ = typeid(*this).name(); 293 | name_ = dynamic_cast(name); 294 | name_->parent_ = this; 295 | reads_ = dynamic_cast(reads); 296 | if (reads_) reads_->parent_ = this; 297 | actions_ = dynamic_cast(actions); 298 | actions_->parent_ = this; 299 | 300 | pragma_ = pragma; 301 | 302 | boost::algorithm::trim_right_if(options, boost::is_any_of("; \t\n")); 303 | if (options != "") { 304 | boost::split(options_, options, boost::is_any_of(";")); 305 | } 306 | for (int i = 0; i < options_.size(); i++){ 307 | boost::algorithm::trim(options_[i]); 308 | } 309 | isMalleable_ = false; 310 | pragmaTransformed_ = false; 311 | } 312 | 313 | string TableNode::toString() { 314 | ostringstream oss; 315 | if(pragma_.compare("")!=0) { 316 | oss << pragma_ << "\n"; 317 | } 318 | oss << "table " << name_->toString() << " {\n"; 319 | if (reads_ && reads_->list_->size() > 0) { 320 | oss << " reads {\n" 321 | << reads_->toString() 322 | << " }\n"; 323 | } 324 | oss << " actions {\n" 325 | << actions_->toString() 326 | << " }\n"; 327 | for (auto str : options_) { 328 | oss << " " << str << ";\n"; 329 | } 330 | oss << "}\n\n"; 331 | return oss.str(); 332 | } 333 | 334 | void TableNode::transformPragma() { 335 | if(pragma_.compare("")!=0 && !pragmaTransformed_) { 336 | std::vector tmp_v; 337 | boost::algorithm::split(tmp_v, pragma_, boost::algorithm::is_space()); 338 | pragma_ = ""; 339 | for(int i=0; i(name); 354 | size_ = dynamic_cast(size); 355 | } 356 | 357 | string FieldDecNode::toString() { 358 | ostringstream oss; 359 | oss << " " << name_->toString() << " : " << size_->toString() << ";"; 360 | return oss.str(); 361 | } 362 | 363 | FieldDecsNode::FieldDecsNode() { 364 | nodeType_ = typeid(*this).name(); 365 | } 366 | 367 | string FieldDecsNode::toString() { 368 | ostringstream oss; 369 | for (auto fdn : *list_) { 370 | oss << fdn->toString() << endl; 371 | } 372 | return oss.str(); 373 | } 374 | 375 | HeaderTypeDeclarationNode::HeaderTypeDeclarationNode(AstNode* name, 376 | AstNode* field_decs, 377 | AstNode* other_stmts) { 378 | nodeType_ = typeid(*this).name(); 379 | name_ = dynamic_cast(name); 380 | field_decs_ = dynamic_cast(field_decs); 381 | other_stmts_ = other_stmts; 382 | } 383 | 384 | string HeaderTypeDeclarationNode::toString() { 385 | ostringstream oss; 386 | oss << "header_type " << name_->toString() << " {\n"; 387 | oss << " fields {\n"; 388 | oss << field_decs_->toString(); 389 | oss << " }\n"; 390 | oss << other_stmts_->toString(); 391 | oss << "}\n\n"; 392 | return oss.str(); 393 | } 394 | 395 | HeaderInstanceNode::HeaderInstanceNode(AstNode* type, AstNode* name) { 396 | nodeType_ = typeid(*this).name(); 397 | type_ = dynamic_cast(type); 398 | name_ = dynamic_cast(name); 399 | } 400 | 401 | string HeaderInstanceNode::toString() { 402 | ostringstream oss; 403 | oss << "header " << type_->toString() << " " << name_->toString() << ";\n\n"; 404 | return oss.str(); 405 | } 406 | 407 | MetadataInstanceNode::MetadataInstanceNode(AstNode* type, AstNode* name) { 408 | nodeType_ = typeid(*this).name(); 409 | type_ = dynamic_cast(type); 410 | name_ = dynamic_cast(name); 411 | } 412 | 413 | string MetadataInstanceNode::toString() { 414 | ostringstream oss; 415 | oss << "metadata " << type_->toString() << " " << name_->toString() << ";\n\n"; 416 | return oss.str(); 417 | } 418 | 419 | ArgsNode::ArgsNode() { 420 | nodeType_ = typeid(*this).name(); 421 | } 422 | 423 | ArgsNode* ArgsNode::deepCopy() { 424 | auto newNode = new ArgsNode(); 425 | for (auto bw : *list_) { 426 | auto newArg = bw->deepCopy(); 427 | newNode->push_back(newArg); 428 | } 429 | 430 | return newNode; 431 | } 432 | 433 | string ArgsNode::toString() { 434 | bool first = true; 435 | ostringstream oss; 436 | for (auto bw : *list_) { 437 | if (first) { 438 | first = false; 439 | } else { 440 | oss << ", "; 441 | } 442 | oss << bw->toString(); 443 | } 444 | return oss.str(); 445 | } 446 | 447 | ActionParamNode::ActionParamNode(AstNode* param) { 448 | nodeType_ = typeid(*this).name(); 449 | param_ = param; 450 | param->parent_ = this; 451 | } 452 | 453 | ActionParamNode* ActionParamNode::deepCopy() { 454 | AstNode* newParam = param_; 455 | if (param_->nodeType_.find("MblRefNode") != string::npos) { 456 | newParam = dynamic_cast(param_)->deepCopy(); 457 | } 458 | auto newNode = new ActionParamNode(newParam); 459 | return newNode; 460 | } 461 | 462 | string ActionParamNode::toString() { 463 | return param_->toString(); 464 | } 465 | 466 | ActionParamsNode::ActionParamsNode() { 467 | nodeType_ = typeid(*this).name(); 468 | } 469 | 470 | ActionParamsNode* ActionParamsNode::deepCopy() { 471 | auto newNode = new ActionParamsNode(); 472 | for (auto ap : *list_) { 473 | auto newParam = ap->deepCopy(); 474 | newNode->push_back(newParam); 475 | } 476 | 477 | return newNode; 478 | } 479 | 480 | string ActionParamsNode::toString(){ 481 | bool first = true; 482 | ostringstream oss; 483 | for (auto ap : *list_) { 484 | if (first) { 485 | first = false; 486 | } else { 487 | oss << ", "; 488 | } 489 | oss << ap->toString(); 490 | } 491 | return oss.str(); 492 | } 493 | 494 | ActionStmtNode::ActionStmtNode(AstNode* name1, AstNode* args, ActionStmtType type, AstNode* name2, AstNode* index) { 495 | nodeType_ = typeid(*this).name(); 496 | name1_ = dynamic_cast(name1); 497 | name2_ = dynamic_cast(name2); 498 | if(args) { 499 | args_ = dynamic_cast(args); 500 | args_->parent_ = this; 501 | } 502 | 503 | index_ = dynamic_cast(index); 504 | type_ = type; 505 | } 506 | 507 | ActionStmtNode* ActionStmtNode::deepCopy() { 508 | return new ActionStmtNode(name1_, args_->deepCopy(), type_, name2_, index_); 509 | } 510 | 511 | string ActionStmtNode::toString() { 512 | ostringstream oss; 513 | if(type_==ActionStmtType::NAME_ARGLIST) { 514 | oss << " " 515 | << name1_->toString() 516 | << "(" 517 | << args_->toString() 518 | << ");\n"; 519 | } else if (type_==ActionStmtType::PROG_EXEC) { 520 | oss << " " 521 | << name1_->toString() 522 | << " . " 523 | << name2_->toString() 524 | << " ( " 525 | << args_->toString() 526 | << " );\n"; 527 | } else { 528 | oss << "parsing error!\n"; 529 | } 530 | return oss.str(); 531 | } 532 | 533 | ActionStmtsNode::ActionStmtsNode() { 534 | nodeType_ = typeid(*this).name(); 535 | } 536 | 537 | ActionStmtsNode* ActionStmtsNode::deepCopy() { 538 | auto newNode = new ActionStmtsNode(); 539 | for (auto as : *list_) { 540 | auto newStmt = as->deepCopy(); 541 | newNode->push_back(newStmt); 542 | } 543 | 544 | return newNode; 545 | } 546 | 547 | string ActionStmtsNode::toString() { 548 | ostringstream oss; 549 | for (auto as : *list_) { 550 | oss << as->toString(); 551 | } 552 | return oss.str(); 553 | } 554 | 555 | ActionNode::ActionNode(AstNode* name, AstNode* params, AstNode* stmts) { 556 | nodeType_ = typeid(*this).name(); 557 | name_ = dynamic_cast(name); 558 | name_->parent_ = this; 559 | params_ = dynamic_cast(params); 560 | params_->parent_ = this; 561 | stmts_ = dynamic_cast(stmts); 562 | stmts_->parent_ = this; 563 | } 564 | 565 | ActionNode* ActionNode::duplicateAction(const string& name) { 566 | auto newNode = new ActionNode(new NameNode(new string(name)), 567 | params_->deepCopy(), stmts_->deepCopy()); 568 | return newNode; 569 | } 570 | 571 | string ActionNode::toString() { 572 | ostringstream oss; 573 | oss << "action " << name_->toString() 574 | << "(" << params_->toString() << ") {\n" 575 | << stmts_->toString() 576 | << "}\n"; 577 | return oss.str(); 578 | } 579 | -------------------------------------------------------------------------------- /src/ast/ast_nodes_p4r.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include "../../include/ast_nodes_p4r.h" 17 | 18 | using namespace std; 19 | 20 | 21 | P4RExprNode::P4RExprNode(AstNode* varOrReaction) { 22 | nodeType_ = typeid(*this).name(); 23 | varOrReaction_ = varOrReaction; 24 | } 25 | 26 | string P4RExprNode::toString() { 27 | return varOrReaction_->toString(); 28 | } 29 | 30 | P4RSettableMalleableNode::P4RSettableMalleableNode(string nodeType, AstNode* name, 31 | AstNode* varWidth, 32 | MalleableType malleableType) 33 | : malleableType_(malleableType) { 34 | nodeType_ = nodeType; 35 | name_ = dynamic_cast(name); 36 | varWidth_ = dynamic_cast(varWidth); 37 | } 38 | 39 | P4RMalleableValueNode::P4RMalleableValueNode(AstNode* name, AstNode* varWidth, 40 | AstNode* varInit) 41 | : P4RSettableMalleableNode(typeid(*this).name(), name, varWidth, 42 | P4RSettableMalleableNode::VALUE) { 43 | varInit_ = varInit; 44 | } 45 | 46 | string P4RMalleableValueNode::toString() { 47 | if (removed_) { 48 | return ""; 49 | } 50 | 51 | ostringstream oss; 52 | oss << "Malleable value " << name_->toString() << " {\n" 53 | << " " << varWidth_->toString() << "\n" 54 | << " " << varInit_->toString() << "\n" 55 | << "}"; 56 | return oss.str(); 57 | } 58 | 59 | FieldNode::FieldNode(AstNode* headerName, AstNode* fieldName) { 60 | nodeType_ = typeid(*this).name(); 61 | headerName_ = dynamic_cast(headerName); 62 | fieldName_ = dynamic_cast(fieldName); 63 | } 64 | 65 | string FieldNode::toString() { 66 | ostringstream oss; 67 | oss << headerName_->toString() 68 | << "." 69 | << fieldName_->toString(); 70 | return oss.str(); 71 | } 72 | 73 | FieldsNode::FieldsNode() { 74 | nodeType_ = typeid(*this).name(); 75 | } 76 | 77 | string FieldsNode::toString() { 78 | bool first = true; 79 | ostringstream oss; 80 | 81 | for (auto fld : *this->list_) { 82 | if (first) { 83 | first = false; 84 | } else { 85 | oss << ", "; 86 | } 87 | oss << fld->toString(); 88 | } 89 | return oss.str(); 90 | } 91 | 92 | VarAltNode::VarAltNode(AstNode* fields) { 93 | nodeType_ = typeid(*this).name(); 94 | fields_ = dynamic_cast(fields); 95 | } 96 | 97 | string VarAltNode::toString() { 98 | ostringstream oss; 99 | oss << "alts: {" << fields_->toString() << "}"; 100 | return oss.str(); 101 | } 102 | 103 | P4RMalleableFieldNode::P4RMalleableFieldNode(AstNode* name, AstNode* varWidth, 104 | AstNode* varInit, AstNode* varAlts) 105 | : P4RSettableMalleableNode(typeid(*this).name(), name, varWidth, 106 | P4RSettableMalleableNode::FIELD) { 107 | varInit_ = varInit; 108 | varAlts_ = dynamic_cast(varAlts); 109 | } 110 | 111 | string P4RMalleableFieldNode::toString() { 112 | if (removed_) { 113 | return ""; 114 | } 115 | 116 | ostringstream oss; 117 | oss << "malleable field " << name_->toString() << " {\n" 118 | << " " << varWidth_->toString() << "\n" 119 | << " " << varInit_->toString() << "\n" 120 | << " " << varAlts_->toString() << "\n" 121 | << "}"; 122 | return oss.str(); 123 | } 124 | 125 | int P4RMalleableFieldNode::mapAltToInt(std::string input) { 126 | int val_int = 0; 127 | for (FieldNode* alt : *varAlts_->fields_->list_) { 128 | if (alt->toString().compare(input)==0) { 129 | return val_int; 130 | } else { 131 | val_int += 1; 132 | } 133 | } 134 | return -1; 135 | } 136 | 137 | P4RMalleableTableNode::P4RMalleableTableNode(AstNode* table, std::string pragma) { 138 | nodeType_ = typeid(*this).name(); 139 | table_ = dynamic_cast(table); 140 | 141 | pragmaTransformed_ = false; 142 | pragma_ = pragma; 143 | } 144 | 145 | void P4RMalleableTableNode::transformPragma() { 146 | if(pragma_.compare("")!=0 && !pragmaTransformed_) { 147 | std::vector tmp_v; 148 | boost::algorithm::split(tmp_v, pragma_, boost::algorithm::is_space()); 149 | pragma_ = ""; 150 | for(int i=0; itoString(); 173 | return oss.str(); 174 | } 175 | 176 | VarWidthNode::VarWidthNode(AstNode* val) { 177 | nodeType_ = typeid(*this).name(); 178 | val_ = dynamic_cast(val); 179 | } 180 | 181 | string VarWidthNode::toString() { 182 | ostringstream oss; 183 | oss << "width: " << val_->toString() << ";"; 184 | return oss.str(); 185 | } 186 | 187 | VarInitNode::VarInitNode(AstNode* val) { 188 | nodeType_ = typeid(*this).name(); 189 | val_ = val; 190 | } 191 | 192 | string VarInitNode::toString() { 193 | ostringstream oss; 194 | oss << "init: " << val_->toString() << ";"; 195 | return oss.str(); 196 | } 197 | 198 | /** 199 | * 200 | * Initialization block (assume a global block). 201 | * 202 | */ 203 | 204 | P4RInitBlockNode::P4RInitBlockNode(AstNode* name, AstNode* body) { 205 | nodeType_ = typeid(*this).name(); 206 | name_ = dynamic_cast(name); 207 | name_->parent_ = this; 208 | body_ = dynamic_cast(body); 209 | body_->parent_ = this; 210 | } 211 | 212 | string P4RInitBlockNode::toString() { 213 | if (removed_) { 214 | return ""; 215 | } 216 | 217 | ostringstream oss; 218 | oss << "initialization " 219 | << name_->toString() 220 | << " {\n" 221 | << body_->toString() 222 | << "}"; 223 | return oss.str(); 224 | } 225 | 226 | /** 227 | * 228 | * Reaction and subnodes. 229 | * 230 | */ 231 | P4RReactionNode::P4RReactionNode(AstNode* name, AstNode* args, AstNode* body) { 232 | nodeType_ = typeid(*this).name(); 233 | name_ = dynamic_cast(name); 234 | name_->parent_ = this; 235 | args_ = dynamic_cast(args); 236 | args_->parent_ = this; 237 | if(body->nodeType_.find("EmptyNode")!=string::npos) { 238 | // if body is empty node 239 | body_ = new BodyNode(NULL, NULL, new BodyWordNode(BodyWordNode::STRING, new StrNode(new string("")))); 240 | } else { 241 | body_ = dynamic_cast(body); 242 | } 243 | body_->parent_ = this; 244 | } 245 | 246 | string P4RReactionNode::toString() { 247 | if (removed_) { 248 | return ""; 249 | } 250 | 251 | ostringstream oss; 252 | oss << "reaction " 253 | << name_->toString() 254 | << " (" 255 | << args_->toString() 256 | << " ) {\n" 257 | << body_->toString() 258 | << "}"; 259 | return oss.str(); 260 | } 261 | 262 | ReactionArgsNode::ReactionArgsNode() { 263 | nodeType_ = typeid(*this).name(); 264 | } 265 | 266 | string ReactionArgsNode::toString() { 267 | bool first = true; 268 | ostringstream oss; 269 | for (auto ra : *list_) { 270 | if (first) { 271 | first = false; 272 | } else { 273 | oss << ", "; 274 | } 275 | oss << ra->toString(); 276 | } 277 | return oss.str(); 278 | } 279 | 280 | ReactionArgNode::ReactionArgNode(const ArgType& argType, AstNode* arg, AstNode* index1, AstNode* index2) 281 | : argType_(argType) { 282 | nodeType_ = typeid(*this).name(); 283 | arg_ = arg; 284 | arg_->parent_ = this; 285 | 286 | index1_ = dynamic_cast(index1); 287 | index2_ = dynamic_cast(index2); 288 | } 289 | 290 | string ReactionArgNode::toString() { 291 | return arg_->toString(); 292 | } 293 | 294 | MblRefNode::MblRefNode(AstNode* name) { 295 | nodeType_ = typeid(*this).name(); 296 | name_ = dynamic_cast(name); 297 | } 298 | 299 | void MblRefNode::transform(const string& transformedHeader, 300 | const string& transformedField) { 301 | transformed_ = true; 302 | transformedHeader_ = transformedHeader; 303 | transformedField_ = transformedField; 304 | } 305 | 306 | MblRefNode* MblRefNode::deepCopy() { 307 | auto newNode = new MblRefNode(name_->deepCopy()); 308 | newNode->transformed_ = transformed_; 309 | newNode->transformedHeader_ = transformedHeader_; 310 | newNode->transformedField_ = transformedField_; 311 | 312 | return newNode; 313 | } 314 | 315 | string MblRefNode::toString() { 316 | if (transformed_) { 317 | if (!inReaction_) { 318 | return transformedHeader_ + "." + transformedField_; 319 | } 320 | } else { 321 | ostringstream oss; 322 | oss << "${" << name_->toString() << "}"; 323 | return oss.str(); 324 | } 325 | } 326 | 327 | UnanchoredNode::UnanchoredNode(string* newCode, string* objType, 328 | string* objName) { 329 | nodeType_ = typeid(*this).name(); 330 | codeBlob_ = newCode; 331 | objType_ = objType; 332 | objName_ = objName; 333 | } 334 | 335 | string UnanchoredNode::toString() { 336 | ostringstream oss; 337 | oss << *codeBlob_; 338 | return oss.str(); 339 | } 340 | -------------------------------------------------------------------------------- /src/compile/compile.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "../../include/compile.h" 23 | #include "../../include/find_nodes.h" 24 | #include "../../include/helper.h" 25 | 26 | #include "compile_const.h" 27 | #include "compile_p4.h" 28 | #include "compile_c.h" 29 | 30 | static int ing_iso_opt = -1; 31 | static int egr_iso_opt = -1; 32 | static int num_init_mbls_ing = -1; 33 | static int num_init_mbls_egr = -1; 34 | static vector global_ing_bins; 35 | static vector global_egr_bins; 36 | static unordered_map mblUsages; 37 | static unordered_map mblValues; 38 | static unordered_map mblFields; 39 | static unordered_map mblTables; 40 | static unordered_map> tableMbls; 41 | static unordered_map> actionMbls; 42 | 43 | 44 | vector compileP4Code(vector* nodeArray) { 45 | 46 | ing_iso_opt = inferIsoOptForIng(nodeArray, true); 47 | egr_iso_opt = inferIsoOptForIng(nodeArray, false); 48 | 49 | transformPragma(nodeArray); 50 | 51 | findAndRemoveMalleables(&mblValues, &mblFields, &mblTables, *nodeArray); 52 | 53 | vector mblRefs; 54 | findMalleableRefs(&mblRefs, *nodeArray); 55 | 56 | // Visitor pass to find the corresponding usage 57 | findMalleableUsage(mblRefs, mblValues, mblFields, nodeArray, &mblUsages); 58 | 59 | // Transform all references to mbls into references to the appropriate metadata 60 | transformMalleableRefs(&mblRefs, mblValues, mblFields, nodeArray); 61 | 62 | transformMalleableTables(&mblTables, *nodeArray, ing_iso_opt, egr_iso_opt); 63 | 64 | auto newNodes = vector(); 65 | 66 | generateMetadata(&newNodes, mblValues, mblFields, ing_iso_opt, egr_iso_opt); 67 | num_init_mbls_ing = generateInitTableForIng(&mblUsages, &newNodes, mblValues, mblFields, ing_iso_opt, true); 68 | num_init_mbls_egr = generateInitTableForIng(&mblUsages, &newNodes, mblValues, mblFields, egr_iso_opt, false); 69 | 70 | generateSetvarControl(&newNodes); 71 | 72 | // Measurement code 73 | HeaderDecsMap headerDecsMap = findHeaderDecs(*nodeArray); 74 | vector reaction_args = findReactionArgs(*nodeArray); 75 | 76 | global_ing_bins = generateIngDigestPacking(&newNodes, reaction_args, headerDecsMap, 77 | mblValues, mblFields, *nodeArray, &ing_iso_opt); 78 | global_egr_bins = generateEgrDigestPacking(&newNodes, reaction_args, headerDecsMap, 79 | mblValues, mblFields, *nodeArray, &egr_iso_opt); 80 | 81 | // After packing, iso_opt is firm 82 | augmentRegisterArgProgForIng(&newNodes, nodeArray, reaction_args, ing_iso_opt, true); 83 | augmentRegisterArgProgForIng(&newNodes, nodeArray, reaction_args, egr_iso_opt, false); 84 | 85 | generateDupRegArgProg(&newNodes, nodeArray, reaction_args, ing_iso_opt, egr_iso_opt); 86 | 87 | generateRegArgGateControl(&newNodes, nodeArray, reaction_args, ing_iso_opt, egr_iso_opt); 88 | 89 | generateExportControl(&newNodes, global_ing_bins, global_egr_bins); 90 | 91 | // Finally, assemble ingress/egress 92 | augmentIngress(nodeArray); 93 | augmentEgress(nodeArray); 94 | 95 | return newNodes; 96 | } 97 | 98 | vector compileCCode(std::vector nodeArray, char * outFnBase) { 99 | vector ret_vec = vector(); 100 | 101 | string out_fn_base_str = string(outFnBase); 102 | string base_fn = out_fn_base_str.substr(out_fn_base_str.find_last_of("/\\") + 1); 103 | PRINT_VERBOSE("Output filename base: %s\n", base_fn.c_str()); 104 | string prefix_str = "p4_pd_" + string(base_fn) + "_mantis_"; 105 | 106 | ostringstream oss_preprocessor; 107 | ostringstream oss_mbl_init; 108 | ostringstream oss_init_end; 109 | ostringstream oss_reaction_mirror; 110 | ostringstream oss_reaction_update; 111 | 112 | extractReactionMacro(nodeArray, oss_preprocessor, string(outFnBase)); 113 | 114 | generateMacroNonMblTable(nodeArray, oss_preprocessor, prefix_str); 115 | 116 | generatePrologueEnd(nodeArray, oss_init_end, ing_iso_opt, egr_iso_opt); 117 | 118 | generateHdlPool(nodeArray, oss_mbl_init, ing_iso_opt, egr_iso_opt); 119 | 120 | generateMacroInitMblsForIng(nodeArray, &mblUsages, oss_mbl_init, oss_reaction_mirror, oss_preprocessor, num_init_mbls_ing, ing_iso_opt, prefix_str, true); 121 | generateMacroInitMblsForIng(nodeArray, &mblUsages, oss_mbl_init, oss_reaction_mirror, oss_preprocessor, num_init_mbls_egr, egr_iso_opt, prefix_str, false); 122 | 123 | generateMacroXorVersionBits(oss_reaction_mirror, oss_preprocessor, ing_iso_opt, egr_iso_opt); 124 | 125 | generateDialogueArgStart(oss_reaction_mirror, ing_iso_opt, egr_iso_opt); 126 | 127 | mirrorFieldArg(nodeArray, oss_reaction_mirror, oss_preprocessor, global_ing_bins, prefix_str, true); 128 | mirrorFieldArg(nodeArray, oss_reaction_mirror, oss_preprocessor, global_egr_bins, prefix_str, false); 129 | 130 | mirrorRegisterArgForIng(nodeArray, oss_reaction_mirror, ing_iso_opt, prefix_str, true); 131 | mirrorRegisterArgForIng(nodeArray, oss_reaction_mirror, egr_iso_opt, prefix_str, false); 132 | 133 | generateMacroMblTable(nodeArray, oss_preprocessor, prefix_str, ing_iso_opt, egr_iso_opt, oss_reaction_mirror); 134 | 135 | generateDialogueEnd(nodeArray, oss_reaction_update, ing_iso_opt, egr_iso_opt); 136 | 137 | ret_vec.push_back(generateMacroNode(oss_preprocessor)); 138 | ret_vec.push_back(generatePrologueNode(nodeArray, oss_mbl_init, oss_init_end)); 139 | ret_vec.push_back(generateDialogueNode(nodeArray, oss_reaction_mirror, oss_reaction_update)); 140 | 141 | return ret_vec; 142 | } -------------------------------------------------------------------------------- /src/compile/compile_c.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef COMPILE_C_H 17 | #define COMPILE_C_H 18 | 19 | #include 20 | #include 21 | 22 | void extractReactionMacro(std::vector nodeArray, ostringstream& oss_preprocessor, string out_fn_base); 23 | 24 | UnanchoredNode* generatePrologueNode(std::vector nodeArray, ostringstream& oss_variable_init, ostringstream& oss_init_end); 25 | 26 | UnanchoredNode* generateDialogueNode(std::vector nodeArray, ostringstream& oss_reaction_start, ostringstream& oss_reaction_end); 27 | 28 | UnanchoredNode * generateMacroNode(ostringstream& oss_preprocessor); 29 | 30 | void mirrorFieldArg(std::vector nodeArray, ostringstream& oss_reaction_start, 31 | ostringstream& oss_preprocessor, vector bins, 32 | string prefix_str, bool forIng); 33 | 34 | void mirrorRegisterArgForIng(std::vector nodeArray, ostringstream& oss_reaction_start, int iso_opt, string prefix_str, bool forIng); 35 | 36 | void generateMacroXorVersionBits(ostringstream& oss_reaction_start, ostringstream& oss_preprocessor, int ing_iso_opt, int egr_iso_opt); 37 | 38 | void generateDialogueArgStart(ostringstream& oss_reaction_start, int ing_iso_opt, int egr_iso_opt); 39 | 40 | void generateMacroNonMblTable(std::vector nodeArray, ostringstream& oss_preprocessor, string prefix_str); 41 | 42 | void generateMacroMblTable(std::vector nodeArray, ostringstream& oss_preprocessor, string prefix_str, int ing_iso_opt, int egr_iso_opt, ostringstream& oss_reaction_mirror); 43 | 44 | void generateMacroInitMblsForIng(std::vector nodeArray, unordered_map* mblUsages, ostringstream& oss_variable_init, ostringstream& oss_reaction_start, 45 | ostringstream& oss_preprocessor, int num_vars, int iso_opt, string prefix_str, bool forIng); 46 | 47 | void generateHdlPool(std::vector nodeArray, ostringstream& oss_mbl_init, int ing_iso_opt, int egr_iso_opt); 48 | 49 | void generatePrologueEnd(std::vector nodeArray, ostringstream& oss_init_end, int ing_iso_opt, int egr_iso_opt); 50 | 51 | void generateDialogueEnd(std::vector nodeArray, ostringstream& oss_reaction_end, int ing_iso_opt, int egr_iso_opt); 52 | 53 | #endif -------------------------------------------------------------------------------- /src/compile/compile_const.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef COMPILE_CONST_H 17 | #define COMPILE_CONST_H 18 | 19 | const char* const kOrigIngControlName = "originalIngress"; 20 | const char* const kSetmblIngControlName = "__ciSetMbls"; 21 | const char* const kSetargsIngControlName = "__ciSetArgs"; 22 | const char* const kSetmblEgrControlName = "__ceSetMbls"; 23 | const char* const kSetargsEgrControlName = "__ceSetArgs"; 24 | const char* const kOrigEgrControlName = "originalEgress"; 25 | const char* const kRegArgGateEgrControlName = "__ceRegArgGate"; 26 | const char* const kRegArgGateIngControlName = "__ciRegArgGate"; 27 | const char* const kP4rIngMetadataType = "__P4RIngMeta_t"; 28 | const char* const kP4rIngMetadataName = "__P4RIngMeta"; 29 | const char* const kP4rEgrMetadataType = "__P4REgrMeta_t"; 30 | const char* const kP4rEgrMetadataName = "__P4REgrMeta"; 31 | const char* const kP4rRegReplicasSuffix0 = "__P4Rreplicas0"; 32 | const char* const kP4rRegReplicasSuffix1 = "__P4Rreplicas1"; 33 | const char* const kP4rRegReplicasTablePrefix = "__tr__"; 34 | const char* const kP4rRegReplicasBlackboxPrefix = "__br__"; 35 | const char* const kP4rRegReplicasActionPrefix = "__ar__"; 36 | const char* const kP4rIngRegMetadataType = "__P4RIngRegMeta_t"; 37 | const char* const kP4rIngRegMetadataName = "__P4RIngRegMeta"; 38 | const char* const kP4rEgrRegMetadataType = "__P4REgrRegMeta_t"; 39 | const char* const kP4rEgrRegMetadataName = "__P4REgrRegMeta"; 40 | const char* const kP4rRegMetadataOutputSuffix = "__output"; 41 | const char* const kP4rRegMetadataIndexSuffix = "__index"; 42 | const char* const kP4rIndexSuffix = "__alt"; 43 | const char* const kP4rIngInitAction= "__aiSetVars"; 44 | const char* const kP4rEgrInitAction= "__aeSetVars"; 45 | const char* const kP4rIngArghdrType = "__packedIngArgs_t"; 46 | const char* const kP4rIngArghdrName = "__packedIngArgs"; 47 | const char* const kP4rEgrArghdrType = "__packedEgrArgs_t"; 48 | const char* const kP4rEgrArghdrName = "__packedEgrArgs"; 49 | 50 | const char* const kMantisNl = "_MANTIS_NL_"; 51 | const char* const kMatchSuffixStr = "match_spec_t"; 52 | const char* const kActionSuffixStr = "action_spec_t"; 53 | const char* const kErrorCheckStr = "if(__mantis__status_tmp!=0) {return false;}_MANTIS_NL_"; 54 | 55 | static int kHandlerOffset = 2; 56 | static int kIngInitEntryHandlerIndex = 0; 57 | static int kEgrInitEntryHandlerIndex = 1; 58 | // Number of concurrent user handlers 59 | static int kNumUserHdls = 5000; 60 | 61 | // %1%: reg width 62 | // %2%: data plane reg name 63 | // %3%: reg size 64 | // %4%: prefix_str 65 | // %5%: number of items to read 66 | // %6%: reg arg name 67 | const char * const kRegArgMirrorT_32 = 68 | R"( 69 | uint%1%_t __mantis__values_%2%[4*%3%]; 70 | __mantis__status_tmp = %4%register_range_read_%2%(sess_hdl, pipe_mgr_dev_tgt, 0, %5%, __mantis__reg_flags, &__mantis__num_actually_read, __mantis__values_%2%, &__mantis__value_count); 71 | if(__mantis__status_tmp!=0) { 72 | return false; 73 | } 74 | // Mirror %6% 75 | uint%1%_t %6%[%3%]; 76 | for (__mantis__i=0; __mantis__i < %5%; __mantis__i++) { 77 | %6%[__mantis__i] = __mantis__values_%2%[1+__mantis__i*2]; 78 | } 79 | )"; 80 | 81 | // %1%: data plane reg name 82 | // %2%: reg size 83 | // %3%: prefix_str 84 | // %4%: number of items to read 85 | // %5%: reg arg name 86 | const char * const kRegArgMirrorT_64 = 87 | R"( 88 | %3%%1%_value_t __mantis__values_%1%[4*%2%]; 89 | __mantis__status_tmp = %3%register_range_read_%1%(sess_hdl, pipe_mgr_dev_tgt, 0, %4%, __mantis__reg_flags, &__mantis__num_actually_read, __mantis__values_%1%, &__mantis__value_count); 90 | if(__mantis__status_tmp!=0) { 91 | return false; 92 | } 93 | // Mirror %5% 94 | %3%%1%_value_t %5%[%2%]; 95 | for (__mantis__i=0; __mantis__i < %4%; __mantis__i++) { 96 | %5%[__mantis__i] = __mantis__values_%1%[1+__mantis__i*2]; 97 | } 98 | )"; 99 | 100 | // For 32b reg arg only 101 | // %1%: reg arg name 102 | // %2%: reg arg width 103 | // %3%: reg arg size 104 | // %4%: prefix str 105 | // %5%: number of items to read 106 | // %6%: mv bit var 107 | const char * const kRegArgIsoMirrorT = 108 | R"( 109 | // Mirror %1% 110 | uint%2%_t %1%[%3%]; 111 | static uint32_t %1%__tstamp__P4Rreplicas0[%3%]; 112 | static uint32_t %1%__tstamp__P4Rreplicas1[%3%]; 113 | if(%6%==0) { 114 | %4%%1%__P4Rreplicas0_value_t __mantis__values_%1%__P4Rreplicas0[4*%3%]; 115 | __mantis__status_tmp = %4%register_range_read_%1%__P4Rreplicas0(sess_hdl, pipe_mgr_dev_tgt, 0, %5%, __mantis__reg_flags, &__mantis__num_actually_read, __mantis__values_%1%__P4Rreplicas0, &__mantis__value_count); 116 | if(__mantis__status_tmp!=0) { 117 | return false; 118 | } 119 | for (__mantis__i=0; __mantis__i < %5%; __mantis__i++) { 120 | if(__mantis__values_%1%__P4Rreplicas0[1+__mantis__i*2].f0 > %1%__tstamp__P4Rreplicas0[__mantis__i]) { 121 | %1%[__mantis__i] = __mantis__values_%1%__P4Rreplicas0[1+__mantis__i*2].f1; 122 | %1%__tstamp__P4Rreplicas0[__mantis__i] = __mantis__values_%1%__P4Rreplicas0[1+__mantis__i*2].f0; 123 | } 124 | } 125 | } else { 126 | %4%%1%__P4Rreplicas0_value_t __mantis__values_%1%__P4Rreplicas1[4*%3%]; 127 | __mantis__status_tmp = %4%register_range_read_%1%__P4Rreplicas1(sess_hdl, pipe_mgr_dev_tgt, 0, %5%, __mantis__reg_flags, &__mantis__num_actually_read, __mantis__values_%1%__P4Rreplicas1, &__mantis__value_count); 128 | if(__mantis__status_tmp!=0) { 129 | return false; 130 | } 131 | for (__mantis__i=0; __mantis__i < %5%; __mantis__i++) { 132 | if(__mantis__values_%1%__P4Rreplicas1[1+__mantis__i*2].f0 > %1%__tstamp__P4Rreplicas1[__mantis__i]) { 133 | %1%[__mantis__i] = __mantis__values_%1%__P4Rreplicas1[1+__mantis__i*2].f1; 134 | %1%__tstamp__P4Rreplicas1[__mantis__i] = __mantis__values_%1%__P4Rreplicas1[1+__mantis__i*2].f0; 135 | } 136 | } 137 | } 138 | )"; 139 | 140 | // %1%: bin size 141 | // %2%: bin index 142 | // %3%: prefix_str 143 | const char * const kIngFieldArgPollT = 144 | R"( 145 | uint%1%_t __mantis__values_riSetArgs_%2%[4]; 146 | __mantis__status_tmp = %3%register_read___riSetArgs%2%(sess_hdl, pipe_mgr_dev_tgt, __mantis__mv_ing, __mantis__reg_flags, __mantis__values_riSetArgs_%2%, &__mantis__value_count); 147 | if(__mantis__status_tmp!=0) { 148 | return false; 149 | } 150 | )"; 151 | 152 | const char * const kEgrFieldArgPollT = 153 | R"( 154 | uint%1%_t __mantis__values_reSetArgs_%2%[4]; 155 | __mantis__status_tmp = %3%register_read___reSetArgs%2%(sess_hdl, pipe_mgr_dev_tgt, __mantis__mv_egr, __mantis__reg_flags, __mantis__values_reSetArgs_%2%, &__mantis__value_count); 156 | if(__mantis__status_tmp!=0) { 157 | return false; 158 | } 159 | )"; 160 | 161 | const char * const kPrologueT = 162 | R"( 163 | bool pd_prologue(uint32_t sess_hdl, dev_target_t pipe_mgr_dev_tgt, uint32_t* hdls) { 164 | uint32_t __mantis__status_tmp; 165 | 166 | %1% 167 | 168 | %2% 169 | 170 | %3% 171 | 172 | return true; 173 | } 174 | )"; 175 | 176 | const char * const kDialogueT = 177 | R"( 178 | bool pd_dialogue(uint32_t sess_hdl, dev_target_t pipe_mgr_dev_tgt, uint32_t* hdls) { 179 | uint32_t __mantis__status_tmp; 180 | 181 | %1% 182 | 183 | %2% 184 | 185 | %3% 186 | 187 | return true; 188 | } 189 | )"; 190 | 191 | #endif -------------------------------------------------------------------------------- /src/compile/compile_p4.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #ifndef COMPILE_P4_H 17 | #define COMPILE_P4_H 18 | 19 | #include 20 | #include 21 | 22 | #define REGISTER_SIZE 32 23 | 24 | typedef pair ReactionArgSize; 25 | typedef pair, int /* size */> ReactionArgBin; 26 | 27 | void transformPragma(vector* astNodes); 28 | 29 | int inferIsoOptForIng(vector* astNodes, bool forIng); 30 | 31 | bool augmentIngress(vector* astNodes); 32 | 33 | bool augmentEgress(vector* astNodes); 34 | 35 | enum USAGE { 36 | INGRESS = 0, 37 | EGRESS = 1, 38 | BOTH = 2 39 | }; 40 | void findMalleableUsage( 41 | vector mblRefs, 42 | const unordered_map mblValues, 43 | const unordered_map mblFields, 44 | vector* nodeArray, 45 | unordered_map* mblUsages); 46 | 47 | void transformMalleableRefs( 48 | vector* mblRefs, 49 | const unordered_map& mblValues, 50 | const unordered_map& mblFields, 51 | vector* nodeArray); 52 | 53 | void transformMalleableTables( 54 | unordered_map* mblTables, vector nodeArray, int ing_iso_opt, int egr_iso_opt); 55 | 56 | void generateExportControl(vector* newNodes, 57 | const vector& argBins, const vector& argBinsEgr); 58 | 59 | void augmentRegisterArgProgForIng(vector* newNodes, 60 | vector* nodeArray, 61 | const vector& reaction_args, 62 | int iso_opt, bool forIng); 63 | 64 | void generateRegArgGateControl(vector* newNodes, 65 | vector* nodeArray, 66 | const vector& reaction_args, 67 | int isolation_opt, int egr_iso_opt); 68 | 69 | void generateDupRegArgProg(vector* newNodes, 70 | vector* nodeArray, 71 | const vector& reaction_args, 72 | int isolation_opt, int egr_iso_opt); 73 | 74 | vector generateIngDigestPacking( 75 | vector* newNodes, 76 | const vector& reaction_args, 77 | const HeaderDecsMap& headerDecsMap, 78 | const unordered_map& mblValues, 79 | const unordered_map& mblFields, 80 | const vector& astNodes, 81 | int* ing_iso_opt); 82 | 83 | vector generateEgrDigestPacking( 84 | vector* newNodes, 85 | const vector& reaction_args, 86 | const HeaderDecsMap& headerDecsMap, 87 | const unordered_map& mblValues, 88 | const unordered_map& mblFields, 89 | const vector& astNodes, 90 | int* egr_iso_opt); 91 | 92 | // Generate metadata for dynamic malleables 93 | void generateMetadata(vector* newNodes, 94 | const unordered_map& mblValues, 96 | const unordered_map& mblFields, 98 | int ing_iso_opt, int egr_iso_opt); 99 | 100 | // Generate a merged table that sets all malleables 101 | int generateInitTableForIng(unordered_map* mblUsages, 102 | vector* newNodes, 103 | const unordered_map& mblValues, 105 | const unordered_map& mblFields, 107 | int iso_opt, bool forIng); 108 | 109 | void generateSetvarControl(vector* newNodes); 110 | 111 | #endif -------------------------------------------------------------------------------- /src/compile/find_nodes.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright 2020-present University of Pennsylvania 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #include "../../include/ast_nodes.h" 20 | #include "../../include/ast_nodes_p4.h" 21 | #include "../../include/ast_nodes_p4r.h" 22 | #include "../../include/helper.h" 23 | 24 | #include "compile_const.h" 25 | 26 | 27 | bool typeContains(AstNode* n, const char* type) { 28 | if (n->nodeType_.find(string(type)) != string::npos) { 29 | return true; 30 | } else { 31 | return false; 32 | } 33 | } 34 | 35 | bool p4KeywordMatches(P4ExprNode* n, const char* type) { 36 | if (n->keyword_->word_->compare(string(type)) == 0) { 37 | return true; 38 | } else { 39 | return false; 40 | } 41 | } 42 | 43 | P4ExprNode* findIngress(const std::vector& astNodes) { 44 | for (auto node : astNodes) { 45 | if (typeContains(node, "P4ExprNode")) { 46 | P4ExprNode* exprNode = dynamic_cast(node); 47 | // Might be called after ing transformation 48 | if (exprNode->name1_->toString().find("ingress") != string::npos || exprNode->name1_->toString().find(string(kOrigIngControlName)) != string::npos) { 49 | return exprNode; 50 | } 51 | } 52 | } 53 | 54 | return NULL; 55 | } 56 | 57 | P4ExprNode* findEgress(const std::vector& astNodes) { 58 | for (auto node : astNodes) { 59 | if (typeContains(node, "P4ExprNode")) { 60 | P4ExprNode* exprNode = dynamic_cast(node); 61 | if (exprNode->name1_->toString().find("egress") != string::npos || exprNode->name1_->toString().find(string(kOrigEgrControlName)) != string::npos) { 62 | return exprNode; 63 | } 64 | } 65 | } 66 | 67 | return NULL; 68 | } 69 | 70 | vector findBlackbox(const std::vector& astNodes) { 71 | vector ret; 72 | for (auto node : astNodes) { 73 | if (typeContains(node, "P4ExprNode")) { 74 | P4ExprNode* exprNode = dynamic_cast(node); 75 | if (exprNode->keyword_->toString().find("blackbox") != string::npos) { 76 | ret.push_back(exprNode); 77 | } 78 | } 79 | } 80 | return ret; 81 | } 82 | 83 | void findAndRemoveMalleables( 84 | unordered_map* mblValues, 85 | unordered_map* mblFields, 86 | unordered_map* mblTables, 87 | const vector& astNodes) { 88 | // Find all the Malleables 89 | for (auto n : astNodes) { 90 | if (typeContains(n, "P4RMalleableValueNode")) { 91 | auto v = dynamic_cast(n); 92 | mblValues->emplace(*v->name_->word_, v); 93 | n->removed_ = true; 94 | } else if (typeContains(n, "P4RMalleableFieldNode")) { 95 | auto v = dynamic_cast(n); 96 | mblFields->emplace(*v->name_->word_, v); 97 | n->removed_ = true; 98 | } else if (typeContains(n, "P4RMalleableTableNode")) { 99 | auto v = dynamic_cast(n); 100 | mblTables->emplace(*v->table_->name_->word_, v); 101 | // for a Malleable table, the parser will give both MalleableTableNode and TableNode, 102 | // we need to tag the latter as Malleable 103 | v->table_->isMalleable_ = true; 104 | n->removed_ = true; 105 | } else if (typeContains(n, "P4RInitBlockNode")) { 106 | n->removed_ = true; 107 | } 108 | } 109 | } 110 | 111 | void findMalleableRefs(vector* varRefs, 112 | const vector& astNodes) { 113 | for (AstNode* node : astNodes) { 114 | if (typeContains(node, "MblRefNode")) { 115 | MblRefNode* varRef = dynamic_cast(node); 116 | varRefs->push_back(varRef); 117 | } 118 | } 119 | } 120 | 121 | unordered_map findTableActionStmts( 122 | const vector& astNodes, const string& actionName) { 123 | auto ret = unordered_map(); 124 | 125 | for (auto node : astNodes) { 126 | if (typeContains(node, "TableNode") && !typeContains(node, "P4R")) { 127 | TableNode* table = dynamic_cast(node); 128 | TableActionStmtsNode* actions = table->actions_; 129 | for (TableActionStmtNode* tas : *actions->list_) { 130 | if (*tas->name_->word_ == actionName) { 131 | ret.emplace(tas, table); 132 | break; 133 | } 134 | } 135 | } 136 | } 137 | return ret; 138 | } 139 | 140 | bool findTableReadStmt(const TableNode& table, const string& fieldName) { 141 | TableReadStmtsNode* reads = table.reads_; 142 | for (TableReadStmtNode* trs : *reads->list_) { 143 | if (typeContains(trs->field_, "StrNode")) { 144 | StrNode* sn = dynamic_cast(trs->field_); 145 | if (*sn->word_ == fieldName) { 146 | return true; 147 | } 148 | } 149 | } 150 | 151 | return false; 152 | } 153 | 154 | void findAndTransformMblRefsInAction(ActionNode* action, 155 | vector* varRefs, 156 | const string& varName, 157 | const string& altHeader, 158 | const string& altField) { 159 | 160 | for (ActionStmtNode* as : *action->stmts_->list_) { 161 | for (BodyWordNode* bw : *as->args_->list_) { 162 | if (bw->wordType_ != BodyWordNode::VARREF) { 163 | continue; 164 | } 165 | 166 | auto varRef = dynamic_cast(bw->contents_); 167 | if (*varRef->name_->word_ == varName) { 168 | varRef->transform(altHeader, altField); 169 | } else if (varRefs && !varRef->transformed_) { 170 | varRefs->push_back(dynamic_cast(bw->contents_)); 171 | } 172 | } 173 | } 174 | } 175 | 176 | vector findAllAlts(const P4RMalleableFieldNode& malleable) { 177 | return vector(*malleable.varAlts_->fields_->list_); 178 | } 179 | 180 | P4RInitBlockNode* findInitBlock(std::vector astNodes){ 181 | // Currently get a single node, easily generalize to multiple distributed init/reaction block 182 | for (auto node : astNodes) { 183 | if (node->nodeType_.find(string("InitBlockNode")) != string::npos) { 184 | P4RInitBlockNode * iNode = dynamic_cast(node); 185 | return iNode; 186 | } 187 | } 188 | return 0; 189 | } 190 | 191 | P4RReactionNode* findReaction(std::vector astNodes){ 192 | for (auto node : astNodes) { 193 | if (node->nodeType_.find(string("ReactionNode")) != string::npos) { 194 | P4RReactionNode * reactionNode = dynamic_cast(node); 195 | return reactionNode; 196 | } 197 | } 198 | return 0; 199 | } 200 | 201 | vector findP4RegisterNode(const vector& astNodes) { 202 | vector ret; 203 | for (auto node : astNodes) { 204 | if (typeContains(node, "P4RegisterNode")) { 205 | ret.push_back(dynamic_cast(node)); 206 | } 207 | } 208 | return ret; 209 | } 210 | 211 | vector findReactionArgs(const vector& astNodes) { 212 | vector ret; 213 | // Find all the argument nodes 214 | for (auto node : astNodes) { 215 | if (typeContains(node, "ReactionArgNode")) { 216 | ret.push_back(dynamic_cast(node)); 217 | } 218 | } 219 | return ret; 220 | } 221 | 222 | typedef unordered_map*> HeaderDecsMap; 224 | HeaderDecsMap findHeaderDecs(const vector& astNodes) { 225 | unordered_map*> typeDecsMap; 227 | for (auto node : astNodes) { 228 | if (typeContains(node, "HeaderTypeDeclarationNode")) { 229 | auto headerDecl = dynamic_cast(node); 230 | typeDecsMap.emplace(headerDecl->name_->toString(), 231 | headerDecl->field_decs_->list_); 232 | } 233 | } 234 | 235 | HeaderDecsMap ret; 236 | for (auto node : astNodes) { 237 | if (typeContains(node, "HeaderInstanceNode")) { 238 | auto headerInstance = dynamic_cast(node); 239 | auto fieldDecVector = typeDecsMap.at(*headerInstance->type_->word_); 240 | ret.emplace(*headerInstance->name_->word_, fieldDecVector); 241 | } 242 | } 243 | 244 | return ret; 245 | } 246 | 247 | vector > findAllReactionArgSizes( 248 | const vector& reactionArgs, 249 | const HeaderDecsMap& headerDecsMap, 250 | const unordered_map& mblValues, 251 | const unordered_map& mblFields) { 252 | vector > argSizes; 253 | 254 | for (auto ra : reactionArgs) { 255 | switch (ra->argType_) { 256 | case ReactionArgNode::INGRESS_FIELD: 257 | case ReactionArgNode::EGRESS_FIELD: 258 | case ReactionArgNode::INGRESS_MBL_FIELD: 259 | case ReactionArgNode::EGRESS_MBL_FIELD: 260 | if (typeContains(ra->arg_, "FieldNode")) { 261 | // If it's a field node, grab from headerDecsMap 262 | auto fieldArg = dynamic_cast(ra->arg_); 263 | auto fieldDecs = headerDecsMap.at(*fieldArg->headerName_->word_); 264 | bool found = false; 265 | 266 | for (FieldDecNode* fd : *fieldDecs) { 267 | if (*fd->name_->word_ == *fieldArg->fieldName_->word_) { 268 | argSizes.emplace_back(ra, stoi(*fd->size_->word_)); 269 | found = true; 270 | break; 271 | } 272 | } 273 | assert(found); 274 | } else if (typeContains(ra->arg_, "MblRefNode")) { 275 | // If it's a var ref, find it 276 | auto varRefArg = dynamic_cast(ra->arg_); 277 | if (mblValues.find(*varRefArg->name_->word_) != mblValues.end()) { 278 | auto size = mblValues.at(*varRefArg->name_->word_)->varWidth_->val_->word_; 279 | argSizes.emplace_back(ra, stoi(*size)); 280 | } else { 281 | auto size = mblFields.at(*varRefArg->name_->word_)->varWidth_->val_->word_; 282 | argSizes.emplace_back(ra, stoi(*size)); 283 | } 284 | } else { 285 | assert(false && "Should never happen."); 286 | } 287 | break; 288 | case ReactionArgNode::REGISTER: 289 | // Not taken into account when bin-packing 290 | break; 291 | } 292 | } 293 | 294 | return argSizes; 295 | } 296 | 297 | void findAndRemoveReactions(vector* reactions, 298 | const vector& astNodes) { 299 | // Find all the malleables 300 | for (auto n : astNodes) { 301 | if (typeContains(n, "P4RReactionNode")) { 302 | auto r = dynamic_cast(n); 303 | reactions->push_back(r); 304 | n->removed_ = true; 305 | } 306 | } 307 | } 308 | 309 | bool findTblInIng(string tableName, const vector& nodeArray) { 310 | P4ExprNode* ing_node = findIngress(nodeArray); 311 | P4ExprNode* egr_node = findEgress(nodeArray); 312 | // Currently assume that control ing/egr doesn't wrap other control blocks (otherwise requires recursive search) 313 | if(ing_node==NULL || egr_node==NULL) { 314 | PANIC("Missing ing/egr node for %s\n", tableName.c_str()); 315 | } 316 | if(ing_node->body_->toString().find(tableName) != string::npos) { 317 | return true; 318 | } else if (egr_node->body_->toString().find(tableName) != string::npos) { 319 | return false; 320 | } else { 321 | PANIC("Failed to locate %s in ing/egr control block\n", tableName.c_str()); 322 | } 323 | } 324 | 325 | bool findRegargInIng(ReactionArgNode* regarg, std::vector nodeArray) { 326 | 327 | if(regarg->argType_!=ReactionArgNode::REGISTER) { 328 | PRINT_VERBOSE("Miscall findRegargInIng for arg %s\n", regarg->toString().c_str()); 329 | return false; 330 | } 331 | 332 | vector blackboxes = findBlackbox(nodeArray); 333 | 334 | std::string prog_name; 335 | for (auto blackbox : blackboxes) { 336 | prog_name = blackbox->name2_->toString(); 337 | std::stringstream ss(blackbox->body_->toString()); 338 | std::string reg_name; 339 | std::string item; 340 | while (std::getline(ss, item, ';')) 341 | { 342 | std::stringstream ss_(item); 343 | std::string item_; 344 | while (std::getline(ss_, item_, ':')) { 345 | boost::algorithm::trim(item_); 346 | if(item_.compare("reg")==0) { 347 | std::getline(ss_, item_, ':'); 348 | boost::algorithm::trim(item_); 349 | reg_name = item_; 350 | } 351 | } 352 | } 353 | if(reg_name.compare(regarg->toString())==0) { 354 | break; 355 | } 356 | } 357 | string action_name; 358 | bool found = false; 359 | for (auto tmp_node : nodeArray) { 360 | if(typeContains(tmp_node, "ActionNode") && !typeContains(tmp_node, "P4R")) { 361 | ActionNode* tmp_action_node = dynamic_cast(tmp_node); 362 | string tmp_action_name = tmp_action_node->name_->toString(); 363 | ActionStmtsNode* actionstmts = tmp_action_node->stmts_; 364 | for (ActionStmtNode* as : *actionstmts->list_) { 365 | if(as->toString().find("execute_stateful_alu")!=string::npos && 366 | as->toString().find(prog_name)!=string::npos) { 367 | found = true; 368 | action_name = tmp_action_node->name_->toString(); 369 | break; 370 | } 371 | } 372 | if (found) { 373 | break; 374 | } 375 | } 376 | } 377 | if(!found) { 378 | PANIC("Action missing to execute the stateful alu for %s\n", regarg->toString().c_str()); 379 | } 380 | string table_name; 381 | found = false; 382 | for (auto node : nodeArray) { 383 | if(typeContains(node, "TableNode") && !typeContains(node, "P4R")) { 384 | TableNode* table = dynamic_cast(node); 385 | table_name = *(table->name_->word_); 386 | TableActionStmtsNode* actions = table->actions_; 387 | for (TableActionStmtNode* tas : *actions->list_) { 388 | string tmp_action_name = *tas->name_->word_; 389 | if(tmp_action_name.compare(action_name)==0) { 390 | found = true; 391 | break; 392 | } 393 | } 394 | if(found) { 395 | break; 396 | } 397 | } 398 | } 399 | if(!found) { 400 | PANIC("Table missing for %s\n", regarg->toString().c_str()); 401 | } 402 | // Now locate ing/egr for the table 403 | P4ExprNode* ing_node = findIngress(nodeArray); 404 | P4ExprNode* egr_node = findEgress(nodeArray); 405 | if(ing_node==NULL || egr_node==NULL) { 406 | PANIC("Missing ing/egr node for %s\n", regarg->toString().c_str()); 407 | } 408 | // A valid table will either be applied at ing or egr 409 | if(ing_node->body_->toString().find(table_name) != string::npos) { 410 | return true; 411 | } else if (egr_node->body_->toString().find(table_name) != string::npos) { 412 | return false; 413 | } else { 414 | PANIC("Failed to locate %s for %s\n", table_name.c_str(), regarg->toString().c_str()); 415 | } 416 | } 417 | 418 | int findRegargWidth(ReactionArgNode* regarg, std::vector nodeArray) { 419 | vector regNodes = findP4RegisterNode(nodeArray); 420 | for(auto reg : regNodes) { 421 | if(reg->name_->toString().compare(regarg->toString())==0) { 422 | return reg->width_; 423 | } 424 | } 425 | PANIC("Non existing reg arg %s\n", regarg->toString().c_str()); 426 | return -1; 427 | } -------------------------------------------------------------------------------- /tutorial.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | P4R is a simple extension to P4 language to express fine-grained, serializable, and switch-local reactive behaviors. 4 | 5 | ## Writing a P4R Program From Scratch 6 | 7 | P4R program can be decomposed into 3 steps. 8 | 9 | #### S1: Static P4 Code 10 | 11 | First, one writes a P4 code (specifically P4-14) as usual which defines a static packet processing behavior. 12 | Note that currently the compiler targets at P4-14 on Wedge100BF-32X Tofino switch. 13 | 14 | #### S2: Specifying Malleable Entities 15 | 16 | One could define malleable entities that are amenable to run time reconfiguration, for example: 17 | 18 | * [figure1.p4r](https://github.com/eniac/Mantis/blob/master/examples/figure4.p4r) shows a simple example declaring a malleable value `value_var` and uses it in `my_action` to change the variable added to `hdr.foo`. 19 | * [figure5.p4r](https://github.com/eniac/Mantis/blob/master/examples/figure5.p4r) defines a malleable field `write_var` and uses it in `my_action` so that `baz` (right hand side) is assigned to one of its `alts`. 20 | * [figure6.p4r](https://github.com/eniac/Mantis/blob/master/examples/figure6.p4r) also defines a malleble field `read_var` but uses it at the left hand side in an addition and `my_table` match, one could later change the references in the reaction during run time. 21 | * [mbl\_table.p4r](https://github.com/eniac/Mantis/blob/master/examples/mbl_table.p4r) defines a malleable table `ti_var_table` that is amenable to fine-grained manipulations ensuring serializability. 22 | 23 | #### S3: Define Reaction 24 | 25 | Reaction function is a C-like function, there are 3 components to specify: reaction arguments (objects in the data plane), control logic, and reconfiguration. 26 | 27 | *Reaction Arguments* 28 | 29 | Reaction arguments are used to specify the data plane metrics to be accessed as C variables/arrays, Mantis will take caure of the packing and the serializable reads underneath. 30 | For example: 31 | 32 | * [field\_arg.p4r](https://github.com/eniac/Mantis/blob/master/examples/field_arg.p4r) specifies field arguments of `hdr.foo` etc and accesses their values as normal C variables. 33 | * [failover\_tstamp.p4r](https://github.com/eniac/Mantis/blob/master/examples/failover_tstamp.p4r) specifies register arguments `ri_pkt_counter`, `ri_ingress_tstamp` and reads them as C arrays in the function. 34 | 35 | *Control Logic* 36 | 37 | One could specify arbitrary C code in the reaction function. Besides, one could specify static variables to retain stateful values across dialogues. 38 | 39 | *Reconfiguration* 40 | 41 | * To reconfigure malleable values, fields, one could leverage a simple syntax `=`, e.g., [figure1.p4r](https://github.com/eniac/Mantis/blob/master/examples/figure4.p4r). 42 | * To reconfigure tables, one could use a simple syntax: `__(, [match arguments], [priority, if ternary], [action arguments])`. [table\_add\_del\_mod.p4r](https://github.com/eniac/Mantis/blob/master/examples/table_add_del_mod.p4r) and [mbl\_table.p4r](https://github.com/eniac/Mantis/blob/master/examples/mbl_table.p4r) are examples showing the usage. 43 | 44 | Note that one could also specify a `init_block` besides `reaction`, which could be handy for setting up initial table entries, as in example [table\_add\_del\_mod.p4r](https://github.com/eniac/Mantis/blob/master/examples/table_add_del_mod.p4r) and [mbl\_table.p4r](https://github.com/eniac/Mantis/blob/master/examples/mbl_table.p4r). 45 | 46 | ## A Practical Example 47 | 48 | [dos.p4r](https://github.com/eniac/Mantis/blob/master/examples/dos.p4r) implements a simple Denial-of-Service reactive behavior that detects/blocks malicious flows with a simple threshold based approach. 49 | 50 | #### Example Setups 51 | 52 | *Topology* 53 | 54 | ``` 55 | S0(10.1.1.3)---switch 25Gbps (malicious sender) 56 | S1(10.1.1.5)---switch 10Gbps (benign sender) 57 | switch---D0(10.1.1.2) 10Gbps 58 | ``` 59 | 60 | *Configuration* 61 | 62 | Based on the testbed environment, set up the ports as in [pi\_setup.py](https://github.com/eniac/Mantis/blob/master/agent/pi_setup.py) and entries for connectivity (via RPC calls in [pi\_setup.py](https://github.com/eniac/Mantis/blob/master/agent/pi_setup.py), interactive switch commands, or `init_block` in p4r). 63 | 64 | Then run Mantis as in [How to Run](https://github.com/eniac/Mantis#how-to-run). 65 | 66 | We configure [DPDK](https://github.com/jsonch/dpdkScripts) senders for S0 and S1 with scripts [benign.dpdk](https://github.com/eniac/Mantis/blob/master/util/benign.dpdk) and [malicious.dpdk](https://github.com/eniac/Mantis/blob/master/util/malicious.dpdk). 67 | 68 | Note that this simple example doesn't add ARP protocol support, one needs to configure the static mappings for the servers. 69 | 70 | #### Expectation 71 | 72 | * The Mantis agent terminal should prints the infomation on new flows and the malicious flow (which is blocked). 73 | * At D0, one could record the pcap trace before launching senders via `sudo tshark -s 64 -B 1024 -i enp101s0f1 > tmp` and verify that after a small time window, packets from S0 no longer reach D0. 74 | -------------------------------------------------------------------------------- /util/benign.dpdk: -------------------------------------------------------------------------------- 1 | set 0 burst 1 2 | set 0 rate 20 3 | set 0 size 550 4 | enable 0 range 5 | page range 6 | range 0 src ip 10.2.2.0 10.2.2.0 10.2.3.0 0.0.0.1 7 | range 0 dst ip 10.1.1.2 10.1.1.2 10.1.1.2 0.0.0.1 8 | range 0 dst port 30000 30000 40000 1 9 | range 0 src port 30000 30000 40000 1 10 | range 0 proto tcp 11 | range 0 size 550 550 550 1 12 | stop 0 13 | start 0 -------------------------------------------------------------------------------- /util/malicious.dpdk: -------------------------------------------------------------------------------- 1 | set 0 size 550 2 | set 0 proto udp 3 | set 0 dst ip 10.1.1.2 4 | set 0 burst 32 5 | set 0 rate 100 6 | set 0 dport 45555 7 | set 0 sport 30000 8 | set 0 src ip 10.1.1.3/32 9 | stop 0 10 | start 0 -------------------------------------------------------------------------------- /util/p4_14_compile.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Usage: 4 | # sudo ./p4_14_compile.sh 5 | 6 | export P4C=$SDE"/install/bin/bf-p4c" 7 | export CURR=$(pwd) 8 | export SDE_BUILD=$SDE"/build" 9 | export SDE_INSTALL=$SDE"/install" 10 | export PATH=$SDE_INSTALL/bin:$PATH 11 | 12 | prog_name=$(basename $1 .p4) 13 | P4_BUILD=$SDE_BUILD"/mantis" 14 | 15 | check_env() { 16 | if [ -z $SDE ]; then 17 | echo "ERROR: SDE Environment variable is not set" 18 | exit 1 19 | else 20 | echo "Using SDE ${SDE}" 21 | fi 22 | 23 | if [ ! -d $SDE ]; then 24 | echo " ERROR: \$SDE ($SDE) is not a directory" 25 | exit 1 26 | fi 27 | 28 | return 0 29 | } 30 | 31 | build_p4_14() { 32 | 33 | cd $SDE/pkgsrc/p4-build 34 | ./configure \ 35 | --prefix=$SDE_INSTALL --enable-thrift --with-tofino \ 36 | P4_NAME=$prog_name P4_PATH=$1 P4_ARCHITECTURE=tna P4_VERSION=p4-14 P4FLAGS="-g --create-graphs" && 37 | 38 | make clean && make && make install && 39 | 40 | CONF_IN=${SDE}"/pkgsrc/p4-examples/tofino/tofino_single_device.conf.in" 41 | 42 | if [ ! -f $CONF_IN ]; then 43 | cat < ${CONF_OUT_DIR}/${prog_name}.conf 53 | 54 | return 0 55 | } 56 | 57 | check_env 58 | build_p4_14 "$@" 59 | 60 | cd ${CURR} 61 | 62 | exit 0 63 | --------------------------------------------------------------------------------