├── .gitignore ├── p4-samples ├── hardcoded │ ├── .gitignore │ ├── Makefile │ └── hardcoded.p4 ├── reflector │ ├── .gitignore │ ├── Makefile │ └── reflector.p4 ├── ipv4-routing │ ├── .gitignore │ ├── entries.txt │ ├── Makefile │ ├── README.md │ ├── entries.pb.txt │ └── basic.p4 ├── port-table │ ├── .gitignore │ ├── entries.txt │ ├── entries.pb.txt │ ├── Makefile │ └── table.p4 ├── vrf-routing │ ├── .gitignore │ ├── test.sh │ ├── entries.txt │ ├── Makefile │ ├── entries.pb.txt │ └── vrf.p4 ├── BUILD.bazel ├── veth_setup.sh ├── README.md └── Makefile ├── .gitmodules ├── .bazelrc ├── p4_symbolic ├── symbolic │ ├── expected │ │ ├── reflector.txt │ │ ├── hardcoded.txt │ │ ├── table.txt │ │ ├── reflector.smt2 │ │ ├── basic.txt │ │ ├── hardcoded.smt2 │ │ └── table.smt2 │ ├── packet.h │ ├── conditional.h │ ├── parser.h │ ├── table.h │ ├── util.h │ ├── conditional.cc │ ├── control.cc │ ├── packet.cc │ ├── BUILD.bazel │ ├── guarded_map.cc │ ├── operators.h │ ├── control.h │ ├── guarded_map.h │ ├── test.bzl │ ├── action.h │ ├── values.h │ ├── parser.cc │ └── symbolic.cc ├── util │ ├── status.h │ ├── BUILD.bazel │ ├── io.h │ ├── io.cc │ └── status.cc ├── ir │ ├── pdpi_driver.cc │ ├── pdpi_driver.h │ ├── ir.h │ ├── table_entries.h │ ├── test.bzl │ ├── test.cc │ ├── table_entries.cc │ ├── expected │ │ ├── reflector.txt │ │ └── hardcoded.txt │ └── BUILD.bazel ├── parser.h ├── bmv2 │ ├── bmv2.h │ ├── bmv2.cc │ ├── BUILD.bazel │ ├── test_sdiff.py │ └── test.cc ├── parser.cc └── BUILD.bazel ├── README.md ├── format.sh ├── Dockerfile ├── .github └── workflows │ ├── ci-test.yml │ └── ci-format.yml ├── z3-samples ├── paths │ ├── paths.p4rt │ ├── paths.opt2.smt2 │ ├── paths.p4 │ └── paths.opt1.smt2 └── port-table │ └── sample.smt2 ├── docs ├── contributing.md └── code-of-conduct.md ├── BUILD.bazel ├── p4c.bzl └── WORKSPACE.bazel /.gitignore: -------------------------------------------------------------------------------- 1 | bazel-* 2 | user.bazelrc 3 | -------------------------------------------------------------------------------- /p4-samples/hardcoded/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.p4i 3 | *.log 4 | *.pid 5 | *.pb.txt 6 | -------------------------------------------------------------------------------- /p4-samples/reflector/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.p4i 3 | *.log 4 | *.pid 5 | *.pb.txt 6 | -------------------------------------------------------------------------------- /p4-samples/ipv4-routing/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.p4i 3 | *.log 4 | *.pid 5 | basic.pb.txt 6 | -------------------------------------------------------------------------------- /p4-samples/port-table/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.p4i 3 | *.log 4 | *.pid 5 | table.pb.txt 6 | -------------------------------------------------------------------------------- /p4-samples/vrf-routing/.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | *.p4i 3 | *.log 4 | *.pid 5 | vrf.pb.txt 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/p4-pdpi"] 2 | path = third_party/p4-pdpi 3 | url = git@github.com:google/p4-pdpi.git 4 | -------------------------------------------------------------------------------- /p4-samples/port-table/entries.txt: -------------------------------------------------------------------------------- 1 | table_add ports_exact set_egress_spec 0 => 1 2 | table_add ports_exact set_egress_spec 1 => 0 3 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | # Optional per-user config goes into user.bazelrc, which is .gitignore-ed. 2 | try-import user.bazelrc 3 | build --cxxopt="-std=c++17" 4 | build --host_cxxopt="-std=c++17" 5 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/reflector.txt: -------------------------------------------------------------------------------- 1 | Finding packet for table tbl_reflector54 and row -1 2 | Dropped = 0 3 | standard_metadata.ingress_port = #b000000001 4 | standard_metadata.egress_spec = #b000000001 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # P4 Symbolic Test Input Generation 2 | 3 | Project to analyze P4 programs and automatically determine interesting inputs 4 | for that P4 program. 5 | 6 | This is a work in progress. 7 | 8 | This is not an officially supported Google product. 9 | -------------------------------------------------------------------------------- /p4-samples/vrf-routing/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | make build && cd ../../ && bazel build //p4_symbolic:main && ./bazel-bin/p4_symbolic/main --bmv2=p4-samples/vrf-routing/vrf.json --p4info=p4-samples/vrf-routing/vrf.pb.txt --entries=p4-samples/vrf-routing/entries.pb.txt --nohardcoded_parser 3 | -------------------------------------------------------------------------------- /p4-samples/ipv4-routing/entries.txt: -------------------------------------------------------------------------------- 1 | table_add ipv4_lpm ipv4_forward 10.10.0.0/16 => 00:00:00:00:00:00 0 2 | table_add ipv4_lpm ipv4_forward 10.10.0.0/32 => 00:00:00:00:00:00 1 3 | table_add ipv4_lpm ipv4_forward 10.0.0.0/8 => 00:00:00:00:00:10 1 4 | table_add ipv4_lpm ipv4_forward 20.20.0.0/16 => 22:00:00:00:00:22 1 5 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/hardcoded.txt: -------------------------------------------------------------------------------- 1 | Finding packet for table tbl_hardcoded55 and row -1 2 | Dropped = 0 3 | standard_metadata.ingress_port = #b000000000 4 | standard_metadata.egress_spec = #b000000001 5 | 6 | Finding packet for table tbl_hardcoded57 and row -1 7 | Dropped = 0 8 | standard_metadata.ingress_port = #b000000001 9 | standard_metadata.egress_spec = #b000000000 10 | 11 | -------------------------------------------------------------------------------- /p4-samples/vrf-routing/entries.txt: -------------------------------------------------------------------------------- 1 | table_add set_vrf_table set_vrf 0.33.1.0&&&11.33.11.11 => 2 2 2 | table_add set_vrf_table set_vrf 33.33.0.0&&&33.33.11.11 => 1 1 3 | table_add ipv4_lpm_table ipv4_forward 10.10.0.0/16 1 => 00:00:00:00:00:00 0 4 | table_add ipv4_lpm_table ipv4_forward 10.10.0.0/32 1 => 00:00:00:00:00:00 1 5 | table_add ipv4_lpm_table ipv4_forward 10.0.0.0/8 1 => 00:00:00:00:00:10 1 6 | table_add ipv4_lpm_table ipv4_forward 20.20.0.0/16 2 => 22:00:00:00:00:22 1 7 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/table.txt: -------------------------------------------------------------------------------- 1 | Finding packet for table MyIngress.ports_exact and row -1 2 | Cannot find solution! 3 | 4 | Finding packet for table MyIngress.ports_exact and row 0 5 | Dropped = 0 6 | standard_metadata.ingress_port = #b000000000 7 | standard_metadata.egress_spec = #b000000001 8 | 9 | Finding packet for table MyIngress.ports_exact and row 1 10 | Dropped = 0 11 | standard_metadata.ingress_port = #b000000001 12 | standard_metadata.egress_spec = #b000000000 13 | 14 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Formats source files according to Google's style guide. Requires clang-format. 3 | 4 | # Only files with these extensions will be formatted by clang-format. 5 | CLANG_FORMAT_EXTENSIONS="cc|h|proto" 6 | 7 | # Display clang-format version. 8 | clang-format --version 9 | 10 | # Run clang-format. 11 | find . -not -path "./third_party/**" \ 12 | | egrep "\.(${CLANG_FORMAT_EXTENSIONS})\$" \ 13 | | xargs clang-format --verbose -style=google -i 14 | 15 | # Run buildifier (Bazel file formatter). 16 | bazel run //:buildifier 17 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/reflector.smt2: -------------------------------------------------------------------------------- 1 | ; 2 | (set-info :status unknown) 3 | (declare-fun standard_metadata.ingress_port () (_ BitVec 9)) 4 | (declare-fun standard_metadata.egress_spec () (_ BitVec 9)) 5 | (assert 6 | (let (($x42 (= standard_metadata.ingress_port (_ bv1 9)))) 7 | (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x42))) 8 | (assert 9 | (let ((?x33 (ite true standard_metadata.ingress_port standard_metadata.egress_spec))) 10 | (or (or (= ?x33 (_ bv455 9)) (= ?x33 (_ bv0 9))) (= ?x33 (_ bv1 9))))) 11 | (assert 12 | (let ((?x33 (ite true standard_metadata.ingress_port standard_metadata.egress_spec))) 13 | (let (($x35 (= ?x33 (_ bv455 9)))) 14 | (and (and (not $x35) true) (= (- 1) (- 1)))))) 15 | (check-sat) 16 | 17 | -------------------------------------------------------------------------------- /p4-samples/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # Make sure programs under p4-samples are visible to other 16 | # bazel packages. 17 | exports_files(glob([ 18 | "**/*.p4", 19 | "**/*.txt", 20 | ])) 21 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM p4lang/p4c:latest 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | # Install dependencies. 6 | RUN apt-get update 7 | RUN apt-get install -y --no-install-recommends \ 8 | wget \ 9 | ca-certificates \ 10 | build-essential \ 11 | python3 \ 12 | libgmp-dev \ 13 | git 14 | RUN update-ca-certificates 15 | 16 | # Install g++-8 for -std=17 with structural binding. 17 | RUN apt-get install -y g++-8 18 | RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 60 --slave /usr/bin/g++ g++ /usr/bin/g++-8 19 | RUN update-alternatives --config gcc 20 | 21 | COPY . /p4-symbolic/ 22 | WORKDIR /p4-symbolic/ 23 | 24 | RUN wget "https://github.com/bazelbuild/bazelisk/releases/download/v1.4.0/bazelisk-linux-amd64" 25 | RUN chmod +x bazelisk-linux-amd64 26 | RUN ln -s $(pwd)/bazelisk-linux-amd64 /usr/local/bin/bazel 27 | -------------------------------------------------------------------------------- /.github/workflows/ci-test.yml: -------------------------------------------------------------------------------- 1 | name: "All Tests and Build" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ "*" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | submodules: recursive 16 | 17 | - name: Build Docker image 18 | run: docker build --tag p4-symbolic-image . 19 | 20 | - name: Run Container in Background 21 | run: docker run --name p4-symbolic-testing -d -i -t p4-symbolic-image 22 | 23 | - name: Build all 24 | env: 25 | TEST: bazel build //... 26 | run: docker exec p4-symbolic-testing sh -c "$TEST" 27 | 28 | - name: Test all 29 | env: 30 | TEST: bazel test //... --test_output=errors 31 | run: docker exec p4-symbolic-testing sh -c "$TEST" 32 | -------------------------------------------------------------------------------- /z3-samples/paths/paths.p4rt: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | table_add port_to_vrf set_vrf 0 => 10 16 | table_add port_to_vrf set_vrf 1 => 20 17 | 18 | table_add vrf_ip_to_port set_vrf 10.10.0.0/24 10 => 1 19 | table_add vrf_ip_to_port set_vrf 10.10.0.0/16 10 => 2 20 | table_add vrf_ip_to_port set_vrf 20.20.0.0/16 20 => 3 21 | -------------------------------------------------------------------------------- /p4-samples/port-table/entries.pb.txt: -------------------------------------------------------------------------------- 1 | updates { 2 | type: INSERT, 3 | entity { 4 | table_entry { 5 | table_id: 42954855 6 | match { 7 | field_id: 1, 8 | exact { 9 | value: "\00" 10 | } 11 | } 12 | action { 13 | action { 14 | action_id: 21735938 15 | params { 16 | param_id: 1 17 | value: "\01" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | } 24 | updates { 25 | type: INSERT, 26 | entity { 27 | table_entry { 28 | table_id: 42954855 29 | match { 30 | field_id: 1, 31 | exact { 32 | value: "\01" 33 | } 34 | } 35 | action { 36 | action { 37 | action_id: 21735938 38 | params { 39 | param_id: 1 40 | value: "\00" 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/ci-format.yml: -------------------------------------------------------------------------------- 1 | name: "Code Formatting" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ "*" ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | submodules: recursive 16 | 17 | - name: Run format.sh 18 | run: ./format.sh 19 | 20 | - name: Check formatting diff 21 | run: | 22 | CHANGED_FILES="$(git diff-index --name-only HEAD --)" 23 | if [[ -z "${CHANGED_FILES}" ]]; then 24 | echo "Success: no formatting changes needed." 25 | exit 0 26 | fi 27 | echo "Found formatting changes in the following files:" 28 | echo "${CHANGED_FILES}" 29 | echo "" 30 | echo "Please run format.sh to apply the changes." 31 | echo "" 32 | echo "===== Git diff ====" 33 | git diff 34 | exit 1 35 | -------------------------------------------------------------------------------- /p4_symbolic/util/status.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #ifndef P4_SYMBOLIC_UTIL_STATUS_H_ 16 | #define P4_SYMBOLIC_UTIL_STATUS_H_ 17 | 18 | #include "absl/status/status.h" 19 | #include "google/protobuf/stubs/status.h" 20 | 21 | namespace p4_symbolic { 22 | namespace util { 23 | 24 | // Transforms protobuf status into semantically equivalent absl status. 25 | absl::Status ProtobufToAbslStatus(const google::protobuf::util::Status& status); 26 | 27 | } // namespace util 28 | } // namespace p4_symbolic 29 | 30 | #endif // P4_SYMBOLIC_UTIL_STATUS_H_ 31 | -------------------------------------------------------------------------------- /p4_symbolic/util/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | load("@rules_cc//cc:defs.bzl", "cc_library") 16 | 17 | cc_library( 18 | name = "util", 19 | srcs = [ 20 | "io.cc", 21 | "status.cc", 22 | ], 23 | hdrs = [ 24 | "io.h", 25 | "status.h", 26 | ], 27 | visibility = ["//p4_symbolic:__subpackages__"], 28 | deps = [ 29 | "@com_google_absl//absl/status", 30 | "@com_google_absl//absl/strings", 31 | "@com_google_protobuf//:protobuf_headers", 32 | "@p4_pdpi//gutil:status", 33 | ], 34 | ) 35 | -------------------------------------------------------------------------------- /p4_symbolic/ir/pdpi_driver.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/ir/pdpi_driver.h" 16 | 17 | #include 18 | 19 | #include "gutil/proto.h" 20 | #include "p4_pdpi/ir.h" 21 | 22 | namespace p4_symbolic { 23 | namespace ir { 24 | 25 | gutil::StatusOr ParseP4InfoFile( 26 | const std::string &p4info_path) { 27 | p4::config::v1::P4Info p4info; 28 | RETURN_IF_ERROR(gutil::ReadProtoFromFile(p4info_path.c_str(), &p4info)); 29 | return pdpi::CreateIrP4Info(p4info); 30 | } 31 | 32 | } // namespace ir 33 | } // namespace p4_symbolic 34 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/basic.txt: -------------------------------------------------------------------------------- 1 | Finding packet for table MyIngress.ipv4_lpm and row -1 2 | Cannot find solution! 3 | 4 | Finding packet for table MyIngress.ipv4_lpm and row 0 5 | Dropped = 0 6 | standard_metadata.ingress_port = #b000000000 7 | standard_metadata.egress_spec = #b000000000 8 | ipv4.srcAddr = #x00000000 9 | ipv4.dstAddr = #x0a0a0001 10 | ethernet.dstAddr = #x000000000000 11 | 12 | Finding packet for table MyIngress.ipv4_lpm and row 1 13 | Dropped = 0 14 | standard_metadata.ingress_port = #b000000000 15 | standard_metadata.egress_spec = #b000000001 16 | ipv4.srcAddr = #x00000000 17 | ipv4.dstAddr = #x0a0a0000 18 | ethernet.dstAddr = #x000000000000 19 | 20 | Finding packet for table MyIngress.ipv4_lpm and row 2 21 | Dropped = 0 22 | standard_metadata.ingress_port = #b000000000 23 | standard_metadata.egress_spec = #b000000001 24 | ipv4.srcAddr = #x00000000 25 | ipv4.dstAddr = #x0a8a0000 26 | ethernet.dstAddr = #x00000000000a 27 | 28 | Finding packet for table MyIngress.ipv4_lpm and row 3 29 | Dropped = 0 30 | standard_metadata.ingress_port = #b000000000 31 | standard_metadata.egress_spec = #b000000001 32 | ipv4.srcAddr = #x00000000 33 | ipv4.dstAddr = #x14140000 34 | ethernet.dstAddr = #x160000000016 35 | 36 | -------------------------------------------------------------------------------- /p4_symbolic/parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This file defines the main API entry point for parsing input files 16 | // into our IR representation. 17 | 18 | #ifndef P4_SYMBOLIC_PARSER_H_ 19 | #define P4_SYMBOLIC_PARSER_H_ 20 | 21 | #include 22 | 23 | #include "gutil/status.h" 24 | #include "p4_symbolic/ir/ir.pb.h" 25 | #include "p4_symbolic/symbolic/symbolic.h" 26 | 27 | namespace p4_symbolic { 28 | 29 | gutil::StatusOr ParseToIr( 30 | const std::string &bmv2_path, const std::string &p4info_path, 31 | const std::string &table_entries_path); 32 | 33 | } // namespace p4_symbolic 34 | 35 | #endif // P4_SYMBOLIC_PARSER_H_ 36 | -------------------------------------------------------------------------------- /p4_symbolic/util/io.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #ifndef P4_SYMBOLIC_UTIL_IO_H_ 16 | #define P4_SYMBOLIC_UTIL_IO_H_ 17 | 18 | #include 19 | 20 | #include "absl/status/status.h" 21 | #include "absl/strings/string_view.h" 22 | #include "gutil/status.h" 23 | 24 | namespace p4_symbolic { 25 | namespace util { 26 | 27 | // Reads the entire content of the file and returns it (or an error status). 28 | gutil::StatusOr ReadFile(const std::string &path); 29 | 30 | // Writes the content of the string to the file. 31 | absl::Status WriteFile(const std::string &content, const std::string &path); 32 | 33 | } // namespace util 34 | } // namespace p4_symbolic 35 | 36 | #endif // P4_SYMBOLIC_UTIL_IO_H_ 37 | -------------------------------------------------------------------------------- /p4_symbolic/ir/pdpi_driver.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #ifndef P4_SYMBOLIC_IR_PDPI_DRIVER_H_ 16 | #define P4_SYMBOLIC_IR_PDPI_DRIVER_H_ 17 | 18 | #include 19 | 20 | #include "gutil/status.h" 21 | #include "p4_pdpi/ir.pb.h" 22 | 23 | namespace p4_symbolic { 24 | namespace ir { 25 | 26 | // Parses the p4info file given by "p4info_path" into a pdpi:IrP4Info 27 | // instance. 28 | // Returns the parsed IrP4Info instance, or an appropriate failure status 29 | // in case of a badly formatted input file, or if the file does not exist. 30 | gutil::StatusOr ParseP4InfoFile(const std::string &p4info_path); 31 | 32 | } // namespace ir 33 | } // namespace p4_symbolic 34 | 35 | #endif // P4_SYMBOLIC_IR_PDPI_DRIVER_H_ 36 | -------------------------------------------------------------------------------- /p4-samples/reflector/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | SHELL := /bin/bash 16 | 17 | run: build simple_switch packets 18 | 19 | # 20 | # Sending Test Packets 21 | # 22 | define PACKETS = 23 | # These wont show up \n\ 24 | p = Ether()/IP(dst="20.20.0.1")/UDP()\n\ 25 | sendp(p, iface="veth1")\n\ 26 | p = Ether()/IP(dst="20.20.0.2")/UDP()\n\ 27 | sendp(p, iface="veth1")\n\ 28 | # These will show up twice each \n\ 29 | p = Ether()/IP(dst="20.20.0.3")/UDP()\n\ 30 | sendp(p, iface="veth3")\n\ 31 | p = Ether()/IP(dst="20.20.0.4")/UDP()\n\ 32 | sendp(p, iface="veth3")\n\ 33 | q = Ether()/IP(dst="20.20.0.8")/UDP()\n\ 34 | sendp(q, iface="veth3")\n 35 | endef 36 | 37 | VETH_PAIRS_COUNT := 2 38 | 39 | # include main Makefile with rules 40 | include ../Makefile 41 | -------------------------------------------------------------------------------- /p4-samples/hardcoded/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | SHELL := /bin/bash 16 | 17 | run: build simple_switch packets 18 | 19 | # 20 | # Sending Test Packets 21 | # 22 | define PACKETS = 23 | p = Ether()/IP(dst="20.20.0.1")/UDP()\n\ 24 | sendp(p, iface="veth1")\n\ 25 | p = Ether()/IP(dst="20.20.0.2")/UDP()\n\ 26 | sendp(p, iface="veth1")\n\ 27 | p = Ether()/IP(dst="20.20.0.3")/UDP()\n\ 28 | sendp(p, iface="veth1")\n\ 29 | p = Ether()/IP(dst="20.20.0.4")/UDP()\n\ 30 | sendp(p, iface="veth1")\n\ 31 | # this will show up once, if the program was incorrect, it would show up twice \n\ 32 | q = Ether()/IP(dst="20.20.0.8")/UDP()\n\ 33 | sendp(q, iface="veth3")\n 34 | endef 35 | 36 | VETH_PAIRS_COUNT := 2 37 | 38 | # include main Makefile with rules 39 | include ../Makefile 40 | -------------------------------------------------------------------------------- /p4_symbolic/bmv2/bmv2.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #ifndef P4_SYMBOLIC_BMV2_BMV2_H_ 16 | #define P4_SYMBOLIC_BMV2_BMV2_H_ 17 | 18 | #include 19 | 20 | #include "gutil/status.h" 21 | #include "p4_symbolic/bmv2/bmv2.pb.h" 22 | 23 | namespace p4_symbolic { 24 | namespace bmv2 { 25 | 26 | // Reads and parses the bmv2 JSON content (typically the output of p4c) from 27 | // the given file. 28 | // Returns the resulting P4Program instance if successful, or an appropriate 29 | // failure status in case of a badly formatted input file, or if the file 30 | // does not exist. 31 | gutil::StatusOr ParseBmv2JsonFile(const std::string &json_path); 32 | 33 | } // namespace bmv2 34 | } // namespace p4_symbolic 35 | 36 | #endif // P4_SYMBOLIC_BMV2_BMV2_H_ 37 | -------------------------------------------------------------------------------- /p4-samples/vrf-routing/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | SHELL := /bin/bash 16 | 17 | run: build simple_switch control_plane packets 18 | 19 | # 20 | # Control Plane 21 | # insert entries into p4 tables 22 | # 23 | CONTROL_PLANE_COMMANDS=$$(cat entries.txt) 24 | 25 | # 26 | # Sending Test Packets 27 | # 28 | define PACKETS = 29 | p = Ether()/IP(dst="20.20.0.1")/UDP()\n\ 30 | sendp(p, iface="veth1")\n\ 31 | p = Ether()/IP(dst="20.20.0.2")/UDP()\n\ 32 | sendp(p, iface="veth1")\n\ 33 | # this should show up once \n\ 34 | p = Ether()/IP(dst="10.10.0.1")/UDP()\n\ 35 | sendp(p, iface="veth3")\n\ 36 | # this should not show up \n\ 37 | p = Ether()/IP(dst="10.10.0.2")/UDP()\n\ 38 | sendp(p, iface="veth1")\n 39 | endef 40 | 41 | VETH_PAIRS_COUNT := 2 42 | 43 | # include main Makefile with rules 44 | include ../Makefile 45 | -------------------------------------------------------------------------------- /p4-samples/ipv4-routing/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | SHELL := /bin/bash 16 | 17 | run: build simple_switch control_plane packets 18 | 19 | # 20 | # Control Plane 21 | # insert entries into p4 tables 22 | # 23 | CONTROL_PLANE_COMMANDS=$$(cat entries.txt) 24 | 25 | # 26 | # Sending Test Packets 27 | # 28 | define PACKETS = 29 | p = Ether()/IP(dst="20.20.0.1")/UDP()\n\ 30 | sendp(p, iface="veth1")\n\ 31 | p = Ether()/IP(dst="20.20.0.2")/UDP()\n\ 32 | sendp(p, iface="veth1")\n\ 33 | # this should show up once \n\ 34 | p = Ether()/IP(dst="10.10.0.1")/UDP()\n\ 35 | sendp(p, iface="veth3")\n\ 36 | # this should not show up \n\ 37 | p = Ether()/IP(dst="10.10.0.2")/UDP()\n\ 38 | sendp(p, iface="veth1")\n 39 | endef 40 | 41 | VETH_PAIRS_COUNT := 2 42 | 43 | # include main Makefile with rules 44 | include ../Makefile 45 | -------------------------------------------------------------------------------- /p4_symbolic/ir/ir.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This file defines the API for transforming a bmv2 protobuf (representing 16 | // the input bmv2 json file) together with a pdpi protobuf (representing the 17 | // p4info file) into our IR protobuf for consumption. 18 | 19 | #ifndef P4_SYMBOLIC_IR_IR_H_ 20 | #define P4_SYMBOLIC_IR_IR_H_ 21 | 22 | #include "gutil/status.h" 23 | #include "p4_symbolic/bmv2/bmv2.pb.h" 24 | #include "p4_symbolic/ir/ir.pb.h" 25 | #include "p4_symbolic/ir/table_entries.h" 26 | 27 | namespace p4_symbolic { 28 | namespace ir { 29 | 30 | // Transforms bmv2 protobuf and pdpi protobuf into our IR protobuf. 31 | gutil::StatusOr Bmv2AndP4infoToIr(const bmv2::P4Program& bmv2, 32 | const pdpi::IrP4Info& pdpi); 33 | 34 | } // namespace ir 35 | } // namespace p4_symbolic 36 | 37 | #endif // P4_SYMBOLIC_IR_IR_H_ 38 | -------------------------------------------------------------------------------- /p4-samples/port-table/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | SHELL := /bin/bash 16 | 17 | run: build simple_switch control_plane packets 18 | 19 | # 20 | # Control Plane 21 | # insert entries into p4 tables 22 | # 23 | CONTROL_PLANE_COMMANDS=$$(cat entries.txt) 24 | 25 | # 26 | # Sending Test Packets 27 | # 28 | define PACKETS = 29 | p = Ether()/IP(dst="20.20.0.1")/UDP()\n\ 30 | sendp(p, iface="veth1")\n\ 31 | p = Ether()/IP(dst="20.20.0.2")/UDP()\n\ 32 | sendp(p, iface="veth1")\n\ 33 | p = Ether()/IP(dst="20.20.0.3")/UDP()\n\ 34 | sendp(p, iface="veth1")\n\ 35 | p = Ether()/IP(dst="20.20.0.4")/UDP()\n\ 36 | sendp(p, iface="veth1")\n\ 37 | # this will show up once, if the program was incorrect, it would show up twice \n\ 38 | q = Ether()/IP(dst="20.20.0.8")/UDP()\n\ 39 | sendp(q, iface="veth3")\n 40 | endef 41 | 42 | VETH_PAIRS_COUNT := 2 43 | 44 | # include main Makefile with rules 45 | include ../Makefile 46 | -------------------------------------------------------------------------------- /p4-samples/ipv4-routing/README.md: -------------------------------------------------------------------------------- 1 | # Basic IPv4 forwarding 2 | 3 | This is a simple P4 program that implements basic IPv4 forwarding. 4 | 5 | Taken from https://github.com/p4lang/tutorials basic tutorial. 6 | 7 | It performs the following pipeline on every received packet: 8 | * parse the packet and extract the ethernet and ipv4 headers 9 | * lookup the mac address and out going port of the next hop from the control plane tables 10 | * update packet headers with the new address and port and decrement ttl 11 | * serialize (deparse) packet and send it via the new port 12 | 13 | ## Test Architecture 14 | 15 | The test follows a very simple network architecture. 16 | 17 | The switch is connected to two ports: 0 and 1, with 4 virtual ethernet interfaces: veth0, veth1, veth2, and veth3. 18 | 19 | veth0 and veth1 are piped togther, same with veth2 and veth3. Packets sent via veth0 appear as outputs on veth1 and vice-versa. 20 | 21 | Finally, the switch assigns veth0 to port 0, and veth1 to port 1. 22 | 23 | The control plane has two longest prefix rules: it routes all packets to 10.10.0.0/16 to port 0 (thus they appear at veth1) and all 24 | packets to 20.20.0.0/16 to port 1 (thus they appear at veth3). Other packets are dropped. 25 | 26 | The makefile sends 4 test packets: two to 20.20.0.*, and one to 10.10.0.*, using differnet ports. The makefile monitors packets 27 | through veth3 (the other end of port 1), and thus shows exactly three of these packets: 2 forwarded from the switch, and one 28 | input packet into the switch. 29 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/packet.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Contains helpers for creating, extracting, and managing concerete and 16 | // symbolic packet structs. 17 | 18 | #ifndef P4_SYMBOLIC_SYMBOLIC_PACKET_H_ 19 | #define P4_SYMBOLIC_SYMBOLIC_PACKET_H_ 20 | 21 | #include "gutil/status.h" 22 | #include "p4_symbolic/symbolic/symbolic.h" 23 | #include "z3++.h" 24 | 25 | namespace p4_symbolic { 26 | namespace symbolic { 27 | namespace packet { 28 | 29 | // Extract the packet fields from their p4 program counterparts. 30 | SymbolicPacket ExtractSymbolicPacket(SymbolicPerPacketState state); 31 | 32 | // Extract a concrete packet by evaluating every field's corresponding 33 | // expression in the model. 34 | ConcretePacket ExtractConcretePacket(SymbolicPacket packet, z3::model model); 35 | 36 | } // namespace packet 37 | } // namespace symbolic 38 | } // namespace p4_symbolic 39 | 40 | #endif // P4_SYMBOLIC_SYMBOLIC_PACKET_H_ 41 | -------------------------------------------------------------------------------- /p4-samples/veth_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | count=$1 4 | 5 | # The script must be run as root! 6 | if [[ $(/usr/bin/id -u) -ne 0 ]]; then 7 | echo "Not running as root" 8 | exit 9 | fi 10 | 11 | # Create $count many pairs of virtual interfaced, each interface in a pair 12 | # is hooked up to the other interface in that pair. 13 | for i in $(seq 0 $(($count-1))); do 14 | # come up with unique names for two virtual ethernet interfaces 15 | in_veth="veth$(($i*2))" 16 | out_veth="veth$(($i*2+1))" 17 | 18 | if ! ip link show $in_veth &> /dev/null; then 19 | # Create virtual interfaces and hook in_veth into out_veth 20 | # and vice versa. 21 | ip link add name $in_veth type veth peer name $out_veth 22 | 23 | # Allow frames to be larger than default. 24 | # This allows testing of jumbo frames with bmv2. 25 | # https://github.com/p4lang/behavioral-model/blob/master/tools/veth_setup.sh 26 | ip link set $in_veth mtu 9500 27 | ip link set $out_veth mtu 9500 28 | 29 | # Linux kernel automatically sends IPv6 headers if IPv6 is enabled. 30 | # This will make our P4 programs complicated or malfunction, 31 | # since they mostly use IPv4 headers. 32 | # Disable IPv6 to avoid this. 33 | sysctl net.ipv6.conf.${in_veth}.disable_ipv6=1 34 | sysctl net.ipv6.conf.${out_veth}.disable_ipv6=1 35 | 36 | # Start the virtual interfaces. 37 | ip link set dev $in_veth up 38 | ip link set dev $out_veth up 39 | fi 40 | done 41 | -------------------------------------------------------------------------------- /p4_symbolic/bmv2/bmv2.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/bmv2/bmv2.h" 16 | 17 | #include 18 | 19 | #include "google/protobuf/util/json_util.h" 20 | #include "p4_symbolic/util/io.h" 21 | #include "p4_symbolic/util/status.h" 22 | 23 | namespace p4_symbolic { 24 | namespace bmv2 { 25 | 26 | gutil::StatusOr ParseBmv2JsonFile(const std::string &json_path) { 27 | ASSIGN_OR_RETURN(std::string file_content, util::ReadFile(json_path)); 28 | 29 | P4Program output; 30 | google::protobuf::util::JsonParseOptions options; 31 | options.ignore_unknown_fields = true; 32 | google::protobuf::util::Status parse_status = 33 | google::protobuf::util::JsonStringToMessage(file_content, &output, 34 | options); 35 | RETURN_IF_ERROR(util::ProtobufToAbslStatus(parse_status)); 36 | 37 | return output; 38 | } 39 | 40 | } // namespace bmv2 41 | } // namespace p4_symbolic 42 | -------------------------------------------------------------------------------- /p4_symbolic/ir/table_entries.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This file parses table entries attached to a p4 program, and fills them 16 | // into the IR of that program. 17 | 18 | #ifndef P4_SYMBOLIC_IR_TABLE_ENTRIES_H_ 19 | #define P4_SYMBOLIC_IR_TABLE_ENTRIES_H_ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "gutil/status.h" 27 | #include "p4_pdpi/ir.pb.h" 28 | #include "p4_symbolic/ir/ir.pb.h" 29 | 30 | namespace p4_symbolic { 31 | namespace ir { 32 | 33 | using TableEntries = 34 | std::unordered_map>; 35 | 36 | // Parses entries read from entries_path, and fills them in given ir in place. 37 | gutil::StatusOr ParseAndFillEntries( 38 | const pdpi::IrP4Info &p4info, const std::string &entries_path); 39 | 40 | } // namespace ir 41 | } // namespace p4_symbolic 42 | 43 | #endif // P4_SYMBOLIC_IR_TABLE_ENTRIES_H_ 44 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/conditional.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Contains functions used to symbolically evaluate P4 conditionals and their 16 | // branches. 17 | 18 | #ifndef P4_SYMBOLIC_SYMBOLIC_CONDITIONAL_H_ 19 | #define P4_SYMBOLIC_SYMBOLIC_CONDITIONAL_H_ 20 | 21 | #include "gutil/status.h" 22 | #include "p4_symbolic/ir/ir.pb.h" 23 | #include "p4_symbolic/symbolic/control.h" 24 | #include "p4_symbolic/symbolic/symbolic.h" 25 | #include "p4_symbolic/symbolic/values.h" 26 | #include "z3++.h" 27 | 28 | namespace p4_symbolic { 29 | namespace symbolic { 30 | namespace conditional { 31 | 32 | gutil::StatusOr EvaluateConditional( 33 | const Dataplane data_plane, const ir::Conditional &table, 34 | SymbolicPerPacketState *state, values::P4RuntimeTranslator *translator, 35 | const z3::expr &guard); 36 | 37 | } // namespace conditional 38 | } // namespace symbolic 39 | } // namespace p4_symbolic 40 | 41 | #endif // P4_SYMBOLIC_SYMBOLIC_CONDITIONAL_H_ 42 | -------------------------------------------------------------------------------- /p4_symbolic/parser.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/parser.h" 16 | 17 | #include "p4_symbolic/bmv2/bmv2.h" 18 | #include "p4_symbolic/ir/ir.h" 19 | #include "p4_symbolic/ir/pdpi_driver.h" 20 | 21 | namespace p4_symbolic { 22 | 23 | gutil::StatusOr ParseToIr( 24 | const std::string &bmv2_path, const std::string &p4info_path, 25 | const std::string &table_entries_path) { 26 | // Parse p4info file into pdpi format. 27 | ASSIGN_OR_RETURN(pdpi::IrP4Info p4info, 28 | p4_symbolic::ir::ParseP4InfoFile(p4info_path)); 29 | 30 | // Parse bmv2 json file into our initial bmv2 protobuf. 31 | ASSIGN_OR_RETURN(bmv2::P4Program bmv2, bmv2::ParseBmv2JsonFile(bmv2_path)); 32 | 33 | // Parse table entries. 34 | p4_symbolic::ir::TableEntries table_entries; 35 | if (!table_entries_path.empty()) { 36 | ASSIGN_OR_RETURN(table_entries, 37 | ir::ParseAndFillEntries(p4info, table_entries_path)); 38 | } 39 | 40 | // Transform all above into our IR. 41 | ASSIGN_OR_RETURN(ir::P4Program program, ir::Bmv2AndP4infoToIr(bmv2, p4info)); 42 | 43 | return p4_symbolic::symbolic::Dataplane{program, table_entries}; 44 | } 45 | 46 | } // namespace p4_symbolic 47 | -------------------------------------------------------------------------------- /docs/contributing.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Contributor License Agreement 6 | 7 | Contributions to this project must be accompanied by a Contributor License 8 | Agreement. You (or your employer) retain the copyright to your contribution; 9 | this simply gives us permission to use and redistribute your contributions as 10 | part of the project. Head over to to see 11 | your current agreements on file or to sign a new one. 12 | 13 | You generally only need to submit a CLA once, so if you've already submitted one 14 | (even if it was for a different project), you probably don't need to do it 15 | again. 16 | 17 | ## Code reviews 18 | 19 | All submissions, including submissions by project members, require review. We 20 | use GitHub pull requests for this purpose. Consult 21 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 22 | information on using pull requests. 23 | 24 | ## Community Guidelines 25 | 26 | This project follows [Google's Open Source Community 27 | Guidelines](https://opensource.google/conduct/). 28 | 29 | ## Source Code Headers 30 | 31 | Every source code file must have the following header: 32 | 33 | Copyright 2020 Google LLC 34 | 35 | Licensed under the Apache License, Version 2.0 (the "License"); 36 | you may not use this file except in compliance with the License. 37 | You may obtain a copy of the License at 38 | 39 | https://www.apache.org/licenses/LICENSE-2.0 40 | 41 | Unless required by applicable law or agreed to in writing, software 42 | distributed under the License is distributed on an "AS IS" BASIS, 43 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 44 | See the License for the specific language governing permissions and 45 | limitations under the License. 46 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Hardcodes the behavior of an interesting p4 parser that is part 16 | // of the p4 program we are interested in. 17 | // The hardcoded behavior sets the validity of certain header fields 18 | // based on the fields in the packet, and sets the default values 19 | // for local_metadata fields. 20 | 21 | #ifndef P4_SYMBOLIC_SYMBOLIC_PARSER_H_ 22 | #define P4_SYMBOLIC_SYMBOLIC_PARSER_H_ 23 | 24 | #include 25 | 26 | #include "gutil/status.h" 27 | #include "p4_symbolic/symbolic/symbolic.h" 28 | 29 | #define ETHERTYPE_IPV4 0x0800 30 | #define ETHERTYPE_IPV6 0x86dd 31 | #define ETHERTYPE_ARP 0x0806 32 | 33 | #define IP_PROTOCOL_TCP 0x06 34 | #define IP_PROTOCOL_UDP 0x11 35 | #define IP_PROTOCOL_ICMP 0x01 36 | #define IP_PROTOCOL_ICMPV6 0x3a 37 | 38 | namespace p4_symbolic { 39 | namespace symbolic { 40 | namespace parser { 41 | 42 | // Creates assertions/constraints that encode some of the interesting 43 | // behavior of parsers in specific programs we want to analyze. 44 | gutil::StatusOr> EvaluateHardcodedParser( 45 | const SymbolicPerPacketState &state); 46 | 47 | } // namespace parser 48 | } // namespace symbolic 49 | } // namespace p4_symbolic 50 | 51 | #endif // P4_SYMBOLIC_SYMBOLIC_PARSER_H_ 52 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Contains functions used to symbolically evaluate P4 tables and their entries. 16 | // A table is turned into a sequence of if-conditions (one per entry), 17 | // each condition corresponds to having that entry matched on, and the 18 | // corresponding then body invokes the appropriate symbolic action expression 19 | // with the parameters specified in the entry. 20 | 21 | #ifndef P4_SYMBOLIC_SYMBOLIC_TABLE_H_ 22 | #define P4_SYMBOLIC_SYMBOLIC_TABLE_H_ 23 | 24 | #include 25 | #include 26 | 27 | #include "google/protobuf/map.h" 28 | #include "gutil/status.h" 29 | #include "p4_pdpi/ir.pb.h" 30 | #include "p4_symbolic/ir/ir.pb.h" 31 | #include "p4_symbolic/symbolic/control.h" 32 | #include "p4_symbolic/symbolic/symbolic.h" 33 | #include "p4_symbolic/symbolic/values.h" 34 | 35 | namespace p4_symbolic { 36 | namespace symbolic { 37 | namespace table { 38 | 39 | gutil::StatusOr EvaluateTable( 40 | const Dataplane data_plane, const ir::Table &table, 41 | const std::vector &entries, 42 | SymbolicPerPacketState *state, values::P4RuntimeTranslator *translator, 43 | const z3::expr &guard); 44 | 45 | } // namespace table 46 | } // namespace symbolic 47 | } // namespace p4_symbolic 48 | 49 | #endif // P4_SYMBOLIC_SYMBOLIC_TABLE_H_ 50 | -------------------------------------------------------------------------------- /p4_symbolic/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # This file contains build rules for the main binary of p4_symbolic. 16 | 17 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library") 18 | 19 | cc_binary( 20 | name = "main", 21 | srcs = [ 22 | "main.cc", 23 | ], 24 | linkopts = ["-lz3"], 25 | visibility = ["//p4_symbolic/symbolic:__pkg__"], 26 | deps = [ 27 | "//p4_symbolic:parser", 28 | "//p4_symbolic/ir:ir_cc_proto", 29 | "//p4_symbolic/symbolic", 30 | "//p4_symbolic/util", 31 | "@com_google_absl//absl/flags:flag", 32 | "@com_google_absl//absl/flags:parse", 33 | "@com_google_absl//absl/flags:usage", 34 | "@com_google_absl//absl/status", 35 | "@com_google_absl//absl/strings", 36 | "@p4_pdpi//gutil:status", 37 | ], 38 | ) 39 | 40 | cc_library( 41 | name = "parser", 42 | srcs = [ 43 | "parser.cc", 44 | ], 45 | hdrs = [ 46 | "parser.h", 47 | ], 48 | visibility = ["//p4_symbolic/ir:__pkg__"], 49 | deps = [ 50 | "//p4_symbolic/bmv2", 51 | "//p4_symbolic/bmv2:bmv2_cc_proto", 52 | "//p4_symbolic/ir", 53 | "//p4_symbolic/ir:ir_cc_proto", 54 | "//p4_symbolic/ir:pdpi_driver", 55 | "//p4_symbolic/ir:table_entries", 56 | "//p4_symbolic/symbolic", 57 | "@p4_pdpi//gutil:status", 58 | ], 59 | ) 60 | -------------------------------------------------------------------------------- /p4-samples/reflector/reflector.p4: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | /* -*- P4_16 -*- */ 16 | #include 17 | #include 18 | 19 | struct metadata {} 20 | struct headers {} 21 | 22 | parser UselessParser(packet_in packet, 23 | out headers hdr, 24 | inout metadata meta, 25 | inout standard_metadata_t standard_metadata) { 26 | state start { 27 | transition accept; 28 | } 29 | } 30 | 31 | control UselessChecksum(inout headers hdr, inout metadata meta) { 32 | apply { } 33 | } 34 | 35 | control UselessEgress(inout headers hdr, 36 | inout metadata meta, 37 | inout standard_metadata_t standard_metadata) { 38 | apply { } 39 | } 40 | 41 | control UselessComputeChecksum(inout headers hdr, inout metadata meta) { 42 | apply { } 43 | } 44 | 45 | control UselessDeparser(packet_out packet, in headers hdr) { 46 | apply { } 47 | } 48 | 49 | // Reflecting Packets Code 50 | control MyIngress(inout headers hdr, 51 | inout metadata meta, 52 | inout standard_metadata_t standard_metadata) { 53 | apply { 54 | standard_metadata.egress_spec = standard_metadata.ingress_port; 55 | } 56 | } 57 | 58 | // Switch 59 | V1Switch( 60 | UselessParser(), 61 | UselessChecksum(), 62 | MyIngress(), 63 | UselessEgress(), 64 | UselessComputeChecksum(), 65 | UselessDeparser() 66 | ) main; 67 | -------------------------------------------------------------------------------- /p4-samples/hardcoded/hardcoded.p4: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | /* -*- P4_16 -*- */ 16 | #include 17 | #include 18 | 19 | struct metadata {} 20 | struct headers {} 21 | 22 | parser UselessParser(packet_in packet, 23 | out headers hdr, 24 | inout metadata meta, 25 | inout standard_metadata_t standard_metadata) { 26 | state start { 27 | transition accept; 28 | } 29 | } 30 | 31 | control UselessChecksum(inout headers hdr, inout metadata meta) { 32 | apply { } 33 | } 34 | 35 | control UselessEgress(inout headers hdr, 36 | inout metadata meta, 37 | inout standard_metadata_t standard_metadata) { 38 | apply { } 39 | } 40 | 41 | control UselessComputeChecksum(inout headers hdr, inout metadata meta) { 42 | apply { } 43 | } 44 | 45 | control UselessDeparser(packet_out packet, in headers hdr) { 46 | apply { } 47 | } 48 | 49 | // Forwarding Code 50 | control MyIngress(inout headers hdr, 51 | inout metadata meta, 52 | inout standard_metadata_t standard_metadata) { 53 | apply { 54 | if (standard_metadata.ingress_port == 0) { 55 | standard_metadata.egress_spec = 1; 56 | } else { 57 | standard_metadata.egress_spec = 0; 58 | } 59 | } 60 | } 61 | 62 | // Switch 63 | V1Switch( 64 | UselessParser(), 65 | UselessChecksum(), 66 | MyIngress(), 67 | UselessEgress(), 68 | UselessComputeChecksum(), 69 | UselessDeparser() 70 | ) main; 71 | -------------------------------------------------------------------------------- /p4-samples/README.md: -------------------------------------------------------------------------------- 1 | # P4 Sample Programs 2 | 3 | This directory contains a collection of sample P4 programs with a simple testing environment. 4 | 5 | 1. Reflector: a simple switch that reflects back all received packets onto the port it received them form. 6 | 2. hardcoded: a simple switch with two ports, that forwards every packet received on one port to the other, implemented via a hardcoded if statement. 7 | 3. port-table: same as hardcoded, but implemented via a contorl plane table. 8 | 4. ipv4-routing: a switch that routes packets based on their destination ipv4 using an LPM table. 9 | 10 | 11 | ## Running 12 | 13 | First `cd` into the directory of the sample you want to run you can then compile, simulate, and run the tests in one shot using 14 | ``` 15 | make 16 | ``` 17 | 18 | Internally, this first compiles the program 19 | ``` 20 | make build 21 | ``` 22 | 23 | Then, it runs bmv2's simple\_switch simulator on that program with virtual ethernet interfaces that the script creates 24 | ``` 25 | make simple_switch 26 | ``` 27 | 28 | After that, it installs the control plane table entries into the switch's control tables using simple\_switch\_cli. Note that this is only 29 | performed for programs that have control plane tables 30 | ``` 31 | make simple_switch_cli 32 | ``` 33 | 34 | Finally, the command monitors the output messages sent on port 1, and logs them via the screen, using `tcpdump`, it 35 | also uses `scapy` to send test packets. 36 | 37 | When you are done experimenting, use `make stop` to remove all temporary files, shutdown simple_switch and tcpdump, and clean. 38 | 39 | ## Dependencies 40 | 41 | You need to have `p4c`, `bmv2`, and `scapy` installed. 42 | 43 | # Credits 44 | 45 | These resources were used either for inspiration or as learning materials for writing the p4 routing program and the testing architecture and packets. 46 | 47 | - The p4 tutorials https://github.com/p4lang/tutorials and associated youtube videos https://www.youtube.com/watch?v=qxT7DKOIk7Q 48 | - The p4app build and simulate system https://github.com/p4lang/p4app 49 | - This very nice blogpost by Bruno Rijsman https://hikingandcoding.wordpress.com/2019/09/17/getting-started-with-p4/ 50 | -------------------------------------------------------------------------------- /p4_symbolic/util/io.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/util/io.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "absl/strings/str_format.h" 23 | #include "gutil/status.h" 24 | 25 | namespace p4_symbolic { 26 | namespace util { 27 | 28 | namespace { 29 | 30 | absl::Status ErrorNoToAbsl(const char *operation, const std::string &path) { 31 | switch (errno) { 32 | case EACCES: 33 | case ENOENT: 34 | return absl::NotFoundError( 35 | absl::StrFormat("%s: %s", strerror(errno), path)); 36 | default: 37 | return absl::UnknownError(absl::StrFormat("Cannot %s file %s, errno = %d", 38 | operation, path, errno)); 39 | } 40 | } 41 | 42 | } // namespace 43 | 44 | gutil::StatusOr ReadFile(const std::string &path) { 45 | std::ifstream f; 46 | f.open(path.c_str()); 47 | if (f.fail()) { 48 | return ErrorNoToAbsl("open", path); 49 | } 50 | f >> std::noskipws; // Read whitespaces. 51 | std::string result(std::istreambuf_iterator(f), 52 | (std::istreambuf_iterator())); 53 | if (f.bad()) { 54 | return ErrorNoToAbsl("read", path); 55 | } 56 | f.close(); 57 | return result; 58 | } 59 | 60 | absl::Status WriteFile(const std::string &content, const std::string &path) { 61 | std::ofstream f; 62 | f.open(path.c_str()); 63 | if (f.fail()) { 64 | return ErrorNoToAbsl("open", path); 65 | } 66 | f << content; 67 | f.close(); 68 | if (f.bad()) { 69 | return ErrorNoToAbsl("write", path); 70 | } 71 | return absl::OkStatus(); 72 | } 73 | 74 | } // namespace util 75 | } // namespace p4_symbolic 76 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/hardcoded.smt2: -------------------------------------------------------------------------------- 1 | ; 2 | (set-info :status unknown) 3 | (declare-fun standard_metadata.ingress_port () (_ BitVec 9)) 4 | (declare-fun standard_metadata.egress_spec () (_ BitVec 9)) 5 | (assert 6 | (let (($x54 (= standard_metadata.ingress_port (_ bv1 9)))) 7 | (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x54))) 8 | (assert 9 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 10 | (let (($x35 (= standard_metadata.ingress_port ?x34))) 11 | (let (($x37 (and true $x35))) 12 | (let (($x38 (and true (not $x35)))) 13 | (let ((?x43 (ite $x38 ?x34 (ite $x37 (concat (_ bv0 8) (_ bv1 1)) standard_metadata.egress_spec)))) 14 | (or (or (= ?x43 (_ bv455 9)) (= ?x43 (_ bv0 9))) (= ?x43 (_ bv1 9))))))))) 15 | (assert 16 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 17 | (let (($x35 (= standard_metadata.ingress_port ?x34))) 18 | (let (($x37 (and true $x35))) 19 | (let (($x44 (ite $x35 $x37 false))) 20 | (let (($x38 (and true (not $x35)))) 21 | (let ((?x43 (ite $x38 ?x34 (ite $x37 (concat (_ bv0 8) (_ bv1 1)) standard_metadata.egress_spec)))) 22 | (let (($x47 (= ?x43 (_ bv455 9)))) 23 | (and (and (not $x47) $x44) (= (- 1) (- 1))))))))))) 24 | (check-sat) 25 | 26 | ; 27 | (set-info :status unknown) 28 | (declare-fun standard_metadata.ingress_port () (_ BitVec 9)) 29 | (declare-fun standard_metadata.egress_spec () (_ BitVec 9)) 30 | (assert 31 | (let (($x54 (= standard_metadata.ingress_port (_ bv1 9)))) 32 | (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x54))) 33 | (assert 34 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 35 | (let (($x35 (= standard_metadata.ingress_port ?x34))) 36 | (let (($x37 (and true $x35))) 37 | (let (($x38 (and true (not $x35)))) 38 | (let ((?x43 (ite $x38 ?x34 (ite $x37 (concat (_ bv0 8) (_ bv1 1)) standard_metadata.egress_spec)))) 39 | (or (or (= ?x43 (_ bv455 9)) (= ?x43 (_ bv0 9))) (= ?x43 (_ bv1 9))))))))) 40 | (assert 41 | (let (($x38 (and true (not (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1))))))) 42 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 43 | (let (($x35 (= standard_metadata.ingress_port ?x34))) 44 | (let (($x45 (ite $x35 false $x38))) 45 | (let (($x37 (and true $x35))) 46 | (let ((?x43 (ite $x38 ?x34 (ite $x37 (concat (_ bv0 8) (_ bv1 1)) standard_metadata.egress_spec)))) 47 | (let (($x47 (= ?x43 (_ bv455 9)))) 48 | (and (and (not $x47) $x45) (= (- 1) (- 1))))))))))) 49 | (check-sat) 50 | 51 | -------------------------------------------------------------------------------- /p4-samples/ipv4-routing/entries.pb.txt: -------------------------------------------------------------------------------- 1 | updates { 2 | type: INSERT, 3 | entity { 4 | table_entry { 5 | table_id: 37375156 6 | match { 7 | field_id: 1, 8 | lpm { 9 | value: "\n\n\000\000", 10 | prefix_len: 16 11 | } 12 | } 13 | action { 14 | action { 15 | action_id: 28792405 16 | params { 17 | param_id: 1 18 | value: "\000\000\000\000\000\000" 19 | } 20 | params { 21 | param_id: 2 22 | value: "\00" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | } 29 | updates { 30 | type: INSERT, 31 | entity { 32 | table_entry { 33 | table_id: 37375156 34 | match { 35 | field_id: 1, 36 | lpm { 37 | value: "\n\n\000\000", 38 | prefix_len: 32 39 | } 40 | } 41 | action { 42 | action { 43 | action_id: 28792405 44 | params { 45 | param_id: 1 46 | value: "\000\000\000\000\000\000" 47 | } 48 | params { 49 | param_id: 2 50 | value: "\01" 51 | } 52 | } 53 | } 54 | } 55 | } 56 | } 57 | updates { 58 | type: INSERT, 59 | entity { 60 | table_entry { 61 | table_id: 37375156 62 | match { 63 | field_id: 1, 64 | lpm { 65 | value: "\n\000\000\000", 66 | prefix_len: 8 67 | } 68 | } 69 | action { 70 | action { 71 | action_id: 28792405 72 | params { 73 | param_id: 1 74 | value: "\000\000\000\000\000\n" 75 | } 76 | params { 77 | param_id: 2 78 | value: "\01" 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | updates { 86 | type: INSERT, 87 | entity { 88 | table_entry { 89 | table_id: 37375156 90 | match { 91 | field_id: 1, 92 | lpm { 93 | value: "\024\024\000\000", 94 | prefix_len: 16 95 | } 96 | } 97 | action { 98 | action { 99 | action_id: 28792405 100 | params { 101 | param_id: 1 102 | value: "\026\000\000\000\000\026" 103 | } 104 | params { 105 | param_id: 2 106 | value: "\01" 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /p4-samples/port-table/table.p4: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | /* -*- P4_16 -*- */ 16 | #include 17 | #include 18 | 19 | struct metadata {} 20 | struct headers {} 21 | 22 | parser UselessParser(packet_in packet, 23 | out headers hdr, 24 | inout metadata meta, 25 | inout standard_metadata_t standard_metadata) { 26 | state start { 27 | transition accept; 28 | } 29 | } 30 | 31 | control UselessChecksum(inout headers hdr, inout metadata meta) { 32 | apply { } 33 | } 34 | 35 | control UselessEgress(inout headers hdr, 36 | inout metadata meta, 37 | inout standard_metadata_t standard_metadata) { 38 | apply { } 39 | } 40 | 41 | control UselessComputeChecksum(inout headers hdr, inout metadata meta) { 42 | apply { } 43 | } 44 | 45 | control UselessDeparser(packet_out packet, in headers hdr) { 46 | apply { } 47 | } 48 | 49 | // Forwarding Code 50 | control MyIngress(inout headers hdr, 51 | inout metadata meta, 52 | inout standard_metadata_t standard_metadata) { 53 | 54 | // set the port when the table is hit 55 | action set_egress_spec(bit<9> port) { 56 | standard_metadata.egress_spec = port; 57 | } 58 | 59 | // define the port routing table 60 | table ports_exact { 61 | key = { 62 | // exact equality matching 63 | standard_metadata.ingress_port: exact; 64 | } 65 | actions = { 66 | @proto_id(1) set_egress_spec; 67 | @proto_id(2) NoAction; 68 | } 69 | size = 1024; 70 | default_action = NoAction; 71 | } 72 | 73 | apply { 74 | ports_exact.apply(); 75 | } 76 | } 77 | 78 | // Switch 79 | V1Switch( 80 | UselessParser(), 81 | UselessChecksum(), 82 | MyIngress(), 83 | UselessEgress(), 84 | UselessComputeChecksum(), 85 | UselessDeparser() 86 | ) main; 87 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/util.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Helpful utilities for managing symbolic and concrete headers and values. 16 | 17 | #ifndef P4_SYMBOLIC_SYMBOLIC_UTIL_H_ 18 | #define P4_SYMBOLIC_SYMBOLIC_UTIL_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "google/protobuf/map.h" 24 | #include "gutil/status.h" 25 | #include "p4_pdpi/ir.pb.h" 26 | #include "p4_symbolic/ir/ir.pb.h" 27 | #include "p4_symbolic/symbolic/symbolic.h" 28 | #include "p4_symbolic/symbolic/values.h" 29 | #include "z3++.h" 30 | 31 | namespace p4_symbolic { 32 | namespace symbolic { 33 | namespace util { 34 | 35 | // Free (unconstrained) symbolic headers consisting of free symbolic variables 36 | // for every field in every header instance defined in the P4 program. 37 | gutil::StatusOr> FreeSymbolicHeaders( 38 | const google::protobuf::Map &headers); 39 | 40 | // Returns an symbolic table match containing default values. 41 | // The table match expression is false, the index is -1, and the value is 42 | // undefined. 43 | SymbolicTableMatch DefaultTableMatch(); 44 | 45 | // Extract a concrete context by evaluating every component's corresponding 46 | // expression in the model. 47 | gutil::StatusOr ExtractFromModel( 48 | SymbolicContext context, z3::model model, 49 | const values::P4RuntimeTranslator &translator); 50 | 51 | // Merges two symbolic traces into a single trace. A field in the new trace 52 | // has the value of the changed trace if the condition is true, and the value 53 | // of the original one otherwise. 54 | // Assertion: both traces must contain matches for the same set of table names. 55 | gutil::StatusOr MergeTracesOnCondition( 56 | const z3::expr &condition, const SymbolicTrace &true_trace, 57 | const SymbolicTrace &false_trace); 58 | 59 | } // namespace util 60 | } // namespace symbolic 61 | } // namespace p4_symbolic 62 | 63 | #endif // P4_SYMBOLIC_SYMBOLIC_UTIL_H_ 64 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/conditional.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Contains functions used to symbolically evaluate P4 conditionals and their 16 | // branches. 17 | 18 | #include "p4_symbolic/symbolic/conditional.h" 19 | 20 | #include "p4_symbolic/symbolic/action.h" 21 | #include "p4_symbolic/symbolic/operators.h" 22 | #include "p4_symbolic/symbolic/util.h" 23 | #include "z3++.h" 24 | 25 | namespace p4_symbolic { 26 | namespace symbolic { 27 | namespace conditional { 28 | 29 | gutil::StatusOr EvaluateConditional( 30 | const Dataplane data_plane, const ir::Conditional &conditional, 31 | SymbolicPerPacketState *state, values::P4RuntimeTranslator *translator, 32 | const z3::expr &guard) { 33 | // Evaluate the condition. 34 | action::ActionContext fake_context = {conditional.name(), {}}; 35 | ASSIGN_OR_RETURN( 36 | z3::expr condition, 37 | action::EvaluateRValue(conditional.condition(), *state, fake_context)); 38 | ASSIGN_OR_RETURN(z3::expr negated_condition, operators::Not(condition)); 39 | 40 | // Build new guards for each branch. 41 | ASSIGN_OR_RETURN(z3::expr if_guard, operators::And(guard, condition)); 42 | ASSIGN_OR_RETURN(z3::expr else_guard, 43 | operators::And(guard, negated_condition)); 44 | 45 | // Evaluate both branches. 46 | ASSIGN_OR_RETURN(SymbolicTrace if_trace, 47 | control::EvaluateControl(data_plane, conditional.if_branch(), 48 | state, translator, if_guard)); 49 | ASSIGN_OR_RETURN( 50 | SymbolicTrace else_trace, 51 | control::EvaluateControl(data_plane, conditional.else_branch(), state, 52 | translator, else_guard)); 53 | 54 | // Now we have two traces that need merging. 55 | // We should merge in a way such that the value of a field in the trace is 56 | // the one from the if branch if the condition is true, and the else branch 57 | // otherwise. 58 | return util::MergeTracesOnCondition(condition, if_trace, else_trace); 59 | } 60 | 61 | } // namespace conditional 62 | } // namespace symbolic 63 | } // namespace p4_symbolic 64 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | load("@com_github_bazelbuild_buildtools//buildifier:def.bzl", "buildifier") 16 | load("@rules_foreign_cc//tools/build_defs:configure.bzl", "configure_make") 17 | 18 | # Bazel buildifier: auto formats bazel files. 19 | buildifier( 20 | name = "buildifier", 21 | exclude_patterns = ["./third_party/**"], 22 | lint_mode = "fix", 23 | ) 24 | 25 | # Compiling z3 using foreign_cc toolchain (configure and make). 26 | configure_make( 27 | name = "z3", 28 | binaries = ["z3"], 29 | # Bazel redacts certain cc macros such as __DATA__ and __TIMESTAMP__ 30 | # since they will cause the compiled code to have timestamps or other 31 | # similar information in it, causing the compilation to be 32 | # non-deterministic. 33 | # Without such redaction, running the compilation twice with no changes in 34 | # the code will produce seemingly different binaries. 35 | # Bazel fixes this by setting the value of these macros to "redacted", 36 | # which is a valid c++ expression through CFLAGS and CXXFLAGS Toolchain 37 | # options. 38 | # See https://github.com/bazelbuild/bazel/issues/5750 39 | # However, the quotes get dropped because of some bash serialization in 40 | # rules_foreign_cc when they are passed (as bash environment variables) to 41 | # "./configure", causing __DATA__ to resolve to the unquoted token redacted, 42 | # which is usually not a valid c++ expression. 43 | # This fixes that, it makes redacted (the token not the string) an alias to 44 | # 0, which is a valid c++ expression. 45 | # This is a minor improvement on top of: 46 | # https://github.com/bazelbuild/rules_foreign_cc/issues/239 47 | configure_env_vars = { 48 | "CFLAGS": "-Dredacted=0", 49 | "CXXFLAGS": "-Dredacted=0", 50 | }, 51 | configure_in_place = True, 52 | lib_source = "@z3//:all", 53 | make_commands = [ 54 | "cd build", 55 | "make -j2", # Use -j2, hardcoded but otherwise it will be too slow. 56 | "make install", 57 | ], 58 | shared_libraries = ["libz3.so"], 59 | visibility = ["//visibility:public"], 60 | ) 61 | -------------------------------------------------------------------------------- /p4-samples/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # Files that store various logs 16 | SIMPLE_SWITCH_LOG := switch.log 17 | SIMPLE_SWITCH_CLI_LOG := cli.log 18 | 19 | # Files that store running process id for clean stop later 20 | SIMPLE_SWITCH_PID := switch.pid 21 | TCPDUMP_PID := packets.pid 22 | 23 | # All compiled file names 24 | SOURCE_FILE_NAMES := $(wildcard *.p4) 25 | COMPILED_FILE_NAMES := $(SOURCE_FILE_NAMES:.p4=.json) 26 | 27 | # 28 | # Compiling 29 | # 30 | build: $(COMPILED_FILE_NAMES) 31 | 32 | %.json: %.p4 33 | p4c --std p4_16 --target bmv2 --arch v1model --p4runtime-files $(basename $@).pb.txt $< 34 | 35 | # 36 | # bmv2 simulation 37 | # run the simple_switch simulator on compiled program 38 | # store process id for later killing 39 | # 40 | simple_switch: build veth $(SIMPLE_SWITCH_PID) 41 | 42 | $(SIMPLE_SWITCH_PID): 43 | @echo "simple switch logs available at $(SIMPLE_SWITCH_LOG)" 44 | @{ sudo simple_switch --interface 0@veth0 --interface 1@veth2 $(COMPILED_FILE_NAMES) > $(SIMPLE_SWITCH_LOG) & echo $$! > $(SIMPLE_SWITCH_PID); } 45 | @sleep 5 46 | 47 | veth: 48 | # create virtual ethernet interfaces 49 | @sudo ../veth_setup.sh $(VETH_PAIRS_COUNT) > /dev/null 50 | 51 | # 52 | # Control Plane 53 | # insert entries into p4 tables 54 | # 55 | control_plane: $(SIMPLE_SWITCH_PID) 56 | @echo "simple_switch_CLI logs available at $(SIMPLE_SWITCH_CLI_LOG)" 57 | @simple_switch_CLI <<< $(CONTROL_PLANE_COMMANDS) > $(SIMPLE_SWITCH_CLI_LOG) 58 | 59 | # 60 | # Sending Test Packets 61 | # 62 | $(TCPDUMP_PID): 63 | @sudo tcpdump -n -i veth3 & echo $$! > $(TCPDUMP_PID) 64 | 65 | packets: $(TCPDUMP_PID) 66 | @sudo scapy <<< $$'$(PACKETS)' > /dev/null 67 | 68 | # 69 | # Stopping all background processing and cleaning 70 | # 71 | stop: kill clean 72 | 73 | kill: 74 | @sudo kill `cat $(SIMPLE_SWITCH_PID)` || echo "simple switch is not running!" 75 | @sudo rm -f $(SIMPLE_SWITCH_PID) 76 | @sudo kill `cat $(TCPDUMP_PID)` || echo "tcpdump is not running!" 77 | @sudo rm -f $(TCPDUMP_PID) 78 | 79 | clean: 80 | @rm -f *.json *.p4i *.log *.info.* *.pb.txt 81 | 82 | # PHONY rules with names that do not correspond to files 83 | .PHONY: build simple_switch veth control_plane packets stop kill clean 84 | -------------------------------------------------------------------------------- /p4_symbolic/bmv2/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_proto_library") 16 | load("@rules_proto//proto:defs.bzl", "proto_library") 17 | load("//p4_symbolic/bmv2:test.bzl", "bmv2_protobuf_parsing_test") 18 | 19 | cc_proto_library( 20 | name = "bmv2_cc_proto", 21 | visibility = ["//p4_symbolic:__subpackages__"], 22 | deps = [":bmv2_proto"], 23 | ) 24 | 25 | proto_library( 26 | name = "bmv2_proto", 27 | srcs = [ 28 | "bmv2.proto", 29 | ], 30 | visibility = ["//p4_symbolic:__subpackages__"], 31 | deps = [ 32 | "@com_google_protobuf//:struct_proto", 33 | ], 34 | ) 35 | 36 | cc_library( 37 | name = "bmv2", 38 | srcs = [ 39 | "bmv2.cc", 40 | ], 41 | hdrs = [ 42 | "bmv2.h", 43 | ], 44 | visibility = ["//p4_symbolic:__subpackages__"], 45 | deps = [ 46 | ":bmv2_cc_proto", 47 | "//p4_symbolic/util", 48 | "@p4_pdpi//gutil:status", 49 | ], 50 | ) 51 | 52 | cc_binary( 53 | name = "test", 54 | srcs = ["test.cc"], 55 | deps = [ 56 | ":bmv2", 57 | ":bmv2_cc_proto", 58 | "//p4_symbolic/util", 59 | "@com_google_absl//absl/flags:flag", 60 | "@com_google_absl//absl/flags:parse", 61 | "@com_google_absl//absl/flags:usage", 62 | "@com_google_absl//absl/status", 63 | "@com_google_absl//absl/strings", 64 | "@p4_pdpi//gutil:status", 65 | ], 66 | ) 67 | 68 | # Test rules: one per program in //p4-samples 69 | bmv2_protobuf_parsing_test( 70 | name = "port_table", 71 | golden_file = "expected/table.pb.txt", 72 | p4_program = "//p4-samples:port-table/table.p4", 73 | ) 74 | 75 | bmv2_protobuf_parsing_test( 76 | name = "ipv4_routing", 77 | golden_file = "expected/basic.pb.txt", 78 | p4_program = "//p4-samples:ipv4-routing/basic.p4", 79 | ) 80 | 81 | bmv2_protobuf_parsing_test( 82 | name = "port_hardcoded", 83 | golden_file = "expected/hardcoded.pb.txt", 84 | p4_program = "//p4-samples:hardcoded/hardcoded.p4", 85 | ) 86 | 87 | bmv2_protobuf_parsing_test( 88 | name = "reflector", 89 | golden_file = "expected/reflector.pb.txt", 90 | p4_program = "//p4-samples:reflector/reflector.p4", 91 | ) 92 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/control.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This file is responsible for evaluating a control construct in a P4 16 | // program flow. A control construct could be a table match or a conditional 17 | // leading to a table match. 18 | // 19 | // Check the header file for a detailed explanation. 20 | 21 | #include "p4_symbolic/symbolic/control.h" 22 | 23 | #include 24 | 25 | #include "absl/status/status.h" 26 | #include "absl/strings/str_cat.h" 27 | #include "p4_pdpi/ir.pb.h" 28 | #include "p4_symbolic/symbolic/conditional.h" 29 | #include "p4_symbolic/symbolic/table.h" 30 | 31 | namespace p4_symbolic { 32 | namespace symbolic { 33 | namespace control { 34 | 35 | gutil::StatusOr EvaluateControl( 36 | const Dataplane &data_plane, const std::string &control_name, 37 | SymbolicPerPacketState *state, values::P4RuntimeTranslator *translator, 38 | const z3::expr &guard) { 39 | // Base case: we got to the end of the evaluation, no more controls! 40 | if (control_name.empty()) { 41 | return SymbolicTrace{{}, Z3Context().bool_val(false)}; 42 | } 43 | 44 | // Find out what type of control we need to evaluate. 45 | if (data_plane.program.tables().count(control_name) == 1) { 46 | // Table: call EvaluateTable on table and its entries. 47 | const ir::Table &table = data_plane.program.tables().at(control_name); 48 | std::vector table_entries; 49 | if (data_plane.entries.count(control_name) == 1) { 50 | table_entries = data_plane.entries.at(control_name); 51 | } 52 | return table::EvaluateTable(data_plane, table, table_entries, state, 53 | translator, guard); 54 | } else if (data_plane.program.conditionals().count(control_name) == 1) { 55 | // Conditional: let EvaluateConditional handle it. 56 | const ir::Conditional &conditional = 57 | data_plane.program.conditionals().at(control_name); 58 | return conditional::EvaluateConditional(data_plane, conditional, state, 59 | translator, guard); 60 | } else { 61 | // Something else: unsupported. 62 | return absl::UnimplementedError( 63 | absl::StrCat("Unsupported control \"", control_name, 64 | "\" is neither a table nor a conditional")); 65 | } 66 | } 67 | 68 | } // namespace control 69 | } // namespace symbolic 70 | } // namespace p4_symbolic 71 | -------------------------------------------------------------------------------- /p4_symbolic/util/status.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/util/status.h" 16 | 17 | namespace p4_symbolic { 18 | namespace util { 19 | 20 | namespace pb_err = google::protobuf::util::error; 21 | 22 | absl::Status ProtobufToAbslStatus( 23 | const google::protobuf::util::Status& status) { 24 | absl::StatusCode out_code; 25 | 26 | google::protobuf::util::error::Code code = status.code(); 27 | switch (code) { 28 | case pb_err::OK: 29 | out_code = absl::StatusCode::kOk; 30 | break; 31 | case pb_err::CANCELLED: 32 | out_code = absl::StatusCode::kCancelled; 33 | break; 34 | case pb_err::UNKNOWN: 35 | out_code = absl::StatusCode::kUnknown; 36 | break; 37 | case pb_err::INVALID_ARGUMENT: 38 | out_code = absl::StatusCode::kInvalidArgument; 39 | break; 40 | case pb_err::DEADLINE_EXCEEDED: 41 | out_code = absl::StatusCode::kDeadlineExceeded; 42 | break; 43 | case pb_err::NOT_FOUND: 44 | out_code = absl::StatusCode::kNotFound; 45 | break; 46 | case pb_err::ALREADY_EXISTS: 47 | out_code = absl::StatusCode::kAlreadyExists; 48 | break; 49 | case pb_err::PERMISSION_DENIED: 50 | out_code = absl::StatusCode::kPermissionDenied; 51 | break; 52 | case pb_err::UNAUTHENTICATED: 53 | out_code = absl::StatusCode::kUnauthenticated; 54 | break; 55 | case pb_err::RESOURCE_EXHAUSTED: 56 | out_code = absl::StatusCode::kResourceExhausted; 57 | break; 58 | case pb_err::FAILED_PRECONDITION: 59 | out_code = absl::StatusCode::kFailedPrecondition; 60 | break; 61 | case pb_err::ABORTED: 62 | out_code = absl::StatusCode::kAborted; 63 | break; 64 | case pb_err::OUT_OF_RANGE: 65 | out_code = absl::StatusCode::kOutOfRange; 66 | break; 67 | case pb_err::UNIMPLEMENTED: 68 | out_code = absl::StatusCode::kUnimplemented; 69 | break; 70 | case pb_err::INTERNAL: 71 | out_code = absl::StatusCode::kInternal; 72 | break; 73 | case pb_err::UNAVAILABLE: 74 | out_code = absl::StatusCode::kUnavailable; 75 | break; 76 | case pb_err::DATA_LOSS: 77 | out_code = absl::StatusCode::kDataLoss; 78 | break; 79 | default: 80 | out_code = absl::StatusCode::kUnknown; 81 | } 82 | 83 | return absl::Status(out_code, status.error_message().ToString()); 84 | } 85 | 86 | } // namespace util 87 | } // namespace p4_symbolic 88 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/packet.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Contains helpers for creating, extracting, and managing concerete and 16 | // symbolic packet structs. 17 | 18 | #include "p4_symbolic/symbolic/packet.h" 19 | 20 | #include 21 | 22 | namespace p4_symbolic { 23 | namespace symbolic { 24 | namespace packet { 25 | 26 | namespace { 27 | 28 | // Get the symbolic field value from state or return a default value 29 | // of the given size. 30 | z3::expr GetOrDefault(SymbolicPerPacketState state, const std::string &field, 31 | unsigned int default_value_bit_size) { 32 | if (state.ContainsKey(field)) { 33 | return state.Get(field).value(); 34 | } 35 | return Z3Context().bv_val(-1, default_value_bit_size); 36 | } 37 | 38 | } // namespace 39 | 40 | SymbolicPacket ExtractSymbolicPacket(SymbolicPerPacketState state) { 41 | z3::expr ipv6_src = GetOrDefault(state, "ipv6.src_addr", 128); 42 | z3::expr ipv6_dst = GetOrDefault(state, "ipv6.dst_addr", 128); 43 | 44 | return {GetOrDefault(state, "ethernet.src_addr", 48), 45 | GetOrDefault(state, "ethernet.dst_addr", 48), 46 | GetOrDefault(state, "ethernet.ether_type", 16), 47 | 48 | GetOrDefault(state, "ipv4.src_addr", 32), 49 | GetOrDefault(state, "ipv4.dst_addr", 32), 50 | ipv6_dst.extract(127, 64), 51 | ipv6_dst.extract(63, 0), 52 | GetOrDefault(state, "ipv4.protocol", 8), 53 | GetOrDefault(state, "ipv4.dscp", 6), 54 | GetOrDefault(state, "ipv4.ttl", 8), 55 | 56 | GetOrDefault(state, "icmp.type", 8)}; 57 | } 58 | 59 | ConcretePacket ExtractConcretePacket(SymbolicPacket packet, z3::model model) { 60 | return {model.eval(packet.eth_src, true).to_string(), 61 | model.eval(packet.eth_dst, true).to_string(), 62 | model.eval(packet.eth_type, true).to_string(), 63 | 64 | model.eval(packet.ipv4_src, true).to_string(), 65 | model.eval(packet.ipv4_dst, true).to_string(), 66 | model.eval(packet.ipv6_dst_upper, true).to_string(), 67 | model.eval(packet.ipv6_dst_lower, true).to_string(), 68 | model.eval(packet.protocol, true).to_string(), 69 | model.eval(packet.dscp, true).to_string(), 70 | model.eval(packet.ttl, true).to_string(), 71 | 72 | model.eval(packet.icmp_type, true).to_string()}; 73 | } 74 | 75 | } // namespace packet 76 | } // namespace symbolic 77 | } // namespace p4_symbolic 78 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | load("@rules_cc//cc:defs.bzl", "cc_library") 16 | load("//p4_symbolic/symbolic:test.bzl", "end_to_end_test") 17 | 18 | cc_library( 19 | name = "symbolic", 20 | srcs = [ 21 | "action.cc", 22 | "conditional.cc", 23 | "control.cc", 24 | "guarded_map.cc", 25 | "operators.cc", 26 | "packet.cc", 27 | "parser.cc", 28 | "symbolic.cc", 29 | "table.cc", 30 | "util.cc", 31 | "values.cc", 32 | ], 33 | hdrs = [ 34 | "action.h", 35 | "conditional.h", 36 | "control.h", 37 | "guarded_map.h", 38 | "operators.h", 39 | "packet.h", 40 | "parser.h", 41 | "symbolic.h", 42 | "table.h", 43 | "util.h", 44 | "values.h", 45 | ], 46 | visibility = ["//p4_symbolic:__subpackages__"], 47 | deps = [ 48 | "//:z3", 49 | "//p4_symbolic/ir:ir_cc_proto", 50 | "//p4_symbolic/ir:table_entries", 51 | "@com_google_absl//absl/status", 52 | "@com_google_absl//absl/strings", 53 | "@com_google_protobuf//:protobuf_headers", 54 | "@p4_pdpi//gutil:status", 55 | "@p4_pdpi//p4_pdpi:ir_cc_proto", 56 | "@p4_pdpi//p4_pdpi/utils:ir", 57 | ], 58 | ) 59 | 60 | # Golden file testing rules to test p4_symbolic/main.cc and the SMT 61 | # program generated by p4_symbolic/symbolic/symbolic.cc. 62 | end_to_end_test( 63 | name = "port_table_test", 64 | output_golden_file = "expected/table.txt", 65 | p4_program = "//p4-samples:port-table/table.p4", 66 | smt_golden_file = "expected/table.smt2", 67 | table_entries = "//p4-samples:port-table/entries.pb.txt", 68 | ) 69 | 70 | end_to_end_test( 71 | name = "port_hardcoded_test", 72 | output_golden_file = "expected/hardcoded.txt", 73 | p4_program = "//p4-samples:hardcoded/hardcoded.p4", 74 | smt_golden_file = "expected/hardcoded.smt2", 75 | ) 76 | 77 | end_to_end_test( 78 | name = "reflector_test", 79 | output_golden_file = "expected/reflector.txt", 80 | p4_program = "//p4-samples:reflector/reflector.p4", 81 | smt_golden_file = "expected/reflector.smt2", 82 | ) 83 | 84 | end_to_end_test( 85 | name = "ipv4_routing_test", 86 | output_golden_file = "expected/basic.txt", 87 | p4_program = "//p4-samples:ipv4-routing/basic.p4", 88 | smt_golden_file = "expected/basic.smt2", 89 | table_entries = "//p4-samples:ipv4-routing/entries.pb.txt", 90 | ) 91 | -------------------------------------------------------------------------------- /p4_symbolic/ir/test.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # This file contains rule definitions for running 16 | # the test binary to produce output json and protobuf files, 17 | # subset diff the input and output json files, and golden file 18 | # testing the output protobuf files against the expected files. 19 | # It also defines a Macro that simplifies defining a protobuf test 20 | # over a sample P4 program. 21 | 22 | load("//:p4c.bzl", "run_p4c") 23 | load("//p4_symbolic/bmv2:test.bzl", "exact_diff_test") 24 | 25 | # Macro that defines exact diff IR testing rules for a given p4 program 26 | # with all their dependent rules. 27 | # The macro defines these rules in order: 28 | # 1. A rule for producing bmv2 json and p4info files from a .p4 file using p4c. 29 | # 2. A rule for parsing the bmv2 json and p4info using p4_symbolic/main.cc, and 30 | # dumping a protobuf output file. 31 | # 3. A rule for golden file testing of the protobuf output file against 32 | # the specified expected file. 33 | # Use the p4_deps list to specify dependent files that p4_program input 34 | # file depends on (e.g. by including them). 35 | def ir_parsing_test(name, p4_program, golden_file, table_entries = None, p4_deps = []): 36 | p4c_name = "%s_p4c" % name 37 | parse_name = "%s_parse" % name 38 | p4info_file = "%s_bazel-p4c-tmp-output/p4info.pb.txt" % p4c_name 39 | 40 | optional_table_entries = [] 41 | optional_table_entry_arg = "" 42 | if table_entries: 43 | optional_table_entries = [table_entries] 44 | optional_table_entry_arg = "--entries=$(location %s)" % table_entries 45 | 46 | # Run p4c to get bmv2 JSON and p4info.pb.txt files. 47 | run_p4c( 48 | name = p4c_name, 49 | src = p4_program, 50 | deps = p4_deps, 51 | p4runtime_files = [p4info_file], 52 | ) 53 | 54 | # Use p4_symbolic/ir/test.cc to parse input json with p4info and dump 55 | # (tmp) output .pb.txt. 56 | output_filename = name + "_tmp.pb.txt" 57 | native.genrule( 58 | name = parse_name, 59 | srcs = [":" + p4c_name, p4info_file] + optional_table_entries, 60 | outs = [output_filename], 61 | tools = ["//p4_symbolic/ir:test"], 62 | cmd = ( 63 | "$(location //p4_symbolic/ir:test) --bmv2=$(location %s) " + 64 | "--p4info=$(location %s) %s &> $(OUTS) || true" 65 | ) % (":" + p4c_name, p4info_file, optional_table_entry_arg), 66 | ) 67 | 68 | # Exact diff test between output and golden protobuf files. 69 | exact_diff_test( 70 | name = name, 71 | actual = output_filename, 72 | expected = golden_file, 73 | ) 74 | -------------------------------------------------------------------------------- /p4_symbolic/ir/test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Test file for producing IR representations of P4 Programs. 16 | 17 | #include 18 | #include 19 | 20 | #include "absl/flags/flag.h" 21 | #include "absl/flags/parse.h" 22 | #include "absl/flags/usage.h" 23 | #include "absl/status/status.h" 24 | #include "absl/strings/str_format.h" 25 | #include "gutil/status.h" 26 | #include "p4_symbolic/parser.h" 27 | 28 | ABSL_FLAG(std::string, p4info, "", 29 | "The path to the p4info protobuf file (required)"); 30 | ABSL_FLAG(std::string, bmv2, "", "The path to the bmv2 json file (required)"); 31 | ABSL_FLAG(std::string, entries, "", 32 | "The path to the table entries txt file (optional), leave this empty " 33 | "if the input p4 program contains no (explicit) tables for which " 34 | "entries are needed."); 35 | 36 | namespace { 37 | 38 | absl::Status Test() { 39 | const std::string &p4info_path = absl::GetFlag(FLAGS_p4info); 40 | const std::string &bmv2_path = absl::GetFlag(FLAGS_bmv2); 41 | const std::string &entries_path = absl::GetFlag(FLAGS_entries); 42 | 43 | RET_CHECK(!p4info_path.empty()); 44 | RET_CHECK(!bmv2_path.empty()); 45 | 46 | // Transform to IR and print. 47 | ASSIGN_OR_RETURN( 48 | p4_symbolic::symbolic::Dataplane dataplane, 49 | p4_symbolic::ParseToIr(bmv2_path, p4info_path, entries_path)); 50 | 51 | // Dump string representation to stdout. 52 | std::cout << dataplane.program.DebugString() << std::endl; 53 | for (const auto &[name, entries] : dataplane.entries) { 54 | std::cout << "=====" << name << " Entries=====" << std::endl; 55 | std::cout << std::endl; 56 | for (const pdpi::IrTableEntry &entry : entries) { 57 | std::cout << entry.DebugString() << std::endl; 58 | } 59 | } 60 | 61 | return absl::OkStatus(); 62 | } 63 | 64 | } // namespace 65 | 66 | int main(int argc, char *argv[]) { 67 | // Verify link and compile versions are the same. 68 | GOOGLE_PROTOBUF_VERIFY_VERSION; 69 | 70 | // Command line arugments and help message. 71 | absl::SetProgramUsageMessage( 72 | absl::StrFormat("usage: %s %s", argv[0], 73 | "--bmv2=path/to/bmv2.json --p4info=path/to/p4info.pb.txt " 74 | "[--entries=path/to/table_entries.txt]")); 75 | absl::ParseCommandLine(argc, argv); 76 | 77 | // Run test. 78 | absl::Status status = Test(); 79 | 80 | // Clean up 81 | google::protobuf::ShutdownProtobufLibrary(); 82 | 83 | // Error handling. 84 | if (!status.ok()) { 85 | std::cerr << "Error: " << status << std::endl; 86 | return 1; 87 | } 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/guarded_map.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Defines our SymbolicGuardedMap class. 16 | 17 | #include "p4_symbolic/symbolic/guarded_map.h" 18 | 19 | #include "absl/strings/str_cat.h" 20 | #include "absl/strings/str_format.h" 21 | #include "p4_symbolic/symbolic/operators.h" 22 | #include "p4_symbolic/symbolic/util.h" 23 | 24 | namespace p4_symbolic { 25 | namespace symbolic { 26 | 27 | gutil::StatusOr 28 | SymbolicGuardedMap::CreateSymbolicGuardedMap( 29 | const google::protobuf::Map &headers) { 30 | ASSIGN_OR_RETURN(auto map, util::FreeSymbolicHeaders(headers)); 31 | return SymbolicGuardedMap(map); 32 | } 33 | 34 | bool SymbolicGuardedMap::ContainsKey(const std::string &key) const { 35 | return this->map_.count(key) == 1; 36 | } 37 | 38 | gutil::StatusOr SymbolicGuardedMap::Get( 39 | const std::string &key) const { 40 | if (this->ContainsKey(key)) { 41 | return this->map_.at(key); 42 | } 43 | 44 | return absl::InvalidArgumentError( 45 | absl::StrCat("Cannot find key \"", key, "\" in SymbolicGuardedMap!")); 46 | } 47 | 48 | absl::Status SymbolicGuardedMap::Set(const std::string &key, 49 | const z3::expr &value, 50 | const z3::expr &guard) { 51 | if (!this->ContainsKey(key)) { 52 | return absl::InvalidArgumentError(absl::StrCat( 53 | "Cannot assign to key \"", key, "\" in SymbolicGuardedMap!")); 54 | } 55 | 56 | z3::expr &old_value = this->map_.at(key); 57 | 58 | // operators::Ite will check for sort compatibility and pad when needed. 59 | // However, Ite() does not have a notion of pre-defined size, and will padd 60 | // to the maximum bitsize of the two operands. We perform that check 61 | // explicitly here. 62 | if (old_value.get_sort().is_bv() && value.get_sort().is_bv()) { 63 | unsigned int new_size = value.get_sort().bv_size(); 64 | unsigned int old_size = old_value.get_sort().bv_size(); 65 | if (new_size > old_size) { 66 | return absl::InvalidArgumentError( 67 | absl::StrFormat("Cannot assign to key \"%s\" a value whose bit size " 68 | "%d is greater than the pre-defined bit size %d in " 69 | "SymbolicGuardedMap!", 70 | key, new_size, old_size)); 71 | } 72 | } 73 | 74 | // This will return an absl error if the sorts are incompatible, and will pad 75 | // shorter bit vectors. 76 | ASSIGN_OR_RETURN(old_value, operators::Ite(guard, value, old_value)); 77 | return absl::OkStatus(); 78 | } 79 | 80 | } // namespace symbolic 81 | } // namespace p4_symbolic 82 | -------------------------------------------------------------------------------- /docs/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Google Open Source Community Guidelines 2 | 3 | At Google, we recognize and celebrate the creativity and collaboration of open 4 | source contributors and the diversity of skills, experiences, cultures, and 5 | opinions they bring to the projects and communities they participate in. 6 | 7 | Every one of Google's open source projects and communities are inclusive 8 | environments, based on treating all individuals respectfully, regardless of 9 | gender identity and expression, sexual orientation, disabilities, 10 | neurodiversity, physical appearance, body size, ethnicity, nationality, race, 11 | age, religion, or similar personal characteristic. 12 | 13 | We value diverse opinions, but we value respectful behavior more. 14 | 15 | Respectful behavior includes: 16 | 17 | * Being considerate, kind, constructive, and helpful. 18 | * Not engaging in demeaning, discriminatory, harassing, hateful, sexualized, or 19 | physically threatening behavior, speech, and imagery. 20 | * Not engaging in unwanted physical contact. 21 | 22 | Some Google open source projects [may adopt][] an explicit project code of 23 | conduct, which may have additional detailed expectations for participants. Most 24 | of those projects will use our [modified Contributor Covenant][]. 25 | 26 | [may adopt]: https://opensource.google/docs/releasing/preparing/#conduct 27 | [modified Contributor Covenant]: https://opensource.google/docs/releasing/template/CODE_OF_CONDUCT/ 28 | 29 | ## Resolve peacefully 30 | 31 | We do not believe that all conflict is necessarily bad; healthy debate and 32 | disagreement often yields positive results. However, it is never okay to be 33 | disrespectful. 34 | 35 | If you see someone behaving disrespectfully, you are encouraged to address the 36 | behavior directly with those involved. Many issues can be resolved quickly and 37 | easily, and this gives people more control over the outcome of their dispute. 38 | If you are unable to resolve the matter for any reason, or if the behavior is 39 | threatening or harassing, report it. We are dedicated to providing an 40 | environment where participants feel welcome and safe. 41 | 42 | ## Reporting problems 43 | 44 | Some Google open source projects may adopt a project-specific code of conduct. 45 | In those cases, a Google employee will be identified as the Project Steward, 46 | who will receive and handle reports of code of conduct violations. In the event 47 | that a project hasn’t identified a Project Steward, you can report problems by 48 | emailing opensource@google.com. 49 | 50 | We will investigate every complaint, but you may not receive a direct response. 51 | We will use our discretion in determining when and how to follow up on reported 52 | incidents, which may range from not taking action to permanent expulsion from 53 | the project and project-sponsored spaces. We will notify the accused of the 54 | report and provide them an opportunity to discuss it before any action is 55 | taken. The identity of the reporter will be omitted from the details of the 56 | report supplied to the accused. In potentially harmful situations, such as 57 | ongoing harassment or threats to anyone's safety, we may take action without 58 | notice. 59 | 60 | *This document was adapted from the [IndieWeb Code of Conduct][] and can also 61 | be found at .* 62 | 63 | [IndieWeb Code of Conduct]: https://indieweb.org/code-of-conduct 64 | -------------------------------------------------------------------------------- /p4_symbolic/bmv2/test_sdiff.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # This file implements a recursive subset diff between two json/dict objects. 16 | 17 | import sys 18 | import json 19 | 20 | 21 | def sdiff(actual, expected, path): 22 | """ Performs a subset diff between two python dicts. 23 | 24 | Actual is said to be a subset of expected iff: 25 | 1. Every key in Actual exists in expected, and its value is a subset 26 | of the value in expected. 27 | 2. For base values (numbers, strings, etc), the values have to be equal. 28 | 3. For lists, the two lists must be equal in length, and every element in 29 | actual must be a subset of the corresponding element in expected. 30 | 4. None values in expected are allowed to have a matching default value in 31 | actual (e.g. "", False, 0, []). 32 | 33 | Args: 34 | actual: the subset dict. 35 | expected: the super set dict. 36 | path: a string specifying the current path in the recursive sdiff. 37 | 38 | Returns: 39 | A tuple (status: bool, path: str), where status is True if actual 40 | is indeed a subset of expected, or False otherwise, and path is 41 | the empty string if actual is a subset, or the path of an element 42 | where actual disagreed with expected. 43 | 44 | When the path looks like foo/bar it means that 45 | "actual[foo][bar]" is an offending element that fails to meet 46 | the subset criteria. 47 | """ 48 | try: 49 | if expected is None and not actual: # equate default values to None 50 | return (True, "") 51 | 52 | if type(actual) != type(expected): 53 | return (False, path) 54 | 55 | if isinstance(actual, list): 56 | if len(actual) != len(expected): 57 | return (False, path) 58 | 59 | for i in range(len(actual)): 60 | status, rpath = sdiff(actual[i], expected[i], "%s/%d" % (path, i)) 61 | if not status: 62 | return (status, rpath) 63 | return (True, "") 64 | 65 | if isinstance(actual, dict): 66 | for k in actual.keys(): 67 | status, rpath = sdiff(actual[k], 68 | expected.get(k, None), 69 | "%s/%s" % (path, str(k))) 70 | if not status: 71 | return (status, rpath) 72 | return (True, "") 73 | 74 | return (actual == expected, path) 75 | except Exception as exp: 76 | return (False, "%s EXCEPTION %s" % (path, str(exp))) 77 | 78 | 79 | if __name__ == "__main__": 80 | expectedFilePath = sys.argv[1] 81 | with open(expectedFilePath) as expectedFile: 82 | expected = json.load(expectedFile) 83 | 84 | actualFilePath = sys.argv[2] 85 | with open(actualFilePath) as actualFile: 86 | actual = json.load(actualFile) 87 | 88 | status, path = sdiff(actual, expected, "") 89 | if not status: 90 | sys.exit("not subset diff! Error at path \"%s\"" % path) 91 | -------------------------------------------------------------------------------- /p4_symbolic/ir/table_entries.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/ir/table_entries.h" 16 | 17 | #include "absl/status/status.h" 18 | #include "absl/strings/str_cat.h" 19 | #include "absl/strings/str_format.h" 20 | #include "absl/strings/str_split.h" 21 | #include "gutil/proto.h" 22 | #include "p4/v1/p4runtime.pb.h" 23 | #include "p4_pdpi/ir.h" 24 | #include "p4_symbolic/util/io.h" 25 | 26 | namespace p4_symbolic { 27 | namespace ir { 28 | 29 | gutil::StatusOr ParseAndFillEntries( 30 | const pdpi::IrP4Info &p4info, const std::string &entries_path) { 31 | // Parse table entries as a plain p4.v1.TableEntry proto. 32 | p4::v1::WriteRequest entries_file; 33 | RETURN_IF_ERROR( 34 | gutil::ReadProtoFromFile(entries_path.c_str(), &entries_file)); 35 | 36 | // Use pdpi to transform each plain p4.v1.TableEntry to the pd representation 37 | // pdpi.ir.IrTableEntry. 38 | TableEntries output; 39 | for (const p4::v1::Update &update : entries_file.updates()) { 40 | // Make sure update is of type insert. 41 | if (update.type() != p4::v1::Update::INSERT) { 42 | return absl::InvalidArgumentError( 43 | absl::StrCat("Table entries file contains a non-insert update ", 44 | update.DebugString())); 45 | } 46 | 47 | // Make sure the entity is a table entry. 48 | const p4::v1::Entity &entity = update.entity(); 49 | if (entity.entity_case() != p4::v1::Entity::kTableEntry) { 50 | return absl::InvalidArgumentError( 51 | absl::StrCat("Table entries file contains a non-table entry entity ", 52 | entity.DebugString())); 53 | } 54 | 55 | // Read table entry as a program-independent P4RT protobuf. 56 | // Transform it to a program dependent pdpi protobuf. 57 | const p4::v1::TableEntry &pi_entry = entity.table_entry(); 58 | ASSIGN_OR_RETURN(pdpi::IrTableEntry pdpi_entry, 59 | pdpi::PiTableEntryToIr(p4info, pi_entry)); 60 | 61 | // Replace table and action aliases with their respective full name. 62 | const std::string &table_alias = pdpi_entry.table_name(); 63 | const std::string &action_alias = pdpi_entry.action().name(); 64 | 65 | // Make sure both table and action referred to by entry exist. 66 | RET_CHECK(p4info.tables_by_name().count(table_alias) == 1); 67 | RET_CHECK(p4info.actions_by_name().count(action_alias) == 1); 68 | 69 | const std::string &table_name = 70 | p4info.tables_by_name().at(table_alias).preamble().name(); 71 | const std::string &action_name = 72 | p4info.actions_by_name().at(action_alias).preamble().name(); 73 | 74 | pdpi_entry.mutable_action()->set_name(action_name); 75 | pdpi_entry.set_table_name(table_name); 76 | output[pdpi_entry.table_name()].push_back(pdpi_entry); 77 | } 78 | return output; 79 | } 80 | 81 | } // namespace ir 82 | } // namespace p4_symbolic 83 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/operators.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Defines a wrapper around z3 c++ API operators. 16 | // The wrappers ensure sort compatibility, and pad bitvectors when needed. 17 | // Additionally, they use absl::Status to convey sort compatibility failures 18 | // instead of runtime crashes. 19 | 20 | #ifndef P4_SYMBOLIC_SYMBOLIC_OPERATORS_H_ 21 | #define P4_SYMBOLIC_SYMBOLIC_OPERATORS_H_ 22 | 23 | #include 24 | 25 | #include "gutil/status.h" 26 | #include "z3++.h" 27 | 28 | namespace p4_symbolic { 29 | namespace symbolic { 30 | namespace operators { 31 | 32 | // Free variable. 33 | gutil::StatusOr FreeVariable(const std::string &variable_base_name, 34 | const z3::sort &sort); 35 | 36 | // Arithmetic operations. 37 | gutil::StatusOr Plus(const z3::expr &a, const z3::expr &b); 38 | gutil::StatusOr Minus(const z3::expr &a, const z3::expr &b); 39 | gutil::StatusOr Times(const z3::expr &a, const z3::expr &b); 40 | 41 | // Relational operations. 42 | gutil::StatusOr Eq(const z3::expr &a, const z3::expr &b); 43 | gutil::StatusOr Neq(const z3::expr &a, const z3::expr &b); 44 | gutil::StatusOr Lt(const z3::expr &a, const z3::expr &b); 45 | gutil::StatusOr Lte(const z3::expr &a, const z3::expr &b); 46 | gutil::StatusOr Gt(const z3::expr &a, const z3::expr &b); 47 | gutil::StatusOr Gte(const z3::expr &a, const z3::expr &b); 48 | 49 | // Boolean operations. 50 | gutil::StatusOr Not(const z3::expr &a); 51 | gutil::StatusOr And(const z3::expr &a, const z3::expr &b); 52 | gutil::StatusOr Or(const z3::expr &a, const z3::expr &b); 53 | 54 | // Binary operations. 55 | gutil::StatusOr BitNeg(const z3::expr &a); 56 | gutil::StatusOr BitAnd(const z3::expr &a, const z3::expr &b); 57 | gutil::StatusOr BitOr(const z3::expr &a, const z3::expr &b); 58 | gutil::StatusOr BitXor(const z3::expr &a, const z3::expr &b); 59 | gutil::StatusOr LShift(const z3::expr &bits, const z3::expr &shift); 60 | gutil::StatusOr RShift(const z3::expr &bits, const z3::expr &shift); 61 | 62 | // If-then-else. 63 | gutil::StatusOr Ite(const z3::expr &condition, 64 | const z3::expr &true_value, 65 | const z3::expr &false_value); 66 | 67 | // Converts the expression into a semantically equivalent boolean expression. 68 | gutil::StatusOr ToBoolSort(const z3::expr &a); 69 | gutil::StatusOr ToBitVectorSort(const z3::expr &a, unsigned int size); 70 | 71 | // Prefix equality: this is the basis for evaluating LPMs. 72 | gutil::StatusOr PrefixEq(const z3::expr &a, const z3::expr &b, 73 | unsigned int prefix_size); 74 | 75 | } // namespace operators 76 | } // namespace symbolic 77 | } // namespace p4_symbolic 78 | 79 | #endif // P4_SYMBOLIC_SYMBOLIC_OPERATORS_H_ 80 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This file is responsible for evaluating a control construct in a P4 16 | // program flow. A control construct could be a table match or a conditional 17 | // leading to a table match. 18 | // 19 | // The bmv2 format suggests it can also be a direct action call, but that is 20 | // unsupported by our IR and this file, supporting it should be easy if the 21 | // need arise. 22 | // 23 | // This file is responsible for evaluating that control on the current 24 | // evaluation state (essentially an assignment of header fields to symbolic 25 | // values). 26 | // 27 | // Evaluating the control (e.g. via table::EvaluateTable(...)) returns a suffix 28 | // SymbolicTrace: a trace that contains new values to header fields, as well as, 29 | // table matches, for the control that was evaluated, and all next controls 30 | // evaluated after it. This file returns that suffix trace as is. It is the 31 | // responsibility of the calling code to extend that suffix to cover previous 32 | // controls. 33 | // 34 | // Note that the first call to this file will return a complete trace. 35 | // 36 | // Control evaluation functions (e.g. table::EvaluateTable(...)) are responsible 37 | // for calling this file again on the next control constructs that should be 38 | // evaluated after they are evaluated. 39 | // 40 | // Example: 41 | // Table A is described in BMV2 JSON so that after it is matched, the next 42 | // control is Table B, which has no next control. 43 | // Our symbolic pipeline will call this file on table A, which calls 44 | // EvaluateTable(A, ...), which will evaluate A, and then call this file again 45 | // on table B, which then calls EvaluateTable(B, ...). 46 | // EvaluateTable(B, ...) will return a suffix trace to this file, consisting of 47 | // the header fields values after B is evaluated, as well as the symbolic match 48 | // for B. Our file will return it to EvaluateTable(A, ...), which will add the 49 | // symbolic match for table A to it, and then return it the very first call, 50 | // resulting in a complete trace. 51 | // 52 | // Conditionals operate similarly, but instead of appending a symbolic match 53 | // to the suffix trace, they call this file on two controls corresponding to 54 | // the conditional's two branches, then merge the two suffixes using symbolic 55 | // if-then-else on a symbolic condition equivalent to that conditional. 56 | 57 | #ifndef P4_SYMBOLIC_SYMBOLIC_CONTROL_H_ 58 | #define P4_SYMBOLIC_SYMBOLIC_CONTROL_H_ 59 | 60 | #include 61 | 62 | #include "gutil/status.h" 63 | #include "p4_symbolic/symbolic/symbolic.h" 64 | #include "p4_symbolic/symbolic/values.h" 65 | #include "z3++.h" 66 | 67 | namespace p4_symbolic { 68 | namespace symbolic { 69 | namespace control { 70 | 71 | // Evaluate the passed control construct. 72 | gutil::StatusOr EvaluateControl( 73 | const Dataplane &data_plane, const std::string &control_name, 74 | SymbolicPerPacketState *state, values::P4RuntimeTranslator *translator, 75 | const z3::expr &guard); 76 | 77 | } // namespace control 78 | } // namespace symbolic 79 | } // namespace p4_symbolic 80 | 81 | #endif // P4_SYMBOLIC_SYMBOLIC_CONTROL_H_ 82 | -------------------------------------------------------------------------------- /p4_symbolic/bmv2/test.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This is a test file for our protobuf specifications of bmv2 json. 16 | // It reads an input bmv2 json file (usually the output of p4c) specified 17 | // as a command line argument. 18 | // It parses that file using protobuf, and then dumps the parsed protobuf 19 | // objects using protobuf text format and json format to two output files. 20 | // The output files paths are provided as command line flags. 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "absl/flags/flag.h" 27 | #include "absl/flags/parse.h" 28 | #include "absl/flags/usage.h" 29 | #include "absl/status/status.h" 30 | #include "absl/strings/str_format.h" 31 | #include "google/protobuf/text_format.h" 32 | #include "google/protobuf/util/json_util.h" 33 | #include "gutil/status.h" 34 | #include "p4_symbolic/bmv2/bmv2.h" 35 | #include "p4_symbolic/util/io.h" 36 | 37 | ABSL_FLAG(std::string, bmv2, "", "The path to the bmv2 json file (required)"); 38 | ABSL_FLAG(std::string, protobuf, "", 39 | "The path to the output protobuf file (required)"); 40 | ABSL_FLAG(std::string, json, "", "The path to the output json file (required)"); 41 | 42 | namespace { 43 | 44 | absl::Status Test() { 45 | const std::string &bmv2_path = absl::GetFlag(FLAGS_bmv2); 46 | const std::string &protobuf_path = absl::GetFlag(FLAGS_protobuf); 47 | const std::string &json_path = absl::GetFlag(FLAGS_json); 48 | 49 | RET_CHECK(!bmv2_path.empty()); 50 | RET_CHECK(!protobuf_path.empty()); 51 | RET_CHECK(!json_path.empty()); 52 | 53 | // Parse JSON using bmv2.cc. 54 | ASSIGN_OR_RETURN(p4_symbolic::bmv2::P4Program bmv2, 55 | p4_symbolic::bmv2::ParseBmv2JsonFile(bmv2_path.c_str())); 56 | 57 | // Dumping protobuf. 58 | RETURN_IF_ERROR( 59 | p4_symbolic::util::WriteFile(bmv2.DebugString(), protobuf_path.c_str())); 60 | 61 | // Dumping JSON. 62 | google::protobuf::util::JsonPrintOptions dumping_options; 63 | dumping_options.add_whitespace = true; 64 | dumping_options.always_print_primitive_fields = true; 65 | dumping_options.preserve_proto_field_names = true; 66 | 67 | std::string json_output_str; 68 | google::protobuf::util::MessageToJsonString(bmv2, &json_output_str, 69 | dumping_options); 70 | RETURN_IF_ERROR( 71 | p4_symbolic::util::WriteFile(json_output_str, json_path.c_str())); 72 | 73 | return absl::OkStatus(); 74 | } 75 | 76 | } // namespace 77 | 78 | int main(int argc, char *argv[]) { 79 | // Verify link and compile versions are the same. 80 | GOOGLE_PROTOBUF_VERIFY_VERSION; 81 | 82 | // Command line arugments and help message. 83 | absl::SetProgramUsageMessage( 84 | absl::StrFormat("usage: %s %s", argv[0], 85 | "--bmv2=path/to/bmv2.json " 86 | "--protobuf=path/to/output.pb.txt " 87 | "--json=path/to/output.json")); 88 | absl::ParseCommandLine(argc, argv); 89 | 90 | // Run test. 91 | absl::Status status = Test(); 92 | 93 | // Clean up. 94 | google::protobuf::ShutdownProtobufLibrary(); 95 | 96 | // Error handling. 97 | if (!status.ok()) { 98 | std::cerr << "Error: " << status << std::endl; 99 | return 1; 100 | } 101 | 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/guarded_map.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Defines our SymbolicGuardedMap class. 16 | 17 | #ifndef P4_SYMBOLIC_SYMBOLIC_GUARDED_MAP_H_ 18 | #define P4_SYMBOLIC_SYMBOLIC_GUARDED_MAP_H_ 19 | 20 | #include 21 | #include 22 | 23 | #include "absl/status/status.h" 24 | #include "google/protobuf/map.h" 25 | #include "gutil/status.h" 26 | #include "p4_symbolic/ir/ir.pb.h" 27 | #include "z3++.h" 28 | 29 | namespace p4_symbolic { 30 | namespace symbolic { 31 | 32 | // This class wraps around an internal std::unordered_map instance, 33 | // while enforcing the following: 34 | // 1. This class can only be instantiated with an instance of the IR 35 | // header definitions. The resulting instance will be initialized 36 | // to have exactly the same keys as the fields defined in those header 37 | // definitions. These keys are initially mapped to free symbolic variables, 38 | // with the same sort (including bitsize) described in the definitions. 39 | // 2. This class supports a const reference .Get(), which returns 40 | // an absl error if the key is not found in the map. 41 | // 3. This class allows mutation via .Set(, , ), which 42 | // sets the value of the key to z3::ite(, , ), 43 | // after checking that the sort of matches the sort of 44 | // modulo padding. 45 | // 46 | // As such, this class provides the following safety properties: 47 | // 1. Once initialized, the class has a fixed set of keys. 48 | // 2. A value mapped by a key always has the same sort. 49 | // 3. A value can only be assigned to a key given a guard. 50 | // 51 | class SymbolicGuardedMap { 52 | public: 53 | // Constructor requires passing the headers definition and will fill the map 54 | // with a free symbolic variable per header field. 55 | static gutil::StatusOr CreateSymbolicGuardedMap( 56 | const google::protobuf::Map &headers); 57 | 58 | // Explicitly copyable and movable! 59 | SymbolicGuardedMap(const SymbolicGuardedMap &other) = default; 60 | SymbolicGuardedMap(SymbolicGuardedMap &&other) = default; 61 | 62 | // Not assignable. 63 | SymbolicGuardedMap &operator=(const SymbolicGuardedMap &other) = delete; 64 | SymbolicGuardedMap &operator=(SymbolicGuardedMap &&other) = delete; 65 | 66 | // Getters. 67 | bool ContainsKey(const std::string &key) const; 68 | gutil::StatusOr Get(const std::string &key) const; 69 | 70 | // Guarded setter. 71 | // Returns an error if the assigned value has incompatible sort with the 72 | // pre-defined value. 73 | absl::Status Set(const std::string &key, const z3::expr &value, 74 | const z3::expr &guard); 75 | 76 | // Constant iterators. 77 | using const_iterator = 78 | std::unordered_map::const_iterator; 79 | const_iterator begin() const noexcept { return this->map_.cbegin(); } 80 | const_iterator end() const noexcept { return this->map_.cend(); } 81 | 82 | private: 83 | // The underlying map storing the keys and their values. 84 | std::unordered_map map_; 85 | 86 | // Private constructor used by factory. 87 | explicit SymbolicGuardedMap(std::unordered_map map) 88 | : map_(map) {} 89 | }; 90 | 91 | } // namespace symbolic 92 | } // namespace p4_symbolic 93 | 94 | #endif // P4_SYMBOLIC_SYMBOLIC_GUARDED_MAP_H_ 95 | -------------------------------------------------------------------------------- /p4-samples/vrf-routing/entries.pb.txt: -------------------------------------------------------------------------------- 1 | updates { 2 | type: INSERT, 3 | entity { 4 | table_entry { 5 | table_id: 37541331 6 | match { 7 | field_id: 1, 8 | ternary { 9 | value: "\000!\001\000", 10 | mask: "\011!\011\011" 11 | } 12 | } 13 | action { 14 | action { 15 | action_id: 26074559 16 | params { 17 | param_id: 1 18 | value: "VRF2" 19 | } 20 | } 21 | } 22 | priority: 2 23 | } 24 | } 25 | } 26 | updates { 27 | type: INSERT, 28 | entity { 29 | table_entry { 30 | table_id: 37541331 31 | match { 32 | field_id: 1, 33 | ternary { 34 | value: "!!\000\000", 35 | mask: "!!\011\011" 36 | } 37 | } 38 | action { 39 | action { 40 | action_id: 26074559 41 | params { 42 | param_id: 1 43 | value: "VRF1" 44 | } 45 | } 46 | } 47 | priority: 1 48 | } 49 | } 50 | } 51 | updates { 52 | type: INSERT, 53 | entity { 54 | table_entry { 55 | table_id: 44809600 56 | match { 57 | field_id: 1, 58 | lpm { 59 | value: "\n\n\000\000", 60 | prefix_len: 16 61 | } 62 | } 63 | match { 64 | field_id: 2, 65 | exact { 66 | value: "VRF1" 67 | } 68 | } 69 | action { 70 | action { 71 | action_id: 27807574 72 | params { 73 | param_id: 1 74 | value: "\000\000\000\000\000\000" 75 | } 76 | params { 77 | param_id: 2 78 | value: "\00" 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | updates { 86 | type: INSERT, 87 | entity { 88 | table_entry { 89 | table_id: 44809600 90 | match { 91 | field_id: 1, 92 | lpm { 93 | value: "\n\n\000\000", 94 | prefix_len: 32 95 | } 96 | } 97 | match { 98 | field_id: 2, 99 | exact { 100 | value: "VRF1" 101 | } 102 | } 103 | action { 104 | action { 105 | action_id: 27807574 106 | params { 107 | param_id: 1 108 | value: "\000\000\000\000\000\000" 109 | } 110 | params { 111 | param_id: 2 112 | value: "\01" 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | updates { 120 | type: INSERT, 121 | entity { 122 | table_entry { 123 | table_id: 44809600 124 | match { 125 | field_id: 1, 126 | lpm { 127 | value: "\n\000\000\000", 128 | prefix_len: 8 129 | } 130 | } 131 | match { 132 | field_id: 2, 133 | exact { 134 | value: "VRF1" 135 | } 136 | } 137 | action { 138 | action { 139 | action_id: 27807574 140 | params { 141 | param_id: 1 142 | value: "\000\000\000\000\000\n" 143 | } 144 | params { 145 | param_id: 2 146 | value: "\01" 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | updates { 154 | type: INSERT, 155 | entity { 156 | table_entry { 157 | table_id: 44809600 158 | match { 159 | field_id: 1, 160 | lpm { 161 | value: "\024\024\000\000", 162 | prefix_len: 16 163 | } 164 | } 165 | match { 166 | field_id: 2, 167 | exact { 168 | value: "VRF2" 169 | } 170 | } 171 | action { 172 | action { 173 | action_id: 27807574 174 | params { 175 | param_id: 1 176 | value: "\026\000\000\000\000\026" 177 | } 178 | params { 179 | param_id: 2 180 | value: "\01" 181 | } 182 | } 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /p4_symbolic/ir/expected/reflector.txt: -------------------------------------------------------------------------------- 1 | headers { 2 | key: "scalars" 3 | value { 4 | name: "scalars_0" 5 | } 6 | } 7 | headers { 8 | key: "standard_metadata" 9 | value { 10 | name: "standard_metadata" 11 | id: 1 12 | fields { 13 | key: "_padding" 14 | value { 15 | name: "_padding" 16 | bitwidth: 3 17 | } 18 | } 19 | fields { 20 | key: "checksum_error" 21 | value { 22 | name: "checksum_error" 23 | bitwidth: 1 24 | } 25 | } 26 | fields { 27 | key: "deq_qdepth" 28 | value { 29 | name: "deq_qdepth" 30 | bitwidth: 19 31 | } 32 | } 33 | fields { 34 | key: "deq_timedelta" 35 | value { 36 | name: "deq_timedelta" 37 | bitwidth: 32 38 | } 39 | } 40 | fields { 41 | key: "egress_global_timestamp" 42 | value { 43 | name: "egress_global_timestamp" 44 | bitwidth: 48 45 | } 46 | } 47 | fields { 48 | key: "egress_port" 49 | value { 50 | name: "egress_port" 51 | bitwidth: 9 52 | } 53 | } 54 | fields { 55 | key: "egress_rid" 56 | value { 57 | name: "egress_rid" 58 | bitwidth: 16 59 | } 60 | } 61 | fields { 62 | key: "egress_spec" 63 | value { 64 | name: "egress_spec" 65 | bitwidth: 9 66 | } 67 | } 68 | fields { 69 | key: "enq_qdepth" 70 | value { 71 | name: "enq_qdepth" 72 | bitwidth: 19 73 | } 74 | } 75 | fields { 76 | key: "enq_timestamp" 77 | value { 78 | name: "enq_timestamp" 79 | bitwidth: 32 80 | } 81 | } 82 | fields { 83 | key: "ingress_global_timestamp" 84 | value { 85 | name: "ingress_global_timestamp" 86 | bitwidth: 48 87 | } 88 | } 89 | fields { 90 | key: "ingress_port" 91 | value { 92 | name: "ingress_port" 93 | bitwidth: 9 94 | } 95 | } 96 | fields { 97 | key: "instance_type" 98 | value { 99 | name: "instance_type" 100 | bitwidth: 32 101 | } 102 | } 103 | fields { 104 | key: "mcast_grp" 105 | value { 106 | name: "mcast_grp" 107 | bitwidth: 16 108 | } 109 | } 110 | fields { 111 | key: "packet_length" 112 | value { 113 | name: "packet_length" 114 | bitwidth: 32 115 | } 116 | } 117 | fields { 118 | key: "parser_error" 119 | value { 120 | name: "parser_error" 121 | bitwidth: 32 122 | } 123 | } 124 | fields { 125 | key: "priority" 126 | value { 127 | name: "priority" 128 | bitwidth: 3 129 | } 130 | } 131 | } 132 | } 133 | actions { 134 | key: "reflector54" 135 | value { 136 | action_definition { 137 | preamble { 138 | name: "reflector54" 139 | } 140 | } 141 | action_implementation { 142 | action_body { 143 | source_info { 144 | filename: "p4-samples/reflector/reflector.p4" 145 | line: 54 146 | column: 8 147 | source_fragment: "standard_metadata.egress_spec = standard_metadata.ingress_port" 148 | } 149 | assignment { 150 | left { 151 | field_value { 152 | header_name: "standard_metadata" 153 | field_name: "egress_spec" 154 | } 155 | } 156 | right { 157 | field_value { 158 | header_name: "standard_metadata" 159 | field_name: "ingress_port" 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } 166 | } 167 | tables { 168 | key: "tbl_reflector54" 169 | value { 170 | table_definition { 171 | preamble { 172 | name: "tbl_reflector54" 173 | } 174 | } 175 | table_implementation { 176 | default_action: "reflector54" 177 | } 178 | } 179 | } 180 | initial_control: "tbl_reflector54" 181 | 182 | -------------------------------------------------------------------------------- /p4_symbolic/ir/BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_proto_library") 16 | load("@rules_proto//proto:defs.bzl", "proto_library") 17 | load("//p4_symbolic/ir:test.bzl", "ir_parsing_test") 18 | 19 | cc_proto_library( 20 | name = "ir_cc_proto", 21 | visibility = ["//p4_symbolic:__subpackages__"], 22 | deps = [":ir_proto"], 23 | ) 24 | 25 | proto_library( 26 | name = "ir_proto", 27 | srcs = ["ir.proto"], 28 | deps = [ 29 | "//p4_symbolic/bmv2:bmv2_proto", 30 | "@p4_pdpi//p4_pdpi:ir_proto", 31 | ], 32 | ) 33 | 34 | cc_library( 35 | name = "pdpi_driver", 36 | srcs = [ 37 | "pdpi_driver.cc", 38 | ], 39 | hdrs = [ 40 | "pdpi_driver.h", 41 | ], 42 | visibility = ["//p4_symbolic:__subpackages__"], 43 | deps = [ 44 | "@com_google_absl//absl/status", 45 | "@p4_pdpi//gutil:proto", 46 | "@p4_pdpi//gutil:status", 47 | "@p4_pdpi//p4_pdpi:ir", 48 | "@p4_pdpi//p4_pdpi:ir_cc_proto", 49 | ], 50 | ) 51 | 52 | cc_library( 53 | name = "table_entries", 54 | srcs = [ 55 | "table_entries.cc", 56 | ], 57 | hdrs = [ 58 | "table_entries.h", 59 | ], 60 | visibility = ["//p4_symbolic:__subpackages__"], 61 | deps = [ 62 | ":ir_cc_proto", 63 | "//p4_symbolic/util", 64 | "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", 65 | "@com_google_absl//absl/strings", 66 | "@p4_pdpi//gutil:proto", 67 | "@p4_pdpi//gutil:status", 68 | "@p4_pdpi//p4_pdpi:ir", 69 | "@p4_pdpi//p4_pdpi:ir_cc_proto", 70 | ], 71 | ) 72 | 73 | cc_library( 74 | name = "ir", 75 | srcs = [ 76 | "ir.cc", 77 | ], 78 | hdrs = [ 79 | "ir.h", 80 | ], 81 | visibility = ["//p4_symbolic:__subpackages__"], 82 | deps = [ 83 | ":ir_cc_proto", 84 | ":table_entries", 85 | "//p4_symbolic/bmv2:bmv2_cc_proto", 86 | "@com_github_p4lang_p4runtime//:p4runtime_cc_proto", 87 | "@com_google_absl//absl/strings", 88 | "@com_google_protobuf//:protobuf_headers", 89 | "@p4_pdpi//gutil:status", 90 | "@p4_pdpi//p4_pdpi:ir_cc_proto", 91 | ], 92 | ) 93 | 94 | cc_binary( 95 | name = "test", 96 | srcs = [ 97 | "test.cc", 98 | ], 99 | deps = [ 100 | "//p4_symbolic:parser", 101 | "@com_google_absl//absl/flags:flag", 102 | "@com_google_absl//absl/flags:parse", 103 | "@com_google_absl//absl/flags:usage", 104 | "@com_google_absl//absl/status", 105 | "@com_google_absl//absl/strings", 106 | "@p4_pdpi//gutil:status", 107 | ], 108 | ) 109 | 110 | # Golden file testing rules to test p4_symbolic/ir/ir.cc. 111 | ir_parsing_test( 112 | name = "port_table_test", 113 | golden_file = "expected/table.txt", 114 | p4_program = "//p4-samples:port-table/table.p4", 115 | table_entries = "//p4-samples:port-table/entries.pb.txt", 116 | ) 117 | 118 | ir_parsing_test( 119 | name = "port_hardcoded_test", 120 | golden_file = "expected/hardcoded.txt", 121 | p4_program = "//p4-samples:hardcoded/hardcoded.p4", 122 | ) 123 | 124 | ir_parsing_test( 125 | name = "reflector_test", 126 | golden_file = "expected/reflector.txt", 127 | p4_program = "//p4-samples:reflector/reflector.p4", 128 | ) 129 | 130 | ir_parsing_test( 131 | name = "ipv4_routing_test", 132 | golden_file = "expected/basic.txt", 133 | p4_program = "//p4-samples:ipv4-routing/basic.p4", 134 | table_entries = "//p4-samples:ipv4-routing/entries.pb.txt", 135 | ) 136 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/expected/table.smt2: -------------------------------------------------------------------------------- 1 | ; 2 | (set-info :status unknown) 3 | (declare-fun standard_metadata.ingress_port () (_ BitVec 9)) 4 | (declare-fun standard_metadata.egress_spec () (_ BitVec 9)) 5 | (assert 6 | (let (($x61 (= standard_metadata.ingress_port (_ bv1 9)))) 7 | (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x61))) 8 | (assert 9 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 10 | (let (($x40 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) 11 | (let (($x43 (and true (not (and true (= standard_metadata.ingress_port ?x34)))))) 12 | (let ((?x38 (concat (_ bv0 8) (_ bv1 1)))) 13 | (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) 14 | (let (($x41 (and true $x36))) 15 | (let ((?x55 (ite $x41 ?x38 (ite (and $x43 $x40) ?x34 standard_metadata.egress_spec)))) 16 | (or (or (= ?x55 (_ bv455 9)) (= ?x55 (_ bv0 9))) (= ?x55 (_ bv1 9))))))))))) 17 | (assert 18 | (let (($x40 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) 19 | (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) 20 | (let (($x41 (and true $x36))) 21 | (let ((?x54 (ite $x41 0 (ite (and true $x40) 1 (- 1))))) 22 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 23 | (let ((?x52 (ite (and (and true (not $x36)) $x40) ?x34 standard_metadata.egress_spec))) 24 | (let ((?x38 (concat (_ bv0 8) (_ bv1 1)))) 25 | (let ((?x55 (ite $x41 ?x38 ?x52))) 26 | (let (($x46 (= ?x55 (_ bv455 9)))) 27 | (and (and (not $x46) true) (= ?x54 (- 1))))))))))))) 28 | (check-sat) 29 | 30 | ; 31 | (set-info :status unknown) 32 | (declare-fun standard_metadata.ingress_port () (_ BitVec 9)) 33 | (declare-fun standard_metadata.egress_spec () (_ BitVec 9)) 34 | (assert 35 | (let (($x61 (= standard_metadata.ingress_port (_ bv1 9)))) 36 | (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x61))) 37 | (assert 38 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 39 | (let (($x40 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) 40 | (let (($x43 (and true (not (and true (= standard_metadata.ingress_port ?x34)))))) 41 | (let ((?x38 (concat (_ bv0 8) (_ bv1 1)))) 42 | (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) 43 | (let (($x41 (and true $x36))) 44 | (let ((?x55 (ite $x41 ?x38 (ite (and $x43 $x40) ?x34 standard_metadata.egress_spec)))) 45 | (or (or (= ?x55 (_ bv455 9)) (= ?x55 (_ bv0 9))) (= ?x55 (_ bv1 9))))))))))) 46 | (assert 47 | (let (($x40 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) 48 | (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) 49 | (let (($x41 (and true $x36))) 50 | (let ((?x54 (ite $x41 0 (ite (and true $x40) 1 (- 1))))) 51 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 52 | (let ((?x52 (ite (and (and true (not $x36)) $x40) ?x34 standard_metadata.egress_spec))) 53 | (let ((?x38 (concat (_ bv0 8) (_ bv1 1)))) 54 | (let ((?x55 (ite $x41 ?x38 ?x52))) 55 | (let (($x46 (= ?x55 (_ bv455 9)))) 56 | (let (($x114 (and (not $x46) true))) 57 | (and $x114 (= ?x54 0))))))))))))) 58 | (check-sat) 59 | 60 | ; 61 | (set-info :status unknown) 62 | (declare-fun standard_metadata.ingress_port () (_ BitVec 9)) 63 | (declare-fun standard_metadata.egress_spec () (_ BitVec 9)) 64 | (assert 65 | (let (($x61 (= standard_metadata.ingress_port (_ bv1 9)))) 66 | (or (or false (= standard_metadata.ingress_port (_ bv0 9))) $x61))) 67 | (assert 68 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 69 | (let (($x40 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) 70 | (let (($x43 (and true (not (and true (= standard_metadata.ingress_port ?x34)))))) 71 | (let ((?x38 (concat (_ bv0 8) (_ bv1 1)))) 72 | (let (($x36 (and true (= standard_metadata.ingress_port ?x34)))) 73 | (let (($x41 (and true $x36))) 74 | (let ((?x55 (ite $x41 ?x38 (ite (and $x43 $x40) ?x34 standard_metadata.egress_spec)))) 75 | (or (or (= ?x55 (_ bv455 9)) (= ?x55 (_ bv0 9))) (= ?x55 (_ bv1 9))))))))))) 76 | (assert 77 | (let (($x40 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv1 1)))))) 78 | (let (($x36 (and true (= standard_metadata.ingress_port (concat (_ bv0 8) (_ bv0 1)))))) 79 | (let (($x41 (and true $x36))) 80 | (let ((?x54 (ite $x41 0 (ite (and true $x40) 1 (- 1))))) 81 | (let ((?x34 (concat (_ bv0 8) (_ bv0 1)))) 82 | (let ((?x52 (ite (and (and true (not $x36)) $x40) ?x34 standard_metadata.egress_spec))) 83 | (let ((?x38 (concat (_ bv0 8) (_ bv1 1)))) 84 | (let ((?x55 (ite $x41 ?x38 ?x52))) 85 | (let (($x46 (= ?x55 (_ bv455 9)))) 86 | (and (and (not $x46) true) (= ?x54 1)))))))))))) 87 | (check-sat) 88 | 89 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/test.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # This file contains rule definitions for running 16 | # the test binary to produce output json and protobuf files, 17 | # subset diff the input and output json files, and golden file 18 | # testing the output protobuf files against the expected files. 19 | # It also defines a Macro that simplifies defining a protobuf test 20 | # over a sample P4 program. 21 | 22 | load("//:p4c.bzl", "run_p4c") 23 | load("//p4_symbolic/bmv2:test.bzl", "exact_diff_test") 24 | 25 | # Macro that defines our end to end tests. 26 | # Given a p4 program, this macro runs our main binary 27 | # on the p4 program and its table entries (if they exist). 28 | # The binary outputs a debugging dump of the underlying smt program, 29 | # as well as the output test packets. 30 | # The macro compares both of these outputs against the provided 31 | # expected golden files. 32 | # The macro defines these rules in order: 33 | # 1. A rule for producing bmv2 json and p4info files from a .p4 file using p4c. 34 | # 2. A rule for running the main binary on the inputs using p4_symbolic/main.cc, 35 | # and dumping both output files. 36 | # 3. Two rules for golden file testing of the two output files. 37 | # 4. A test suite combining the two rules above with the same name 38 | # as given to the macro. 39 | # Use the p4_deps list to specify dependent files that p4_program input 40 | # file depends on (e.g. by including them). 41 | def end_to_end_test( 42 | name, 43 | p4_program, 44 | output_golden_file, 45 | smt_golden_file, 46 | table_entries = None, 47 | p4_deps = []): 48 | p4c_name = "%s_p4c" % name 49 | run_name = "%s_main" % name 50 | p4info_file = "%s_bazel-p4c-tmp-output/p4info.pb.txt" % p4c_name 51 | output_test_name = "%s_output" % name 52 | smt_test_name = "%s_smt" % name 53 | 54 | optional_table_entries = [] 55 | optional_table_entry_arg = "" 56 | if table_entries: 57 | optional_table_entries = [table_entries] 58 | optional_table_entry_arg = "--entries=$(location %s)" % table_entries 59 | 60 | # Run p4c to get bmv2 JSON and p4info.pb.txt files. 61 | run_p4c( 62 | name = p4c_name, 63 | src = p4_program, 64 | deps = p4_deps, 65 | p4runtime_files = [p4info_file], 66 | ) 67 | 68 | # Use p4_symbolic/main.cc to run our tool on the p4 program 69 | # and produce a debugging smt file and an output file with 70 | # interesting testing packets. 71 | output_filename = name + ".txt" 72 | output_smt_filename = name + ".smt2" 73 | native.genrule( 74 | name = run_name, 75 | srcs = [":" + p4c_name, p4info_file] + optional_table_entries, 76 | outs = [output_filename, output_smt_filename], 77 | tools = ["//p4_symbolic:main"], 78 | cmd = ( 79 | "$(location //p4_symbolic:main) --bmv2=$(location %s) " + 80 | "--p4info=$(location %s) %s --debug=$(location %s) " + 81 | "--hardcoded_parser=false &> $(location %s)" 82 | ) % ( 83 | ":" + p4c_name, 84 | p4info_file, 85 | optional_table_entry_arg, 86 | output_smt_filename, 87 | output_filename, 88 | ), 89 | ) 90 | 91 | # Exact diff test for the packet output file. 92 | exact_diff_test( 93 | name = output_test_name, 94 | actual = output_filename, 95 | expected = output_golden_file, 96 | ) 97 | 98 | # Exact diff test for the smt output file. 99 | exact_diff_test( 100 | name = smt_test_name, 101 | actual = output_smt_filename, 102 | expected = smt_golden_file, 103 | ) 104 | 105 | # Group tests into a test_suite with the given name. 106 | # This is just to make the provided name alias to something. 107 | native.test_suite( 108 | name = name, 109 | tests = [ 110 | ":" + output_test_name, 111 | ":" + smt_test_name, 112 | ], 113 | ) 114 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/action.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Contains functions used to symbolically evaluate actions and their bodies. 16 | // An action is represented as a boolean symbolic z3 expression over 17 | // unconstrained symbolic parameters corresponding to its actual P4 parameters. 18 | 19 | #ifndef P4_SYMBOLIC_SYMBOLIC_ACTION_H_ 20 | #define P4_SYMBOLIC_SYMBOLIC_ACTION_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "google/protobuf/repeated_field.h" 27 | #include "gutil/status.h" 28 | #include "p4_pdpi/ir.pb.h" 29 | #include "p4_symbolic/ir/ir.pb.h" 30 | #include "p4_symbolic/symbolic/symbolic.h" 31 | #include "p4_symbolic/symbolic/values.h" 32 | #include "z3++.h" 33 | 34 | namespace p4_symbolic { 35 | namespace symbolic { 36 | namespace action { 37 | 38 | // Symbolically evaluates the given action on the given symbolic parameters. 39 | // This produces a symbolic expression on the symbolic parameters that is 40 | // semantically equivalent to the behavior of the action on its concrete 41 | // parameters. 42 | absl::Status EvaluateAction(const ir::Action &action, 43 | const google::protobuf::RepeatedPtrField< 44 | pdpi::IrActionInvocation::IrActionParam> &args, 45 | SymbolicPerPacketState *state, 46 | values::P4RuntimeTranslator *translator, 47 | const z3::expr &guard); 48 | 49 | // Internal functions used to Evaluate statements and expressions within an 50 | // action body. These are internal functions not used beyond this header and its 51 | // associated source file. 52 | 53 | // The scope of this action: maps local variable names to their symbolic values. 54 | struct ActionContext { 55 | std::string action_name; 56 | std::unordered_map scope; 57 | }; 58 | 59 | // Performs a switch case over support statement types and call the 60 | // appropriate function. 61 | absl::Status EvaluateStatement(const ir::Statement &statement, 62 | SymbolicPerPacketState *state, 63 | ActionContext *context, const z3::expr &guard); 64 | 65 | // Constructs a symbolic expression for the assignment value, and either 66 | // constrains it in an enclosing assignment expression, or stores it in 67 | // the action scope. 68 | absl::Status EvaluateAssignmentStatement( 69 | const ir::AssignmentStatement &assignment, SymbolicPerPacketState *state, 70 | ActionContext *context, const z3::expr &guard); 71 | 72 | // Constructs a symbolic expression corresponding to this value, according 73 | // to its type. 74 | gutil::StatusOr EvaluateRValue(const ir::RValue &rvalue, 75 | const SymbolicPerPacketState &state, 76 | const ActionContext &context); 77 | 78 | // Extract the field symbolic value from the symbolic state. 79 | gutil::StatusOr EvaluateFieldValue( 80 | const ir::FieldValue &field_value, const SymbolicPerPacketState &state, 81 | const ActionContext &context); 82 | 83 | // Parse and format literal values as symbolic expression. 84 | gutil::StatusOr EvaluateHexStr(const ir::HexstrValue &hexstr); 85 | 86 | gutil::StatusOr EvaluateBool(const ir::BoolValue &bool_value); 87 | 88 | gutil::StatusOr EvaluateString(const ir::StringValue &string_value); 89 | 90 | // Looks up the symbolic value of the variable in the action scope. 91 | gutil::StatusOr EvaluateVariable(const ir::Variable &variable, 92 | const ActionContext &context); 93 | 94 | // Evaluate expression by recursively evaluating operands and applying the 95 | // symbolic version of the operator to them. 96 | gutil::StatusOr EvaluateRExpression( 97 | const ir::RExpression &expr, const SymbolicPerPacketState &state, 98 | const ActionContext &context); 99 | 100 | } // namespace action 101 | } // namespace symbolic 102 | } // namespace p4_symbolic 103 | 104 | #endif // P4_SYMBOLIC_SYMBOLIC_ACTION_H_ 105 | -------------------------------------------------------------------------------- /p4-samples/vrf-routing/vrf.p4: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | /* -*- P4_16 -*- */ 16 | #include 17 | #include 18 | 19 | const bit<16> TYPE_IPV4 = 0x800; 20 | 21 | typedef bit<9> egress_spec_t; 22 | typedef bit<48> mac_addr_t; 23 | typedef bit<32> ipv4_addr_t; 24 | 25 | @p4runtime_translation("", string) 26 | type bit<10> vrf_t; 27 | 28 | header ethernet_t { 29 | mac_addr_t dstAddr; 30 | mac_addr_t srcAddr; 31 | bit<16> eth_type; 32 | } 33 | header ipv4_t { 34 | bit<4> version; 35 | bit<4> ihl; 36 | bit<8> diffserv; 37 | bit<16> totalLen; 38 | bit<16> identification; 39 | bit<3> flags; 40 | bit<13> fragOffset; 41 | bit<8> ttl; 42 | bit<8> protocol; 43 | bit<16> hdrChecksum; 44 | ipv4_addr_t srcAddr; 45 | ipv4_addr_t dstAddr; 46 | } 47 | struct headers { 48 | ethernet_t ethernet; 49 | ipv4_t ipv4; 50 | } 51 | struct local_metadata_t { 52 | vrf_t vrf; 53 | bool vrf_is_valid; 54 | } 55 | 56 | parser packet_parser(packet_in packet, out headers hdr, 57 | inout local_metadata_t local_metadata, 58 | inout standard_metadata_t standard_metadata) { 59 | state start { 60 | transition parse_ethernet; 61 | } 62 | 63 | state parse_ethernet { 64 | packet.extract(hdr.ethernet); 65 | transition select(hdr.ethernet.eth_type) { 66 | TYPE_IPV4: parse_ipv4; 67 | default: accept; 68 | } 69 | } 70 | 71 | state parse_ipv4 { 72 | packet.extract(hdr.ipv4); 73 | transition accept; 74 | } 75 | } 76 | 77 | // Ingress processing: 78 | // First set vrf by matching on ipv4.srcAddr, 79 | // then match on vrf and ipv4.dstAddr to route. 80 | control packet_ingress(inout headers hdr, 81 | inout local_metadata_t local_metadata, 82 | inout standard_metadata_t standard_metadata) { 83 | // NoAction: 21257015 84 | 85 | // 26764252 86 | action drop() { 87 | mark_to_drop(standard_metadata); 88 | } 89 | 90 | // 26074559 91 | action set_vrf(vrf_t vrf) { 92 | local_metadata.vrf = vrf; 93 | local_metadata.vrf_is_valid = true; 94 | } 95 | 96 | // 27807574 97 | action ipv4_forward(mac_addr_t dstAddr, egress_spec_t port) { 98 | standard_metadata.egress_spec = port; 99 | hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; 100 | hdr.ethernet.dstAddr = dstAddr; 101 | hdr.ipv4.ttl = hdr.ipv4.ttl - 1; 102 | } 103 | 104 | // 37541331 105 | table set_vrf_table { 106 | key = { 107 | hdr.ipv4.srcAddr: ternary @format(IPV4_ADDRESS); 108 | } 109 | actions = { 110 | @proto_id(1) set_vrf; 111 | @proto_id(2) NoAction; 112 | } 113 | size = 1024; 114 | default_action = NoAction; 115 | } 116 | 117 | // 44809600 118 | table ipv4_lpm_table { 119 | key = { 120 | hdr.ipv4.dstAddr: lpm @format(IPV4_ADDRESS); 121 | local_metadata.vrf: exact; 122 | } 123 | actions = { 124 | @proto_id(1) ipv4_forward; 125 | @proto_id(2) drop; 126 | } 127 | size = 1024; 128 | default_action = drop(); 129 | } 130 | 131 | apply { 132 | // vrf is not valid by default. 133 | local_metadata.vrf_is_valid = false; 134 | 135 | // Check that the packet is an ipv4 packet. 136 | if (hdr.ipv4.isValid()) { 137 | // Set a vrf. 138 | set_vrf_table.apply(); 139 | if (local_metadata.vrf_is_valid) { 140 | // If vrf was set, do lpm matching on dst to route. 141 | ipv4_lpm_table.apply(); 142 | } 143 | } 144 | } 145 | } 146 | 147 | control empty_deparser(packet_out packet, in headers hdr) { 148 | apply { 149 | packet.emit(hdr.ethernet); 150 | packet.emit(hdr.ipv4); 151 | } 152 | } 153 | 154 | control empty_egress(inout headers hdr, inout local_metadata_t local_metadata, 155 | inout standard_metadata_t standard_metadata) { 156 | apply {} 157 | } 158 | 159 | 160 | control empty_compute_checksum(inout headers hdr, 161 | inout local_metadata_t local_metadata) { 162 | apply {} 163 | } 164 | 165 | control empty_verify_checksum(inout headers hdr, 166 | inout local_metadata_t local_metadata) { 167 | apply {} 168 | } 169 | 170 | V1Switch( 171 | packet_parser(), 172 | empty_verify_checksum(), 173 | packet_ingress(), 174 | empty_egress(), 175 | empty_compute_checksum(), 176 | empty_deparser() 177 | ) main; 178 | -------------------------------------------------------------------------------- /z3-samples/paths/paths.opt2.smt2: -------------------------------------------------------------------------------- 1 | ;; Copyright 2020 Google LLC 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 | ;; Packet attributes 16 | (declare-const ingress_port Int) 17 | (declare-const dstAddr (_ BitVec 32)) 18 | 19 | ;; Table output parameters 20 | (declare-const vrf Int) 21 | 22 | ;; Final output attributes 23 | (declare-const egress_spec Int) 24 | 25 | ;; Table Matches 26 | ;; port_to_vrf 27 | (define-fun port_to_vrf0 () Bool (= ingress_port 0)) 28 | (define-fun port_to_vrf1 () Bool (= ingress_port 1)) 29 | 30 | ;; vrf_ip_to_port 31 | (define-fun vrf_ip_to_port0_lpm () Bool (= ((_ extract 31 8) dstAddr) #x0a0a00)) ;; 10.10.0.* 32 | (define-fun vrf_ip_to_port1_lpm () Bool (= ((_ extract 31 16) dstAddr) #x0a0a)) ;; 10.10.*.* 33 | (define-fun vrf_ip_to_port2_lpm () Bool (= ((_ extract 31 16) dstAddr) #x1414)) ;; 20.20.*.* 34 | 35 | (define-fun vrf_ip_to_port0 () Bool (and 36 | vrf_ip_to_port0_lpm 37 | (= vrf 10) 38 | )) 39 | (define-fun vrf_ip_to_port1 () Bool (and 40 | (and (not vrf_ip_to_port0_lpm) vrf_ip_to_port1_lpm) 41 | (= vrf 10) 42 | )) 43 | (define-fun vrf_ip_to_port2 () Bool (and 44 | vrf_ip_to_port2_lpm 45 | (= vrf 20) 46 | )) 47 | 48 | ;; Branches 49 | (define-fun set_port_if1_then () Bool (= dstAddr #x0a0a0000)) 50 | (define-fun set_port_if1_else () Bool (not (= dstAddr #x0a0a0000))) 51 | 52 | ;; link vrf value to table match 53 | ;; 54 | (assert (=> port_to_vrf0 (= vrf 10))) 55 | (assert (=> port_to_vrf1 (= vrf 20))) 56 | (assert (=> (and (not port_to_vrf0) (not port_to_vrf1)) (= vrf -1))) 57 | 58 | ;; link output values to their paths/traces 59 | (assert (=> set_port_if1_then (= egress_spec 0))) 60 | (assert (=> (and set_port_if1_else vrf_ip_to_port0) (= egress_spec 1))) 61 | (assert (=> (and set_port_if1_else vrf_ip_to_port1) (= egress_spec 2))) 62 | (assert (=> (and set_port_if1_else vrf_ip_to_port2) (= egress_spec 3))) 63 | (assert (=> 64 | (and (not vrf_ip_to_port0) (not vrf_ip_to_port1) (not vrf_ip_to_port2)) 65 | (= egress_spec -1) 66 | )) 67 | 68 | ;; what does not being dropped mean in this program 69 | (define-fun not_dropped () Bool (not (= egress_spec -1))) 70 | 71 | ;; Optimization/Idea to consider: 72 | ;; perhaps setting vrf and egress_spec to -1 is not needed as an assert 73 | ;; but only as part of the not_dropped or other similar aliases? 74 | ;; this means that the smt solver will not need to assert it for 75 | ;; complete queries, which will be valid by definition, and only 76 | ;; assert them (and have to do more work) for some of the partial 77 | ;; queries. 78 | 79 | ;; Combinations 80 | ;; 81 | ;; Our code can ignore combinations that are obviously unsat 82 | ;; for example, table matches that are contradictory 83 | ;; if statements are probably harder. 84 | ;; 85 | ;; Another idea is to invoke the solver incremently 86 | ;; if a prefix of a combination is unsat, all additions to it are also unsat 87 | ;; and can be ignored. 88 | ;; 89 | (push) 90 | (echo "combination 1") 91 | (assert (and port_to_vrf0 vrf_ip_to_port0 set_port_if1_then)) 92 | (check-sat) 93 | (get-model) 94 | (echo "") 95 | (pop) 96 | 97 | (push) 98 | (echo "combination 2") 99 | (assert (and port_to_vrf0 vrf_ip_to_port0 set_port_if1_else)) 100 | (check-sat) 101 | (get-model) 102 | (echo "") 103 | (pop) 104 | 105 | (push) 106 | (echo "combination 3") 107 | (assert (and port_to_vrf0 vrf_ip_to_port1 set_port_if1_then)) 108 | (check-sat) 109 | (get-model) 110 | (echo "") 111 | (pop) 112 | 113 | (push) 114 | (echo "combination 4") 115 | (assert (and port_to_vrf0 vrf_ip_to_port1 set_port_if1_else)) 116 | (check-sat) 117 | (get-model) 118 | (echo "") 119 | (pop) 120 | 121 | (push) 122 | (echo "combination 5") 123 | (assert (and port_to_vrf1 vrf_ip_to_port2 set_port_if1_then)) 124 | (check-sat) 125 | (get-model) 126 | (echo "") 127 | (pop) 128 | 129 | (push) 130 | (echo "combination 6") 131 | (assert (and port_to_vrf1 vrf_ip_to_port2 set_port_if1_else)) 132 | (check-sat) 133 | (get-model) 134 | (echo "") 135 | (pop) 136 | 137 | 138 | ;; Alternatively, you can have partial queries 139 | ;; For example: 140 | ;; Give me any valid packet 141 | ;; 142 | (push) 143 | (echo "Any valid packet") 144 | (assert not_dropped) 145 | (check-sat) 146 | (get-model) 147 | (echo "") 148 | (pop) 149 | 150 | ;; Give me a packet that will have output port 3 151 | (push) 152 | (echo "output = 3") 153 | (assert (= egress_spec 3)) 154 | (check-sat) 155 | (get-model) 156 | (echo "") 157 | (pop) 158 | 159 | 160 | ;; Give me a packet with output port 2 and vrf 10 161 | (push) 162 | (echo "output = 2 and vrf = 10") 163 | (assert (and (= egress_spec 2) (= vrf 10))) 164 | (check-sat) 165 | (get-model) 166 | (echo "") 167 | (pop) 168 | 169 | ;; Partial Trace plus output port is 1 170 | (push) 171 | (echo "output = 1 and port to vrf entry 0 is hit") 172 | (assert (and (= egress_spec 1) port_to_vrf0)) 173 | (check-sat) 174 | (get-model) 175 | (echo "") 176 | (pop) 177 | -------------------------------------------------------------------------------- /p4c.bzl: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | # This file defines a rule for running p4c and producing bmv2 json files. 16 | 17 | def _run_p4c_impl(ctx): 18 | # Figure out the outpt file extension. 19 | # If provided explicitly, use that. 20 | # Otherwise, if the target is bmv2, the extension must be .json 21 | extension = ctx.attr.extension 22 | if extension == "": 23 | # Add other extensions here as needed. 24 | if ctx.attr.target == "bmv2": 25 | extension = ".json" 26 | else: 27 | fail("Extension is not provided for unknown target %s" % 28 | ctx.attr.target) 29 | 30 | # The output file path (relative to the directory of the src input file). 31 | fname = "".join([ 32 | ctx.attr.name, 33 | "-bazel-p4c-tmp-output/", 34 | # Base name without the .p4 extension. 35 | ctx.file.src.basename[:-3], 36 | extension, 37 | ]) 38 | 39 | # Declare the output file. 40 | output_file = ctx.actions.declare_file(fname) 41 | 42 | # Get the full path (relative to root of sandbox) of the directory of 43 | # the output file. 44 | # This is needed because p4c expects a directory to be passed with "-o". 45 | # P4c will put the output file in that directory. 46 | output_dir_path = output_file.path[:-len(output_file.basename) - 1] 47 | 48 | # Validate extensions of p4info files and build their cmd arg for p4c. 49 | p4info_arg = "" 50 | if len(ctx.outputs.p4runtime_files) > 0: 51 | paths = [] 52 | for f in ctx.outputs.p4runtime_files: 53 | if not (f.path.endswith(".txt") or f.path.endswith(".json") or 54 | f.path.endswith(".bin")): 55 | fail("p4runtime-file can only end with one of '.txt', " + 56 | "'.json', or '.bin'", "p4runtime_files") 57 | paths.append(f.path) 58 | p4info_arg = "--p4runtime-files " + ",".join(paths) 59 | 60 | # Run p4c. 61 | ctx.actions.run_shell( 62 | inputs = [ctx.file.src] + ctx.files.deps, 63 | outputs = [output_file] + ctx.outputs.p4runtime_files, 64 | use_default_shell_env = True, 65 | command = "p4c --std $1 --target $2 --arch $3 -o $4 $5 $6 $7", 66 | arguments = [ 67 | ctx.attr.std, 68 | ctx.attr.target, 69 | ctx.attr.arch, 70 | output_dir_path, 71 | p4info_arg, 72 | ctx.attr.p4c_args, 73 | ctx.file.src.path, 74 | ], 75 | ) 76 | 77 | return [ 78 | DefaultInfo(files = depset([output_file])), 79 | OutputGroupInfo(p4info = ctx.outputs.p4runtime_files), 80 | ] 81 | 82 | run_p4c = rule( 83 | doc = "Runs p4c to produce output files according to given params.", 84 | implementation = _run_p4c_impl, 85 | attrs = { 86 | "src": attr.label( 87 | doc = "Input .p4 files to pass to p4c.", 88 | mandatory = True, 89 | allow_single_file = [".p4"], 90 | ), 91 | "deps": attr.label_list( 92 | doc = "Other dependent files/labels. Use for included p4 files.", 93 | mandatory = False, 94 | allow_empty = True, 95 | allow_files = [".p4"], 96 | default = [], 97 | ), 98 | "target": attr.string( 99 | doc = "The --target argument passed to p4c (default: bmv2).", 100 | mandatory = False, 101 | default = "bmv2", 102 | ), 103 | "arch": attr.string( 104 | doc = "The --arch argument passed to p4v (default: v1model).", 105 | mandatory = False, 106 | default = "v1model", 107 | ), 108 | "std": attr.string( 109 | doc = "The --std argument passed to p4v (default: p4_16).", 110 | mandatory = False, 111 | default = "p4_16", 112 | ), 113 | "p4c_args": attr.string( 114 | doc = "Any additional command line arguments to pass to p4c.", 115 | mandatory = False, 116 | default = "", 117 | ), 118 | "extension": attr.string( 119 | doc = """ 120 | The expected extension of the ouput file, depending on 121 | the architecture. 122 | If the extension is not provided, the rule will attempt to 123 | figure this out by looking at the target attribute, and will 124 | throw an Error if it was not able to determine it. 125 | """, 126 | mandatory = False, 127 | default = "", 128 | ), 129 | "p4runtime_files": attr.output_list( 130 | doc = """ 131 | File(s) path(s) to write the control plane 132 | API description (P4Info) to. Acceptable extensions 133 | are ".txt", ".json", and ".bin". 134 | """, 135 | mandatory = False, 136 | allow_empty = True, 137 | ), 138 | }, 139 | ) 140 | -------------------------------------------------------------------------------- /p4_symbolic/ir/expected/hardcoded.txt: -------------------------------------------------------------------------------- 1 | headers { 2 | key: "scalars" 3 | value { 4 | name: "scalars_0" 5 | } 6 | } 7 | headers { 8 | key: "standard_metadata" 9 | value { 10 | name: "standard_metadata" 11 | id: 1 12 | fields { 13 | key: "_padding" 14 | value { 15 | name: "_padding" 16 | bitwidth: 3 17 | } 18 | } 19 | fields { 20 | key: "checksum_error" 21 | value { 22 | name: "checksum_error" 23 | bitwidth: 1 24 | } 25 | } 26 | fields { 27 | key: "deq_qdepth" 28 | value { 29 | name: "deq_qdepth" 30 | bitwidth: 19 31 | } 32 | } 33 | fields { 34 | key: "deq_timedelta" 35 | value { 36 | name: "deq_timedelta" 37 | bitwidth: 32 38 | } 39 | } 40 | fields { 41 | key: "egress_global_timestamp" 42 | value { 43 | name: "egress_global_timestamp" 44 | bitwidth: 48 45 | } 46 | } 47 | fields { 48 | key: "egress_port" 49 | value { 50 | name: "egress_port" 51 | bitwidth: 9 52 | } 53 | } 54 | fields { 55 | key: "egress_rid" 56 | value { 57 | name: "egress_rid" 58 | bitwidth: 16 59 | } 60 | } 61 | fields { 62 | key: "egress_spec" 63 | value { 64 | name: "egress_spec" 65 | bitwidth: 9 66 | } 67 | } 68 | fields { 69 | key: "enq_qdepth" 70 | value { 71 | name: "enq_qdepth" 72 | bitwidth: 19 73 | } 74 | } 75 | fields { 76 | key: "enq_timestamp" 77 | value { 78 | name: "enq_timestamp" 79 | bitwidth: 32 80 | } 81 | } 82 | fields { 83 | key: "ingress_global_timestamp" 84 | value { 85 | name: "ingress_global_timestamp" 86 | bitwidth: 48 87 | } 88 | } 89 | fields { 90 | key: "ingress_port" 91 | value { 92 | name: "ingress_port" 93 | bitwidth: 9 94 | } 95 | } 96 | fields { 97 | key: "instance_type" 98 | value { 99 | name: "instance_type" 100 | bitwidth: 32 101 | } 102 | } 103 | fields { 104 | key: "mcast_grp" 105 | value { 106 | name: "mcast_grp" 107 | bitwidth: 16 108 | } 109 | } 110 | fields { 111 | key: "packet_length" 112 | value { 113 | name: "packet_length" 114 | bitwidth: 32 115 | } 116 | } 117 | fields { 118 | key: "parser_error" 119 | value { 120 | name: "parser_error" 121 | bitwidth: 32 122 | } 123 | } 124 | fields { 125 | key: "priority" 126 | value { 127 | name: "priority" 128 | bitwidth: 3 129 | } 130 | } 131 | } 132 | } 133 | actions { 134 | key: "hardcoded55" 135 | value { 136 | action_definition { 137 | preamble { 138 | name: "hardcoded55" 139 | } 140 | } 141 | action_implementation { 142 | action_body { 143 | source_info { 144 | filename: "p4-samples/hardcoded/hardcoded.p4" 145 | line: 55 146 | column: 12 147 | source_fragment: "standard_metadata.egress_spec = 1" 148 | } 149 | assignment { 150 | left { 151 | field_value { 152 | header_name: "standard_metadata" 153 | field_name: "egress_spec" 154 | } 155 | } 156 | right { 157 | hexstr_value { 158 | value: "0x0001" 159 | } 160 | } 161 | } 162 | } 163 | } 164 | } 165 | } 166 | actions { 167 | key: "hardcoded57" 168 | value { 169 | action_definition { 170 | preamble { 171 | id: 1 172 | name: "hardcoded57" 173 | } 174 | } 175 | action_implementation { 176 | action_body { 177 | source_info { 178 | filename: "p4-samples/hardcoded/hardcoded.p4" 179 | line: 57 180 | column: 12 181 | source_fragment: "standard_metadata.egress_spec = 0" 182 | } 183 | assignment { 184 | left { 185 | field_value { 186 | header_name: "standard_metadata" 187 | field_name: "egress_spec" 188 | } 189 | } 190 | right { 191 | hexstr_value { 192 | value: "0x0000" 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | } 200 | tables { 201 | key: "tbl_hardcoded55" 202 | value { 203 | table_definition { 204 | preamble { 205 | name: "tbl_hardcoded55" 206 | } 207 | } 208 | table_implementation { 209 | default_action: "hardcoded55" 210 | } 211 | } 212 | } 213 | tables { 214 | key: "tbl_hardcoded57" 215 | value { 216 | table_definition { 217 | preamble { 218 | name: "tbl_hardcoded57" 219 | } 220 | } 221 | table_implementation { 222 | default_action: "hardcoded57" 223 | } 224 | } 225 | } 226 | conditionals { 227 | key: "node_2" 228 | value { 229 | name: "node_2" 230 | condition { 231 | expression_value { 232 | binary_expression { 233 | operation: EQUALS 234 | left { 235 | field_value { 236 | header_name: "standard_metadata" 237 | field_name: "ingress_port" 238 | } 239 | } 240 | right { 241 | hexstr_value { 242 | value: "0x0000" 243 | } 244 | } 245 | } 246 | } 247 | } 248 | if_branch: "tbl_hardcoded55" 249 | else_branch: "tbl_hardcoded57" 250 | } 251 | } 252 | initial_control: "node_2" 253 | 254 | -------------------------------------------------------------------------------- /z3-samples/port-table/sample.smt2: -------------------------------------------------------------------------------- 1 | ;; Copyright 2020 Google LLC 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 | ;; Define un-interpreted constants that constitute a packet 16 | ;; the exact definition should depend on the headers and 17 | ;; metadata defined/used in the p4 program. 18 | ;; 19 | ;; It is probably a good idea to keep the names of theses 20 | ;; constants similar to the attribute names from p4 21 | ;; 22 | (declare-const ingress_port Int) 23 | 24 | ;; Define un-interpreted constants that constitute an output 25 | ;; packet. At the end of the program, we will define some 26 | ;; constraints on this constant guided by the program flow 27 | ;; and the table entries 28 | (declare-const egress_spec Int) 29 | 30 | ;; Define an interpreted function (i.e. just an alias) per 31 | ;; row in the table. 32 | ;; 33 | ;; The body of the function uses the packet constants, such 34 | ;; the body is true iff the corresponding entry is hit by the 35 | ;; corresponding packet. 36 | ;; 37 | ;; The table definition should tell us which types to use and 38 | ;; what kind of logical operations should be performed (exact, LPM) 39 | ;; the values of the table entries can tell us what the concrete 40 | ;; values in these logical operations are. 41 | ;; 42 | ;; Later, we should consider using quantified variables instead 43 | ;; of values to abstract away from concrete table entries. 44 | ;; 45 | ;; These functions must be so that any given packet can only satisfy 46 | ;; no more than one function. An easy way to do this is to and 47 | ;; the function body with the negation of every other function. 48 | ;; that is probably inefficient, and can cause problems with cyclic 49 | ;; definitions. A better way is to do this by design, e.g. by taking 50 | ;; into consideration LPM and priorities inline. 51 | ;; 52 | ;; A good naming convention is to use the table name with the index 53 | ;; of the table entry 54 | ;; 55 | ;; 56 | ;; When parsing the p4 program, if we find table dependence (e.g. vrf) 57 | ;; then the dependent table functions should reference the depending 58 | ;; table functions in their body. 59 | ;; 60 | ;; This simple mechanism should be extended to handle entries 61 | ;; pointing to different actions and providing different parameters 62 | ;; actions likely do not need to be reflected here beyond as aliases 63 | ;; parameters are important depending on their use within the action 64 | (define-fun ports_exact0 () Bool (= ingress_port 0)) 65 | (define-fun ports_exact1 () Bool (= ingress_port 1)) 66 | 67 | ;; Any branching within the code should also be reflected here, 68 | ;; likely as function aliases similar to the above. For example, 69 | ;; an if statement with two branches should result in two functions 70 | ;; indicating which branch was taken. 71 | ;; 72 | ;; A branch's side effects likely need to be encoded in a way similar 73 | ;; to a table match side effect (e.g. params) 74 | ;; 75 | 76 | ;; We define restrictions/constraints on output attributes here, 77 | ;; this should also be done for other intermediate attributes 78 | ;; such as action inputs from table row matches. 79 | ;; 80 | (assert (= egress_spec 81 | (ite ports_exact0 1 82 | (ite ports_exact1 0 83 | -1 84 | ) 85 | ) 86 | )) 87 | 88 | ;; We should define aliases for important properties/restrictions here. 89 | ;; Things like packet validity, not being dropped, etc. 90 | ;; 91 | (define-fun not_dropped () Bool (not (= egress_spec -1))) 92 | 93 | ;; Finally, we need to have combinations of the above defined quantities 94 | ;; representing combinations of code paths and table hits. 95 | ;; 96 | ;; This should likely be generated via some cross-product like iteration 97 | ;; in our code, over all table entries from different tables and code branches, 98 | ;; guided by the parsed program trace/flow. 99 | ;; 100 | ;; For performance reasons, we should exclude clearly unreachable combinations. 101 | ;; For example, a table match that invokes an action with an if branch in a different 102 | ;; un-invoked action. However, not excluding them should not affect correctness, 103 | ;; the SMT solver should return unsat for such combinations 104 | ;; 105 | (push) 106 | (echo "combination 1") 107 | (assert ports_exact0) 108 | (check-sat) 109 | (get-model) 110 | (echo "") 111 | (pop) 112 | 113 | (push) 114 | (echo "combination 2") 115 | (assert ports_exact1) 116 | (check-sat) 117 | (get-model) 118 | (echo "") 119 | (pop) 120 | 121 | ;; Alternatively, we can ask for a packet that has output port 1 122 | ;; 123 | (push) 124 | (echo "output = 1") 125 | (assert (= egress_spec 1)) 126 | (check-sat) 127 | (get-model) 128 | (echo "") 129 | (pop) 130 | 131 | ;; Or, we can simulate what will happen to an input packet on port 1 132 | (push) 133 | (echo "input = 1") 134 | (assert (= ingress_port 1)) 135 | (check-sat) 136 | (get-model) 137 | (echo "") 138 | (pop) 139 | 140 | ;; Finally, we can determine if a packet is valid/not dropped, or ask 141 | ;; for an arbitrary valid one 142 | (push) 143 | (echo "Is packet with input port 5 valid/not-dropped?") 144 | (assert (and (= ingress_port 5) not_dropped)) 145 | (check-sat) 146 | (echo "") 147 | (pop) 148 | 149 | (push) 150 | (echo "Get any valid/not dropped packet") 151 | (assert not_dropped) 152 | (check-sat) 153 | (get-model) 154 | (echo "") 155 | (pop) 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/values.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // This file is responsible for parsing values from the bmv2 json and table 16 | // entries. 17 | // It is also responsible for translating any string values to corresponding 18 | // bitvectors and back, for fields that have the @p4runtime_translation 19 | // annotation. 20 | 21 | #ifndef P4_SYMBOLIC_SYMBOLIC_VALUES_H_ 22 | #define P4_SYMBOLIC_SYMBOLIC_VALUES_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include "gutil/status.h" 29 | #include "p4_pdpi/ir.pb.h" 30 | #include "z3++.h" 31 | 32 | namespace p4_symbolic { 33 | namespace symbolic { 34 | namespace values { 35 | 36 | // This class is responsible for translating string values into consistent 37 | // numberic ids, that can then be used to create bitvector values to use in 38 | // z3. 39 | // It also handles the reverse translation of previously allocated ids to 40 | // their corresponding string value. 41 | class IdAllocator { 42 | public: 43 | // Allocates an arbitrary integer id to this string identifier. 44 | // The Id is guaranteed to be consistent (the same bitvector is 45 | // allocated to equal strings), and unique per instance of this class. 46 | uint64_t AllocateId(const std::string &string_value); 47 | 48 | // Reverse translation of an allocated bit vector to the string value for 49 | // which it was allocated. 50 | gutil::StatusOr IdToString(uint64_t value) const; 51 | 52 | private: 53 | // A mapping from string values to bitvector values. 54 | std::unordered_map string_to_id_map_; 55 | 56 | // A mapping from bitvector values to string values. 57 | std::unordered_map id_to_string_map_; 58 | 59 | // Counter used to come up with new values per new allocation. 60 | uint64_t counter_ = 0; 61 | }; 62 | 63 | // This struct stores all the state that is required to translate string values 64 | // to internal bitvectors per custom p4 type (e.g. vrf_t), and reverse translate 65 | // bitvector values of fields of such a custom type to a string value. 66 | struct P4RuntimeTranslator { 67 | // Maps a type name to an allocator responsible for translating values for 68 | // that type. 69 | // We have an instance of the allocator class per translatable type. 70 | // The generated ids are unique only per type, different types may re-use 71 | // the same id. 72 | std::unordered_map p4runtime_translation_allocators; 73 | // Maps field name to a type name, only for fields we were able to detect had 74 | // a custom p4 type with a @p4runtime_translation annotation attached to it. 75 | // This information is not available in the bmv2 file, since it strips away 76 | // type aliases and annotations, so we must build it ourselves with a best 77 | // effort approach. 78 | std::unordered_map fields_p4runtime_type; 79 | }; 80 | 81 | // Transforms a hex string literal from bmv2 json to a pdpi::IrValue 82 | gutil::StatusOr ParseIrValue(std::string value); 83 | 84 | // Transforms a value read from bmv2 (e.g. hardcoded in the program) 85 | // to a z3::expr. 86 | // Essentially, this is just a formatting function that can format ipv4, ipv6 87 | // hexstrings, and macs as bitvectors in z3's format. 88 | // This function does not perform any string translation, and instead assumes 89 | // the values are already in the actual domain of values in the p4 program, 90 | // because p4 and bmv2 both do not support string translation, it is only used 91 | // as a logical translation at the boundry between P4RT and bmv2. 92 | gutil::StatusOr FormatBmv2Value(const pdpi::IrValue &value); 93 | 94 | // Transforms a value read from a table entry to a z3::expr. 95 | // On top of formatting ipv4, ipv6, hexstrings, and macs as bitvectors, 96 | // this function also translates string values to unique bitvectors. 97 | // The mapping of strings to bitvectors is stored in the translator state, 98 | // and used to map the same string to the same consistent bitvector, and to 99 | // do a reverse-translation when extracting values for headers and fields from 100 | // the z3 model. 101 | gutil::StatusOr FormatP4RTValue(const std::string &field_name, 102 | const std::string &type_name, 103 | const pdpi::IrValue &value, 104 | P4RuntimeTranslator *translator); 105 | 106 | // Reverse translation: operates opposite to FormatP4RTValue(). 107 | // If the given field was not detected to be translatable (perhaps it is indeed 108 | // not translatable), the function returns the value unchanged. 109 | // If the given field was detected to be translatable (e.g. some values for it 110 | // were previously translated by a call to FormatP4RTValue), then the value 111 | // is looked up using the reverse mapping inside the given translator, if that 112 | // look fails, an absl error is returned. 113 | gutil::StatusOr TranslateValueToP4RT( 114 | const std::string &field_name, const std::string &value, 115 | const P4RuntimeTranslator &translator); 116 | 117 | } // namespace values 118 | } // namespace symbolic 119 | } // namespace p4_symbolic 120 | 121 | #endif // P4_SYMBOLIC_SYMBOLIC_VALUES_H_ 122 | -------------------------------------------------------------------------------- /z3-samples/paths/paths.p4: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | /* -*- P4_16 -*- */ 16 | #include 17 | #include 18 | 19 | const bit<16> TYPE_IPV4 = 0x800; 20 | 21 | /************************************************************************* 22 | *********************** H E A D E R S *********************************** 23 | *************************************************************************/ 24 | 25 | typedef bit<9> egressSpec_t; 26 | typedef bit<48> macAddr_t; 27 | typedef bit<32> ip4Addr_t; 28 | 29 | header ethernet_t { 30 | macAddr_t dstAddr; 31 | macAddr_t srcAddr; 32 | bit<16> etherType; 33 | } 34 | 35 | header ipv4_t { 36 | bit<4> version; 37 | bit<4> ihl; 38 | bit<8> diffserv; 39 | bit<16> totalLen; 40 | bit<16> identification; 41 | bit<3> flags; 42 | bit<13> fragOffset; 43 | bit<8> ttl; 44 | bit<8> protocol; 45 | bit<16> hdrChecksum; 46 | ip4Addr_t srcAddr; 47 | ip4Addr_t dstAddr; 48 | } 49 | 50 | struct metadata { 51 | bit<8> vrf; 52 | } 53 | 54 | struct headers { 55 | ethernet_t ethernet; 56 | ipv4_t ipv4; 57 | } 58 | 59 | /************************************************************************* 60 | *********************** P A R S E R *********************************** 61 | *************************************************************************/ 62 | 63 | parser MyParser(packet_in packet, 64 | out headers hdr, 65 | inout metadata meta, 66 | inout standard_metadata_t standard_metadata) { 67 | 68 | state start { 69 | transition parse_ethernet; 70 | } 71 | 72 | state parse_ethernet { 73 | packet.extract(hdr.ethernet); 74 | transition select(hdr.ethernet.etherType) { 75 | TYPE_IPV4: parse_ipv4; 76 | default: accept; 77 | } 78 | } 79 | 80 | state parse_ipv4 { 81 | packet.extract(hdr.ipv4); 82 | transition accept; 83 | } 84 | 85 | } 86 | 87 | /************************************************************************* 88 | ************ C H E C K S U M V E R I F I C A T I O N ************* 89 | *************************************************************************/ 90 | 91 | control MyVerifyChecksum(inout headers hdr, inout metadata meta) { 92 | apply { } 93 | } 94 | 95 | 96 | /************************************************************************* 97 | ************** I N G R E S S P R O C E S S I N G ******************* 98 | *************************************************************************/ 99 | 100 | control MyIngress(inout headers hdr, 101 | inout metadata meta, 102 | inout standard_metadata_t standard_metadata) { 103 | action drop() { 104 | mark_to_drop(standard_metadata); 105 | } 106 | 107 | action set_port(PortId_t new_port) { 108 | if (hdr.ipv4.dstAddr == 10.10.0.0) { 109 | standard_metadata.egress_spec = 0; 110 | } else { 111 | standard_metadata.egress_spec = new_port; 112 | } 113 | } 114 | 115 | action set_vrf(bit<8> vrf) { 116 | metadata.vrf = vrf; 117 | vrf_ip_to_port.apply(); 118 | } 119 | 120 | table vrf_ip_to_port { 121 | key = { 122 | hdr.ipv4.dstAddr: lpm; 123 | meta.vrf: exact; 124 | } 125 | actions = { 126 | set_port; 127 | drop; 128 | } 129 | size = 1024; 130 | default_action = drop(); 131 | } 132 | 133 | table port_to_vrf { 134 | key = { 135 | standard_metadata.ingress_port: exact; 136 | } 137 | actions = { 138 | set_vrf; 139 | drop; 140 | } 141 | size = 1024; 142 | default_action = drop(); 143 | } 144 | 145 | apply { 146 | port_to_vrf.apply(); 147 | } 148 | } 149 | 150 | /************************************************************************* 151 | **************** E G R E S S P R O C E S S I N G ******************* 152 | *************************************************************************/ 153 | 154 | control MyEgress(inout headers hdr, 155 | inout metadata meta, 156 | inout standard_metadata_t standard_metadata) { 157 | apply { } 158 | } 159 | 160 | /************************************************************************* 161 | ************* C H E C K S U M C O M P U T A T I O N ************** 162 | *************************************************************************/ 163 | 164 | control MyComputeChecksum(inout headers hdr, inout metadata meta) { 165 | apply { } 166 | } 167 | 168 | /************************************************************************* 169 | *********************** D E P A R S E R ******************************* 170 | *************************************************************************/ 171 | 172 | control MyDeparser(packet_out packet, in headers hdr) { 173 | apply { } 174 | } 175 | 176 | /************************************************************************* 177 | *********************** S W I T C H ******************************* 178 | *************************************************************************/ 179 | 180 | V1Switch( 181 | MyParser(), 182 | MyVerifyChecksum(), 183 | MyIngress(), 184 | MyEgress(), 185 | MyComputeChecksum(), 186 | MyDeparser() 187 | ) main; 188 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/parser.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | // Hardcodes the behavior of an interesting p4 parser that is part 16 | // of the p4 program we are interested in. 17 | // The hardcoded behavior sets the validity of certain header fields 18 | // based on the fields in the packet, and sets the default values 19 | // for local_metadata fields. 20 | 21 | #include "p4_symbolic/symbolic/parser.h" 22 | 23 | #include "p4_symbolic/symbolic/operators.h" 24 | #include "z3++.h" 25 | 26 | namespace p4_symbolic { 27 | namespace symbolic { 28 | namespace parser { 29 | 30 | gutil::StatusOr> EvaluateHardcodedParser( 31 | const SymbolicPerPacketState &state) { 32 | std::vector constraints; 33 | 34 | // Set initial value for vrf. 35 | ASSIGN_OR_RETURN(z3::expr vrf_id, state.Get("scalars.userMetadata.vrf_id")); 36 | ASSIGN_OR_RETURN(z3::expr vrf_constraint, 37 | operators::Eq(vrf_id, Z3Context().bv_val(0, 1))); 38 | constraints.push_back(vrf_constraint); 39 | 40 | // l4_src_port and l4_dst_port are extracted from the headers if tcp or udp 41 | // were used, and set to zero otherwise. 42 | // We must be careful that these values are guarded by the proper validity 43 | // guards, or we will impose contradictory constraints. 44 | ASSIGN_OR_RETURN(z3::expr l4_src_port, 45 | state.Get("scalars.userMetadata.l4_src_port")); 46 | ASSIGN_OR_RETURN(z3::expr l4_dst_port, 47 | state.Get("scalars.userMetadata.l4_dst_port")); 48 | 49 | // Find out which headers the program supports. 50 | ASSIGN_OR_RETURN(z3::expr ipv4_valid, state.Get("ipv4.$valid$")); 51 | ASSIGN_OR_RETURN(z3::expr ipv6_valid, state.Get("ipv6.$valid$")); 52 | ASSIGN_OR_RETURN(z3::expr arp_valid, state.Get("arp.$valid$")); 53 | 54 | // Put restrictions on what "eth_type" can be and how it affects validity of 55 | // certain headers. 56 | ASSIGN_OR_RETURN(z3::expr eth_type, state.Get("ethernet.ether_type")); 57 | constraints.push_back(ipv4_valid == (eth_type == ETHERTYPE_IPV4)); 58 | constraints.push_back(ipv6_valid == (eth_type == ETHERTYPE_IPV6)); 59 | constraints.push_back(arp_valid == (eth_type == ETHERTYPE_ARP)); 60 | 61 | // Put similar restrictions on the validity of protocol-specific headers. 62 | // Which protocol used is specified by ipv4.protcol or ipv6.next_headers. 63 | ASSIGN_OR_RETURN(z3::expr protocol, state.Get("ipv4.protocol")); 64 | ASSIGN_OR_RETURN(z3::expr next_header, state.Get("ipv6.next_header")); 65 | 66 | // ICMP. 67 | ASSIGN_OR_RETURN(z3::expr icmp_valid, state.Get("icmp.$valid$")); 68 | z3::expr icmp_valid_ipv4 = (protocol == IP_PROTOCOL_ICMP) && ipv4_valid; 69 | z3::expr icmp_valid_ipv6 = (next_header == IP_PROTOCOL_ICMPV6) && ipv6_valid; 70 | constraints.push_back(icmp_valid == (icmp_valid_ipv4 || icmp_valid_ipv6)); 71 | 72 | // TCP. 73 | ASSIGN_OR_RETURN(z3::expr tcp_valid, state.Get("tcp.$valid$")); 74 | z3::expr tcp_valid_ipv4 = (protocol == IP_PROTOCOL_TCP) && ipv4_valid; 75 | z3::expr tcp_valid_ipv6 = (next_header == IP_PROTOCOL_TCP) && ipv6_valid; 76 | constraints.push_back(tcp_valid == (tcp_valid_ipv4 || tcp_valid_ipv6)); 77 | 78 | // Set l4_src_port and l4_dst_port to those of tcp header, if tcp is used. 79 | ASSIGN_OR_RETURN(z3::expr tcp_src_port, state.Get("tcp.src_port")); 80 | ASSIGN_OR_RETURN(z3::expr tcp_dst_port, state.Get("tcp.dst_port")); 81 | ASSIGN_OR_RETURN(z3::expr l4_src_port_eq_tcp_src_port, 82 | operators::Eq(tcp_src_port, l4_src_port)); 83 | ASSIGN_OR_RETURN(z3::expr l4_dst_port_eq_tcp_dst_port, 84 | operators::Eq(tcp_dst_port, l4_dst_port)); 85 | 86 | constraints.push_back(z3::implies(tcp_valid, l4_src_port_eq_tcp_src_port)); 87 | constraints.push_back(z3::implies(tcp_valid, l4_dst_port_eq_tcp_dst_port)); 88 | 89 | // UDP. 90 | ASSIGN_OR_RETURN(z3::expr udp_valid, state.Get("udp.$valid$")); 91 | z3::expr udp_valid_ipv4 = (protocol == IP_PROTOCOL_UDP) && ipv4_valid; 92 | z3::expr udp_valid_ipv6 = (next_header == IP_PROTOCOL_UDP) && ipv6_valid; 93 | constraints.push_back(udp_valid == (udp_valid_ipv4 || udp_valid_ipv6)); 94 | 95 | // Set l4_src_port and l4_dst_port to those of udp header, if udp is used. 96 | ASSIGN_OR_RETURN(z3::expr udp_src_port, state.Get("udp.src_port")); 97 | ASSIGN_OR_RETURN(z3::expr udp_dst_port, state.Get("udp.dst_port")); 98 | ASSIGN_OR_RETURN(z3::expr l4_src_port_eq_udp_src_port, 99 | operators::Eq(udp_src_port, l4_src_port)); 100 | ASSIGN_OR_RETURN(z3::expr l4_dst_port_eq_udp_dst_port, 101 | operators::Eq(udp_dst_port, l4_dst_port)); 102 | 103 | constraints.push_back(z3::implies(udp_valid, l4_src_port_eq_udp_src_port)); 104 | constraints.push_back(z3::implies(udp_valid, l4_dst_port_eq_udp_dst_port)); 105 | 106 | // Default values for l4_src_port and l4_dst_port. 107 | ASSIGN_OR_RETURN(z3::expr tcp_or_udp_valid, 108 | operators::Or(tcp_valid, udp_valid)); 109 | ASSIGN_OR_RETURN(z3::expr l4_src_port_constraint, 110 | operators::Eq(l4_src_port, Z3Context().bv_val(0, 1))); 111 | ASSIGN_OR_RETURN(z3::expr l4_dst_port_constraint, 112 | operators::Eq(l4_dst_port, Z3Context().bv_val(0, 1))); 113 | constraints.push_back(z3::implies(!tcp_or_udp_valid, l4_src_port_constraint)); 114 | constraints.push_back(z3::implies(!tcp_or_udp_valid, l4_dst_port_constraint)); 115 | 116 | // Done, return all constraints. 117 | return constraints; 118 | } 119 | 120 | } // namespace parser 121 | } // namespace symbolic 122 | } // namespace p4_symbolic 123 | -------------------------------------------------------------------------------- /p4-samples/ipv4-routing/basic.p4: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | /* -*- P4_16 -*- */ 16 | #include 17 | #include 18 | 19 | const bit<16> TYPE_IPV4 = 0x800; 20 | 21 | /************************************************************************* 22 | *********************** H E A D E R S *********************************** 23 | *************************************************************************/ 24 | 25 | typedef bit<9> egressSpec_t; 26 | typedef bit<48> macAddr_t; 27 | typedef bit<32> ip4Addr_t; 28 | 29 | header ethernet_t { 30 | macAddr_t dstAddr; 31 | macAddr_t srcAddr; 32 | bit<16> etherType; 33 | } 34 | 35 | header ipv4_t { 36 | bit<4> version; 37 | bit<4> ihl; 38 | bit<8> diffserv; 39 | bit<16> totalLen; 40 | bit<16> identification; 41 | bit<3> flags; 42 | bit<13> fragOffset; 43 | bit<8> ttl; 44 | bit<8> protocol; 45 | bit<16> hdrChecksum; 46 | ip4Addr_t srcAddr; 47 | ip4Addr_t dstAddr; 48 | } 49 | 50 | struct metadata { 51 | /* empty */ 52 | } 53 | 54 | struct headers { 55 | ethernet_t ethernet; 56 | ipv4_t ipv4; 57 | } 58 | 59 | /************************************************************************* 60 | *********************** P A R S E R *********************************** 61 | *************************************************************************/ 62 | 63 | parser MyParser(packet_in packet, 64 | out headers hdr, 65 | inout metadata meta, 66 | inout standard_metadata_t standard_metadata) { 67 | 68 | state start { 69 | transition parse_ethernet; 70 | } 71 | 72 | state parse_ethernet { 73 | packet.extract(hdr.ethernet); 74 | transition select(hdr.ethernet.etherType) { 75 | TYPE_IPV4: parse_ipv4; 76 | default: accept; 77 | } 78 | } 79 | 80 | state parse_ipv4 { 81 | packet.extract(hdr.ipv4); 82 | transition accept; 83 | } 84 | 85 | } 86 | 87 | /************************************************************************* 88 | ************ C H E C K S U M V E R I F I C A T I O N ************* 89 | *************************************************************************/ 90 | 91 | control MyVerifyChecksum(inout headers hdr, inout metadata meta) { 92 | apply { } 93 | } 94 | 95 | 96 | /************************************************************************* 97 | ************** I N G R E S S P R O C E S S I N G ******************* 98 | *************************************************************************/ 99 | 100 | control MyIngress(inout headers hdr, 101 | inout metadata meta, 102 | inout standard_metadata_t standard_metadata) { 103 | action drop() { 104 | mark_to_drop(standard_metadata); 105 | } 106 | 107 | action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) { 108 | standard_metadata.egress_spec = port; 109 | hdr.ethernet.srcAddr = hdr.ethernet.dstAddr; 110 | hdr.ethernet.dstAddr = dstAddr; 111 | hdr.ipv4.ttl = hdr.ipv4.ttl - 1; 112 | } 113 | 114 | table ipv4_lpm { 115 | key = { 116 | hdr.ipv4.dstAddr: lpm @format(IPV4_ADDRESS); 117 | } 118 | actions = { 119 | @proto_id(1) ipv4_forward; 120 | @proto_id(2) drop; 121 | @proto_id(3) NoAction; 122 | } 123 | size = 1024; 124 | default_action = drop(); 125 | } 126 | 127 | apply { 128 | if (hdr.ipv4.isValid()) { 129 | ipv4_lpm.apply(); 130 | } 131 | } 132 | } 133 | 134 | /************************************************************************* 135 | **************** E G R E S S P R O C E S S I N G ******************* 136 | *************************************************************************/ 137 | 138 | control MyEgress(inout headers hdr, 139 | inout metadata meta, 140 | inout standard_metadata_t standard_metadata) { 141 | apply { } 142 | } 143 | 144 | /************************************************************************* 145 | ************* C H E C K S U M C O M P U T A T I O N ************** 146 | *************************************************************************/ 147 | 148 | control MyComputeChecksum(inout headers hdr, inout metadata meta) { 149 | apply { 150 | update_checksum( 151 | hdr.ipv4.isValid(), 152 | { hdr.ipv4.version, 153 | hdr.ipv4.ihl, 154 | hdr.ipv4.diffserv, 155 | hdr.ipv4.totalLen, 156 | hdr.ipv4.identification, 157 | hdr.ipv4.flags, 158 | hdr.ipv4.fragOffset, 159 | hdr.ipv4.ttl, 160 | hdr.ipv4.protocol, 161 | hdr.ipv4.srcAddr, 162 | hdr.ipv4.dstAddr }, 163 | hdr.ipv4.hdrChecksum, 164 | HashAlgorithm.csum16); 165 | } 166 | } 167 | 168 | /************************************************************************* 169 | *********************** D E P A R S E R ******************************* 170 | *************************************************************************/ 171 | 172 | control MyDeparser(packet_out packet, in headers hdr) { 173 | apply { 174 | packet.emit(hdr.ethernet); 175 | packet.emit(hdr.ipv4); 176 | } 177 | } 178 | 179 | /************************************************************************* 180 | *********************** S W I T C H ******************************* 181 | *************************************************************************/ 182 | 183 | V1Switch( 184 | MyParser(), 185 | MyVerifyChecksum(), 186 | MyIngress(), 187 | MyEgress(), 188 | MyComputeChecksum(), 189 | MyDeparser() 190 | ) main; 191 | -------------------------------------------------------------------------------- /z3-samples/paths/paths.opt1.smt2: -------------------------------------------------------------------------------- 1 | ;; Copyright 2020 Google LLC 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 | ;; Packet attributes 16 | (declare-const ingress_port Int) 17 | (declare-const dstAddr (_ BitVec 32)) 18 | 19 | ;; Table output parameters 20 | (declare-const vrf Int) 21 | 22 | ;; Final output attributes 23 | (declare-const egress_spec Int) 24 | 25 | ;; Table Matches 26 | ;; port_to_vrf 27 | (define-fun port_to_vrf0 () Bool (= ingress_port 0)) 28 | (define-fun port_to_vrf1 () Bool (= ingress_port 1)) 29 | 30 | ;; vrf_ip_to_port 31 | ;; 32 | ;; There are several ways to handle lpm overlaps, priorities, etc 33 | ;; one simple way is to define each key match as its own alias indpendently 34 | ;; then combine them when defining a row match with the appropriate negations. 35 | ;; 36 | ;; It would have been nice to always mindlessly negate all other lpm matches when defining 37 | ;; one match. However, this won't work: 38 | ;; (1) there are other field matches (e.g. vrf) and if they are different, there is no overlap. 39 | ;; (2) lpm overlaps are directional, they must be included in the largest prefix, but not the others. 40 | ;; 41 | (define-fun vrf_ip_to_port0_lpm () Bool (= ((_ extract 31 8) dstAddr) #x0a0a00)) ;; 10.10.0.* 42 | (define-fun vrf_ip_to_port1_lpm () Bool (= ((_ extract 31 16) dstAddr) #x0a0a)) ;; 10.10.*.* 43 | (define-fun vrf_ip_to_port2_lpm () Bool (= ((_ extract 31 16) dstAddr) #x1414)) ;; 20.20.*.* 44 | 45 | (define-fun vrf_ip_to_port0 () Bool (and 46 | vrf_ip_to_port0_lpm 47 | (= vrf 10) 48 | )) 49 | (define-fun vrf_ip_to_port1 () Bool (and 50 | ;; our code determines which combinations are needed 51 | ;; can invoke the SMT solver to find when! 52 | (and (not vrf_ip_to_port0_lpm) vrf_ip_to_port1_lpm) 53 | (= vrf 10) 54 | )) 55 | (define-fun vrf_ip_to_port2 () Bool (and 56 | vrf_ip_to_port2_lpm 57 | (= vrf 20) 58 | )) 59 | 60 | ;; Branches 61 | (define-fun set_port_if1_then () Bool (= dstAddr #x0a0a0000)) 62 | (define-fun set_port_if1_else () Bool (not (= dstAddr #x0a0a0000))) 63 | 64 | ;; link vrf value to table match 65 | ;; 66 | ;; simple way of doing it 67 | ;; encode the table mechanism in SMT 68 | ;; if statements proportional to table size 69 | ;; not very efficient probably 70 | ;; 71 | (assert (= vrf 72 | (ite port_to_vrf0 10 73 | (ite port_to_vrf1 20 74 | -1 75 | ) 76 | ) 77 | )) 78 | 79 | ;; Similarly, link output actions to the rest of the follow 80 | (assert (= egress_spec 81 | (ite set_port_if1_then 0 82 | (ite vrf_ip_to_port0 1 83 | (ite vrf_ip_to_port1 2 84 | (ite vrf_ip_to_port2 3 85 | -1 86 | ) 87 | ) 88 | ) 89 | ) 90 | )) 91 | 92 | ;; Validity: that a packet is not dropped 93 | (define-fun not_dropped () Bool (not (= egress_spec -1))) 94 | 95 | ;; Combinations 96 | ;; 97 | ;; Mindlessly dump all combintations, even ones that are obviously impossible. 98 | ;; 99 | (push) 100 | (echo "combination 1") 101 | (assert (and port_to_vrf0 vrf_ip_to_port0 set_port_if1_then)) 102 | (check-sat) 103 | (get-model) 104 | (echo "") 105 | (pop) 106 | 107 | (push) 108 | (echo "combination 2") 109 | (assert (and port_to_vrf0 vrf_ip_to_port0 set_port_if1_else)) 110 | (check-sat) 111 | (get-model) 112 | (echo "") 113 | (pop) 114 | 115 | (push) 116 | (echo "combination 3") 117 | (assert (and port_to_vrf0 vrf_ip_to_port1 set_port_if1_then)) 118 | (check-sat) 119 | (get-model) 120 | (echo "") 121 | (pop) 122 | 123 | (push) 124 | (echo "combination 4") 125 | (assert (and port_to_vrf0 vrf_ip_to_port1 set_port_if1_else)) 126 | (check-sat) 127 | (get-model) 128 | (echo "") 129 | (pop) 130 | 131 | (push) 132 | (echo "combination 5") 133 | (assert (and port_to_vrf0 vrf_ip_to_port2 set_port_if1_then)) 134 | (check-sat) 135 | (get-model) 136 | (echo "") 137 | (pop) 138 | 139 | (push) 140 | (echo "combination 6") 141 | (assert (and port_to_vrf0 vrf_ip_to_port2 set_port_if1_else)) 142 | (check-sat) 143 | (get-model) 144 | (echo "") 145 | (pop) 146 | 147 | (push) 148 | (echo "combination 7") 149 | (assert (and port_to_vrf1 vrf_ip_to_port0 set_port_if1_then)) 150 | (check-sat) 151 | (get-model) 152 | (echo "") 153 | (pop) 154 | 155 | (push) 156 | (echo "combination 8") 157 | (assert (and port_to_vrf1 vrf_ip_to_port0 set_port_if1_else)) 158 | (check-sat) 159 | (get-model) 160 | (echo "") 161 | (pop) 162 | 163 | (push) 164 | (echo "combination 9") 165 | (assert (and port_to_vrf1 vrf_ip_to_port1 set_port_if1_then)) 166 | (check-sat) 167 | (get-model) 168 | (echo "") 169 | (pop) 170 | 171 | (push) 172 | (echo "combination 10") 173 | (assert (and port_to_vrf1 vrf_ip_to_port1 set_port_if1_else)) 174 | (check-sat) 175 | (get-model) 176 | (echo "") 177 | (pop) 178 | 179 | (push) 180 | (echo "combination 11") 181 | (assert (and port_to_vrf1 vrf_ip_to_port2 set_port_if1_then)) 182 | (check-sat) 183 | (get-model) 184 | (echo "") 185 | (pop) 186 | 187 | (push) 188 | (echo "combination 12") 189 | (assert (and port_to_vrf1 vrf_ip_to_port2 set_port_if1_else)) 190 | (check-sat) 191 | (get-model) 192 | (echo "") 193 | (pop) 194 | 195 | ;; Alternatively, you can have partial queries 196 | ;; For example: 197 | ;; Give me any valid packet 198 | ;; 199 | (push) 200 | (echo "Any valid packet") 201 | (assert not_dropped) 202 | (check-sat) 203 | (get-model) 204 | (echo "") 205 | (pop) 206 | 207 | ;; Give me a packet that will have output port 3 208 | (push) 209 | (echo "output = 3") 210 | (assert (= egress_spec 3)) 211 | (check-sat) 212 | (get-model) 213 | (echo "") 214 | (pop) 215 | 216 | 217 | ;; Give me a packet with output port 2 and vrf 10 218 | (push) 219 | (echo "output = 2 and vrf = 10") 220 | (assert (and (= egress_spec 2) (= vrf 10))) 221 | (check-sat) 222 | (get-model) 223 | (echo "") 224 | (pop) 225 | 226 | ;; Partial Trace plus output port is 1 227 | (push) 228 | (echo "output = 1 and port to vrf entry 0 is hit") 229 | (assert (and (= egress_spec 1) port_to_vrf0)) 230 | (check-sat) 231 | (get-model) 232 | (echo "") 233 | (pop) 234 | -------------------------------------------------------------------------------- /p4_symbolic/symbolic/symbolic.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 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 | #include "p4_symbolic/symbolic/symbolic.h" 16 | 17 | #include 18 | 19 | #include "p4_symbolic/symbolic/control.h" 20 | #include "p4_symbolic/symbolic/operators.h" 21 | #include "p4_symbolic/symbolic/packet.h" 22 | #include "p4_symbolic/symbolic/parser.h" 23 | #include "p4_symbolic/symbolic/util.h" 24 | 25 | namespace p4_symbolic { 26 | namespace symbolic { 27 | 28 | z3::context &Z3Context() { 29 | static z3::context *z3_context = new z3::context(); 30 | return *z3_context; 31 | } 32 | 33 | gutil::StatusOr> EvaluateP4Pipeline( 34 | const Dataplane &data_plane, const std::vector &physical_ports, 35 | bool hardcoded_parser) { 36 | // Use global context to define a solver. 37 | std::unique_ptr z3_solver = 38 | std::make_unique(Z3Context()); 39 | 40 | // Create free/unconstrainted headers variables, and then 41 | // put constraints on them matching the hardcoded behavior of the parser 42 | // for programs we are interested in. 43 | ASSIGN_OR_RETURN(SymbolicPerPacketState ingress_headers, 44 | SymbolicGuardedMap::CreateSymbolicGuardedMap( 45 | data_plane.program.headers())); 46 | if (hardcoded_parser) { 47 | ASSIGN_OR_RETURN(std::vector parser_constraints, 48 | parser::EvaluateHardcodedParser(ingress_headers)); 49 | for (z3::expr constraint : parser_constraints) { 50 | z3_solver->add(constraint); 51 | } 52 | } 53 | 54 | // Initially, the p4runtime translator has empty state. 55 | values::P4RuntimeTranslator translator; 56 | 57 | // "Accumulator"-style p4 program headers. 58 | // This is used to evaluate the P4 program. 59 | // Initially free/unconstrained and contains symbolic variables for 60 | // every header field. 61 | SymbolicPerPacketState egress_headers(ingress_headers); 62 | 63 | ASSIGN_OR_RETURN(z3::expr ingress_port, 64 | ingress_headers.Get("standard_metadata.ingress_port")); 65 | SymbolicPacket ingress_packet = 66 | packet::ExtractSymbolicPacket(ingress_headers); 67 | 68 | // Evaluate the initial control, which will evaluate the next controls 69 | // internally and return the full symbolic trace. 70 | ASSIGN_OR_RETURN( 71 | SymbolicTrace trace, 72 | control::EvaluateControl(data_plane, data_plane.program.initial_control(), 73 | &egress_headers, &translator, 74 | Z3Context().bool_val(true))); 75 | 76 | // Alias the event that the packet is dropped for ease of use in assertions. 77 | z3::expr dropped_value = 78 | Z3Context().bv_val(DROPPED_EGRESS_SPEC_VALUE, DROPPED_EGRESS_SPEC_LENGTH); 79 | ASSIGN_OR_RETURN(trace.dropped, 80 | egress_headers.Get("standard_metadata.egress_spec")); 81 | ASSIGN_OR_RETURN(trace.dropped, operators::Eq(trace.dropped, dropped_value)); 82 | 83 | // Construct a symbolic context, containing state and trace information 84 | // from evaluating the tables. 85 | ASSIGN_OR_RETURN(z3::expr egress_port, 86 | egress_headers.Get("standard_metadata.egress_spec")); 87 | 88 | // Restrict ports to the available physical ports. 89 | if (physical_ports.size() > 0) { 90 | z3::expr ingress_port_domain = Z3Context().bool_val(false); 91 | z3::expr egress_port_domain = trace.dropped; 92 | unsigned int port_size = ingress_port.get_sort().bv_size(); 93 | for (int port : physical_ports) { 94 | ASSIGN_OR_RETURN( 95 | z3::expr ingress_port_eq, 96 | operators::Eq(ingress_port, Z3Context().bv_val(port, port_size))); 97 | ASSIGN_OR_RETURN( 98 | z3::expr egress_port_eq, 99 | operators::Eq(egress_port, Z3Context().bv_val(port, port_size))); 100 | 101 | ASSIGN_OR_RETURN(ingress_port_domain, 102 | operators::Or(ingress_port_domain, ingress_port_eq)); 103 | ASSIGN_OR_RETURN(egress_port_domain, 104 | operators::Or(egress_port_domain, egress_port_eq)); 105 | } 106 | z3_solver->add(ingress_port_domain); 107 | z3_solver->add(egress_port_domain); 108 | } 109 | 110 | // Construct solver state for this program. 111 | SymbolicPacket egress_packet = packet::ExtractSymbolicPacket(egress_headers); 112 | SymbolicContext symbolic_context = { 113 | ingress_port, egress_port, ingress_packet, egress_packet, 114 | ingress_headers, egress_headers, trace}; 115 | 116 | return std::make_unique(data_plane.program, data_plane.entries, 117 | symbolic_context, std::move(z3_solver), 118 | translator); 119 | } 120 | 121 | gutil::StatusOr> Solve( 122 | const std::unique_ptr &solver_state, 123 | const Assertion &assertion) { 124 | z3::expr constraint = assertion(solver_state->context); 125 | 126 | solver_state->solver->push(); 127 | solver_state->solver->add(constraint); 128 | switch (solver_state->solver->check()) { 129 | case z3::unsat: 130 | solver_state->solver->pop(); 131 | return std::optional(); 132 | 133 | case z3::unknown: 134 | solver_state->solver->pop(); 135 | return std::optional(); 136 | 137 | case z3::sat: 138 | default: 139 | z3::model packet_model = solver_state->solver->get_model(); 140 | ASSIGN_OR_RETURN( 141 | ConcreteContext result, 142 | util::ExtractFromModel(solver_state->context, packet_model, 143 | solver_state->translator)); 144 | solver_state->solver->pop(); 145 | return std::make_optional(result); 146 | } 147 | } 148 | 149 | std::string DebugSMT(const std::unique_ptr &solver_state, 150 | const Assertion &assertion) { 151 | solver_state->solver->push(); 152 | solver_state->solver->add(assertion(solver_state->context)); 153 | std::string smt = solver_state->solver->to_smt2(); 154 | solver_state->solver->pop(); 155 | return smt; 156 | } 157 | 158 | } // namespace symbolic 159 | } // namespace p4_symbolic 160 | -------------------------------------------------------------------------------- /WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 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 | workspace(name = "p4_symbolic") 16 | 17 | # Buildtools (and dependencies). This Include buildifier, bazel's auto 18 | # formatting/linting. 19 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 20 | load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") 21 | 22 | http_archive( 23 | name = "io_bazel_rules_go", 24 | sha256 = "a8d6b1b354d371a646d2f7927319974e0f9e52f73a2452d2b3877118169eb6bb", 25 | urls = [ 26 | "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.23.3/rules_go-v0.23.3.tar.gz", 27 | "https://github.com/bazelbuild/rules_go/releases/download/v0.23.3/rules_go-v0.23.3.tar.gz", 28 | ], 29 | ) 30 | 31 | load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") 32 | 33 | go_rules_dependencies() 34 | 35 | go_register_toolchains() 36 | 37 | http_archive( 38 | name = "bazel_gazelle", 39 | sha256 = "cdb02a887a7187ea4d5a27452311a75ed8637379a1287d8eeb952138ea485f7d", 40 | urls = [ 41 | "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.1/bazel-gazelle-v0.21.1.tar.gz", 42 | "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.21.1/bazel-gazelle-v0.21.1.tar.gz", 43 | ], 44 | ) 45 | 46 | load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") 47 | 48 | # Workaround for WORKSPACE.bazel. 49 | # See https://github.com/bazelbuild/bazel-gazelle/issues/678 50 | gazelle_dependencies(go_repository_default_config = "@//:WORKSPACE.bazel") 51 | 52 | http_archive( 53 | name = "com_github_bazelbuild_buildtools", 54 | strip_prefix = "buildtools-master", 55 | url = "https://github.com/bazelbuild/buildtools/archive/master.zip", 56 | ) 57 | 58 | # "rules_cc" defines rules for generating C++ code from Protocol Buffers. 59 | http_archive( 60 | name = "rules_cc", 61 | sha256 = "35f2fb4ea0b3e61ad64a369de284e4fbbdcdba71836a5555abb5e194cf119509", 62 | strip_prefix = "rules_cc-624b5d59dfb45672d4239422fa1e3de1822ee110", 63 | urls = [ 64 | "https://mirror.bazel.build/github.com/bazelbuild/rules_cc/archive/624b5d59dfb45672d4239422fa1e3de1822ee110.tar.gz", 65 | "https://github.com/bazelbuild/rules_cc/archive/624b5d59dfb45672d4239422fa1e3de1822ee110.tar.gz", 66 | ], 67 | ) 68 | 69 | # "rules_proto" defines abstract rules for building Protocol Buffers. 70 | http_archive( 71 | name = "rules_proto", 72 | sha256 = "2490dca4f249b8a9a3ab07bd1ba6eca085aaf8e45a734af92aad0c42d9dc7aaf", 73 | strip_prefix = "rules_proto-218ffa7dfa5408492dc86c01ee637614f8695c45", 74 | urls = [ 75 | "https://mirror.bazel.build/github.com/bazelbuild/rules_proto/archive/218ffa7dfa5408492dc86c01ee637614f8695c45.tar.gz", 76 | "https://github.com/bazelbuild/rules_proto/archive/218ffa7dfa5408492dc86c01ee637614f8695c45.tar.gz", 77 | ], 78 | ) 79 | 80 | load("@rules_cc//cc:repositories.bzl", "rules_cc_dependencies") 81 | 82 | rules_cc_dependencies() 83 | 84 | # protobuf headers and utils 85 | http_archive( 86 | name = "com_google_protobuf", 87 | sha256 = "1a83f0525e5c8096b7b812181865da3c8637de88f9777056cefbf51a1eb0b83f", 88 | strip_prefix = "protobuf-3.12.3", 89 | url = "https://github.com/protocolbuffers/protobuf/releases/download/v3.12.3/protobuf-all-3.12.3.tar.gz", 90 | ) 91 | 92 | load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") 93 | 94 | protobuf_deps() 95 | 96 | load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") 97 | 98 | rules_proto_dependencies() 99 | 100 | rules_proto_toolchains() 101 | 102 | # PDPI and its dependencies. 103 | local_repository( 104 | name = "p4_pdpi", 105 | path = "third_party/p4-pdpi", 106 | ) 107 | 108 | git_repository( 109 | name = "com_google_absl", 110 | remote = "https://github.com/abseil/abseil-cpp.git", 111 | tag = "20200225.1", 112 | ) 113 | 114 | # p4 Runtime and its dependencies 115 | local_repository( 116 | name = "com_github_p4lang_p4runtime", 117 | path = "third_party/p4-pdpi/third_party/p4runtime/proto", 118 | ) 119 | 120 | git_repository( 121 | name = "com_google_googleapis", 122 | commit = "dd244bb3a5023a4a9290b21dae6b99020c026123", 123 | remote = "https://github.com/googleapis/googleapis", 124 | shallow_since = "1591402163 -0700", 125 | ) 126 | 127 | load("@com_google_googleapis//:repository_rules.bzl", "switched_rules_by_language") 128 | 129 | switched_rules_by_language( 130 | name = "com_google_googleapis_imports", 131 | cc = True, 132 | grpc = True, 133 | python = True, 134 | ) 135 | 136 | http_archive( 137 | name = "com_github_grpc_grpc", 138 | sha256 = "0343e6dbde66e9a31c691f2f61e98d79f3584e03a11511fad3f10e3667832a45", 139 | strip_prefix = "grpc-1.29.1", 140 | url = "https://github.com/grpc/grpc/archive/v1.29.1.tar.gz", 141 | ) 142 | 143 | load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps") 144 | 145 | grpc_deps() 146 | 147 | load("@com_github_grpc_grpc//bazel:grpc_extra_deps.bzl", "grpc_extra_deps") 148 | 149 | grpc_extra_deps() 150 | 151 | load("@com_github_grpc_grpc//bazel:grpc_python_deps.bzl", "grpc_python_deps") 152 | 153 | grpc_python_deps() 154 | 155 | load("@rules_python//python:pip.bzl", "pip_import", "pip_repositories") 156 | 157 | pip_repositories() 158 | 159 | pip_import( 160 | name = "grpc_python_dependencies", 161 | requirements = "@com_github_grpc_grpc//:requirements.bazel.txt", 162 | ) 163 | 164 | load("@grpc_python_dependencies//:requirements.bzl", "pip_install") 165 | 166 | pip_install() 167 | 168 | # Foreign rules to use for z3 169 | http_archive( 170 | name = "rules_foreign_cc", 171 | strip_prefix = "rules_foreign_cc-master", 172 | url = "https://github.com/bazelbuild/rules_foreign_cc/archive/master.zip", 173 | ) 174 | 175 | load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies") 176 | 177 | rules_foreign_cc_dependencies() 178 | 179 | # Grab z3 180 | all_content = """filegroup(name = "all", srcs = glob(["**"]), visibility = ["//visibility:public"])""" 181 | 182 | http_archive( 183 | name = "z3", 184 | build_file_content = all_content, 185 | strip_prefix = "z3-z3-4.8.8", 186 | type = "zip", 187 | url = "https://github.com/Z3Prover/z3/archive/z3-4.8.8.zip", 188 | ) 189 | --------------------------------------------------------------------------------