├── p4runtime_sh ├── __init__.py ├── testdata │ ├── unittest.bin │ └── unittest.p4info.pb.txt ├── __main__.py ├── utils.py ├── bytes_utils.py ├── global_options.py ├── context.py ├── p4runtime.py └── test.py ├── .coveragerc ├── .flake8 ├── requirements-dev.txt ├── .gitignore ├── .dockerignore ├── unittest.cfg ├── setup.py ├── docker_entry_point.sh ├── .github ├── dependabot.yml └── workflows │ ├── toc.yml │ ├── pypi.yml │ └── test.yml ├── pyproject.toml ├── Makefile ├── p4runtime-sh ├── Dockerfile.dev ├── usage ├── oneshots.md ├── pre.md ├── read_table_entry_counter.md └── packet_io.md ├── setup.cfg ├── Dockerfile ├── README-publish-python-package.md ├── config_builders ├── tofino.py └── test_tofino.py ├── p4runtime-sh-docker ├── LICENSE └── README.md /p4runtime_sh/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /p4runtime_sh/testdata/unittest.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = *test* 3 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 100 3 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | flake8==3.8.4 2 | nose2==0.9.2 3 | callee==0.3.1 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | .coverage 4 | venv 5 | build 6 | dist 7 | *.egg-info 8 | .eggs 9 | .vscode 10 | gh-md-toc 11 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.pyc 3 | .coverage 4 | venv 5 | build 6 | dist 7 | *.egg-info 8 | .eggs 9 | .vscode 10 | gh-md-toc 11 | Dockerfile* 12 | -------------------------------------------------------------------------------- /unittest.cfg: -------------------------------------------------------------------------------- 1 | [coverage] 2 | always-on = False 3 | coverage = config_builders 4 | p4runtime_sh 5 | coverage-config = .coveragerc 6 | coverage-report = 7 | -------------------------------------------------------------------------------- /p4runtime_sh/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Yi Tseng 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | from p4runtime_sh.shell import main 5 | main() # pragma: no cover 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Yi Tseng 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | import setuptools 5 | 6 | if __name__ == "__main__": 7 | setuptools.setup() 8 | -------------------------------------------------------------------------------- /docker_entry_point.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 Antonin Bas 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | source $VENV/bin/activate 7 | python3 -m p4runtime_sh "$@" 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | # Workflow files stored in the default location of `.github/workflows` 5 | directory: "/" 6 | schedule: 7 | interval: "weekly" 8 | open-pull-requests-limit: 5 9 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools", 4 | "setuptools_scm[toml]", 5 | "setuptools_scm_git_archive", 6 | "wheel", 7 | ] 8 | build-backend = 'setuptools.build_meta' 9 | 10 | [tool.setuptools_scm] 11 | root = "./" 12 | # use current tag and not next one 13 | version_scheme = "post-release" 14 | -------------------------------------------------------------------------------- /.github/workflows/toc.yml: -------------------------------------------------------------------------------- 1 | name: TOC 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | toc: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check-out code 15 | uses: actions/checkout@v5 16 | - name: Update TOCs 17 | run: make toc 18 | - name: Check for changes 19 | run: | 20 | [ -z "$(git status --untracked-files=no --porcelain)" ] 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Antonin Bas 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | all: build 5 | 6 | .PHONY: build 7 | build: 8 | docker build -t p4lang/p4runtime-sh . 9 | 10 | .PHONY: toc 11 | toc: 12 | @curl -s https://raw.githubusercontent.com/ekalinin/github-markdown-toc/master/gh-md-toc -o gh-md-toc 13 | @chmod +x gh-md-toc 14 | @./gh-md-toc --insert --no-backup --hide-footer README.md 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf gh-md-toc 19 | -------------------------------------------------------------------------------- /p4runtime-sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2019 Barefoot Networks, Inc. 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | import p4runtime_sh.shell as sh 20 | sh.main() 21 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Antonin Bas 2 | # SPDX-License-Identifier: Apache-2.0 3 | ARG BASE_IMAGE=p4lang/p4runtime-sh:latest 4 | FROM ${BASE_IMAGE} 5 | 6 | SHELL ["/bin/bash", "-c"] 7 | 8 | WORKDIR /p4runtime-sh/ 9 | 10 | ENV PKG_DEPS curl git python3-venv 11 | 12 | # git is required for codecov 13 | RUN apt-get update && \ 14 | apt-get install -y --no-install-recommends $PKG_DEPS && \ 15 | rm -rf /var/cache/apt/* /var/lib/apt/lists/* 16 | 17 | RUN curl -Os https://uploader.codecov.io/latest/linux/codecov && \ 18 | chmod +x codecov && \ 19 | mv codecov /usr/local/bin/ 20 | 21 | COPY . /p4runtime-sh 22 | RUN source $VENV/bin/activate && \ 23 | pip3 install --upgrade pip && \ 24 | pip3 install --upgrade setuptools && \ 25 | pip3 install -r requirements-dev.txt && \ 26 | rm -rf ~/.cache/pip 27 | 28 | ENTRYPOINT [] 29 | -------------------------------------------------------------------------------- /p4runtime_sh/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Antonin Bas 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # See https://stackoverflow.com/a/32997046 5 | def my_partialmethod(func, *args1, **kwargs1): 6 | def method(self, *args2, **kwargs2): 7 | return func(self, *args1, *args2, **kwargs1, **kwargs2) 8 | return method 9 | 10 | 11 | class UserError(Exception): 12 | def __init__(self, info=""): 13 | self.info = info 14 | 15 | def __str__(self): 16 | return self.info 17 | 18 | # TODO(antonin): is this the best way to get a custom traceback? 19 | def _render_traceback_(self): 20 | return [str(self)] 21 | 22 | 23 | class InvalidP4InfoError(Exception): 24 | def __init__(self, info=""): 25 | self.info = info 26 | 27 | def __str__(self): 28 | return "Invalid P4Info message: {}".format(self.info) 29 | 30 | def _render_traceback_(self): 31 | return [str(self)] 32 | -------------------------------------------------------------------------------- /usage/oneshots.md: -------------------------------------------------------------------------------- 1 | ```python 2 | *** Welcome to the IPython shell for P4Runtime *** 3 | P4Runtime sh >>> t = table_entry["FabricIngress.next.hashed"] 4 | 5 | P4Runtime sh >>> a = Action("nop") 6 | 7 | P4Runtime sh >>> t.oneshot.add(a) 8 | Out[3]: 9 | action_profile_actions { 10 | action { 11 | action_id: 16819938 12 | } 13 | weight: 1 14 | } 15 | 16 | 17 | P4Runtime sh >>> t.match["next_id"] = "10" 18 | field_id: 1 19 | exact { 20 | value: "\000\000\000\n" 21 | } 22 | 23 | 24 | P4Runtime sh >>> t.insert 25 | 26 | P4Runtime sh >>> t.read(lambda e: print(e)) 27 | table_id: 33608588 ("FabricIngress.next.hashed") 28 | match { 29 | field_id: 1 ("next_id") 30 | exact { 31 | value: "\\x00\\x00\\x00\\x0a" 32 | } 33 | } 34 | action { 35 | action_profile_action_set { 36 | action_profile_actions { 37 | action { 38 | action_id: 16819938 ("nop") 39 | } 40 | weight: 1 41 | } 42 | } 43 | } 44 | 45 | 46 | P4Runtime sh >>> 47 | ``` 48 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish releases to Pypi 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | publish-to-pypi: 10 | name: Publish a Python distribution to PyPI 11 | if: ${{ github.repository == 'p4lang/p4runtime-shell' }} 12 | runs-on: [ubuntu-latest] 13 | steps: 14 | - uses: actions/checkout@v5 15 | - name: Set up Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: '3.x' 19 | - name: Install pypa/build 20 | run: >- 21 | python -m 22 | pip install 23 | build 24 | --user 25 | - name: Build a binary wheel and a source tarball 26 | run: >- 27 | python -m 28 | build 29 | --sdist 30 | --wheel 31 | --outdir dist/ 32 | . 33 | - name: Publish distribution to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | with: 36 | user: __token__ 37 | password: ${{ secrets.PYPI_API_TOKEN }} 38 | skip_existing: true 39 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = p4runtime-shell 3 | description = The P4Runtime shell 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown; charset=UTF-8 6 | url = https://github.com/p4lang/p4runtime-shell 7 | author = P4 API Working Group 8 | author_email = p4-api@lists.p4.org 9 | license = Apache-2.0 10 | license_files = LICENSE 11 | classifiers = 12 | License :: OSI Approved :: Apache Software License 13 | Programming Language :: Python 14 | Programming Language :: Python :: 3 15 | 16 | [options] 17 | packages = p4runtime_sh 18 | platforms = any 19 | python_requires = >=3.6 20 | setup_requires = 21 | setuptools_scm 22 | install_requires = 23 | jedi == 0.17.2 24 | ipython >= 7.31.1, == 7.31.*; python_version>='3.7' 25 | ipython >= 7.16.3, == 7.16.*; python_version<'3.7' 26 | # more recent versions of Protobuf need P4Runtime Python files to be regenerated with the correct protoc version 27 | protobuf >= 3.15.0, < 3.21.0 28 | grpcio >= 1.35.0 29 | p4runtime == 1.4.1 30 | -------------------------------------------------------------------------------- /usage/pre.md: -------------------------------------------------------------------------------- 1 | ```python 2 | *** Welcome to the IPython shell for P4Runtime *** 3 | P4Runtime sh >>> mcge = multicast_group_entry(1) 4 | 5 | P4Runtime sh >>> mcge.add(1, 1).add(1, 2).add(2, 3) 6 | Out[2]: 7 | multicast_group_entry { 8 | multicast_group_id: 1 9 | replicas { 10 | egress_port: 1 11 | instance: 1 12 | } 13 | replicas { 14 | egress_port: 1 15 | instance: 2 16 | } 17 | replicas { 18 | egress_port: 2 19 | instance: 3 20 | } 21 | } 22 | 23 | 24 | P4Runtime sh >>> mcge.insert 25 | 26 | P4Runtime sh >>> 27 | ``` 28 | 29 | ```python 30 | *** Welcome to the IPython shell for P4Runtime *** 31 | P4Runtime sh >>> cse = clone_session_entry(1) 32 | 33 | P4Runtime sh >>> cse.add(1, 1).add(1, 2).add(2, 3) 34 | Out[2]: 35 | clone_session_entry { 36 | session_id: 1 37 | replicas { 38 | egress_port: 1 39 | instance: 1 40 | } 41 | replicas { 42 | egress_port: 1 43 | instance: 2 44 | } 45 | replicas { 46 | egress_port: 2 47 | instance: 3 48 | } 49 | } 50 | 51 | 52 | P4Runtime sh >>> cse.insert 53 | 54 | P4Runtime sh >>> 55 | ``` 56 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Antonin Bas 2 | # SPDX-License-Identifier: Apache-2.0 3 | FROM ubuntu:22.04 AS deps 4 | 5 | SHELL ["/bin/bash", "-c"] 6 | ENV PKG_DEPS python3 python3-venv git 7 | ENV VENV /p4runtime-sh/venv 8 | 9 | RUN apt-get update && \ 10 | apt-get install -y --no-install-recommends $PKG_DEPS && \ 11 | rm -rf /var/cache/apt/* /var/lib/apt/lists/* 12 | 13 | COPY . /p4runtime-sh/ 14 | WORKDIR /p4runtime-sh/ 15 | 16 | RUN python3 -m venv $VENV && \ 17 | source $VENV/bin/activate && \ 18 | pip3 install --upgrade pip && \ 19 | pip3 install --upgrade setuptools && \ 20 | pip3 install --upgrade wheel && \ 21 | pip3 install . && \ 22 | rm -rf ~/.cache/pip 23 | 24 | FROM ubuntu:22.04 25 | LABEL maintainer="P4 Developers " 26 | LABEL description="A shell based on ipython3 for P4Runtime" 27 | 28 | # Any easy way to avoid installing these packages again? 29 | ENV PKG_DEPS python3 30 | ENV VENV /p4runtime-sh/venv 31 | 32 | RUN apt-get update && \ 33 | apt-get install -y --no-install-recommends $PKG_DEPS && \ 34 | rm -rf /var/cache/apt/* /var/lib/apt/lists/* 35 | 36 | COPY --from=deps /p4runtime-sh/venv /p4runtime-sh/venv 37 | COPY --from=deps /p4runtime-sh/docker_entry_point.sh /p4runtime-sh/docker_entry_point.sh 38 | 39 | WORKDIR /p4runtime-sh 40 | 41 | ENTRYPOINT ["/p4runtime-sh/docker_entry_point.sh"] 42 | -------------------------------------------------------------------------------- /README-publish-python-package.md: -------------------------------------------------------------------------------- 1 | # Notes on publishing new version of Python package for p4runtime-shell 2 | 3 | Prerequisites: You must have a Github user account that has privileges 4 | to push new tags to this repository. This might only be Github 5 | accounts with administration privileges on the repository. 6 | 7 | Run these commands: 8 | ``` 9 | git clone git@github.com:p4lang/p4runtime-shell 10 | cd p4runtime-shell 11 | git tag -a v -m "Version - " 12 | git push origin v 13 | ``` 14 | 15 | Example for releasing version 0.0.5 with comment "Version 0.0.5 - Add 16 | eburst": 17 | ``` 18 | git clone git@github.com:p4lang/p4runtime-shell 19 | cd p4runtime-shell 20 | git tag -a v0.0.5 -m "Version 0.0.5 - Add eburst" 21 | git push origin v0.0.5 22 | ``` 23 | 24 | To verify whether this successfully triggers creating a new release on 25 | PyPI: 26 | 27 | + Go to https://github.com/p4lang/p4runtime-shell 28 | + Click the "Actions" link near the top of the page. 29 | + Verify that the most recent workflow was started near the time you 30 | did the `git push` command above, that it has the 31 | `v` in the column to the left of the start time, and 32 | to the left of that is a reasonable-looking commit description for 33 | the commit with that version. 34 | + After a few minutes, check the pypi.org page for the p4runtime-shell 35 | package and verify that the new version is the most recent one 36 | mentioned: https://pypi.org/project/p4runtime-shell/ 37 | 38 | If you further wish to test installing this package on a development 39 | system that has the Python3 `venv` virtual environment package 40 | installed: 41 | 42 | ```bash 43 | python3 -m venv $HOME/venv-test-p4runtime-shell-install 44 | source $HOME/venv-test-p4runtime-shell-install/bin/activate 45 | pip list 46 | pip install p4runtime-shell 47 | pip list 48 | ``` 49 | 50 | The `p4runtime-shell` with the new latest version should have been 51 | installed, along with many packages it depends upon. 52 | 53 | Note: The reason that pushing a tag to this repository causes a new 54 | release to be published to pypi.org is because of the Github action 55 | defined in the file 56 | [`.github/workflows/pypi.yml`](.github/workflows/pypi.yml) of this 57 | repository. 58 | -------------------------------------------------------------------------------- /usage/read_table_entry_counter.md: -------------------------------------------------------------------------------- 1 | ## Read `counter_data` for `table_entries` 2 | 3 | This example uses the [basic tutorial for Stratum](https://github.com/stratum/tutorial/tree/master/basic). 4 | The goal is to read out the byte and packet counts for a specific table entry. 5 | 6 | ```python 7 | *** Welcome to the IPython shell for P4Runtime *** 8 | P4Runtime sh >>> ########################## Add table entries 9 | ...: te = table_entry["ingress.table0_control.table0"](action = "ingress.table0_control.set_egress_port") 10 | ...: te.priority = 1 11 | ...: te.match["standard_metadata.ingress_port"] = ("1") 12 | ...: te.action['port'] = ("2") 13 | ...: te.insert() 14 | ...: 15 | ...: te = table_entry["ingress.table0_control.table0"](action = "ingress.table0_control.set_egress_port") 16 | ...: te.priority = 1 17 | ...: te.match["standard_metadata.ingress_port"] = ("2") 18 | ...: te.action['port'] = ("1") 19 | ...: te.insert() 20 | ...: 21 | field_id: 1 22 | ternary { 23 | value: "\000\001" 24 | mask: "\001\377" 25 | } 26 | 27 | param_id: 1 28 | value: "\000\002" 29 | 30 | field_id: 1 31 | ternary { 32 | value: "\000\002" 33 | mask: "\001\377" 34 | } 35 | 36 | param_id: 1 37 | value: "\000\001" 38 | 39 | 40 | P4Runtime sh >>> ########################## Retrieve all table entries and print out counter_data (byte and packet counts) 41 | ...: ########################## (Note: you HAVE to generate traffic on some table entries to see non-zero counters) 42 | ...: 43 | ...: te = table_entry['ingress.table0_control.table0'] 44 | ...: # You HAVE to set the te.counter_data field to trigger reading out the counter_data 45 | ...: te.counter_data.byte_count = 0 46 | ...: for x in te.read(): 47 | ...: if x.counter_data.byte_count == 0: 48 | ...: print('counter_data.byte_count == 0 -> Generate some traffic before reading out the counters') 49 | ...: else: 50 | ...: print('Counter data:') 51 | ...: print(x.counter_data) 52 | ...: 53 | Counter data byte_count: 1022 54 | packet_count: 11 55 | 56 | Counter data byte_count: 1022 57 | packet_count: 11 58 | 59 | 60 | P4Runtime sh >>> 61 | ``` 62 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | # Local registry where we output p4lang/p4runtime-sh so that it can be used 14 | # to build p4lang/p4runtime-sh-dev. 15 | services: 16 | registry: 17 | image: registry:2 18 | ports: 19 | - 5000:5000 20 | steps: 21 | - name: Check-out code 22 | uses: actions/checkout@v5 23 | - name: Set up Docker Buildx 24 | uses: docker/setup-buildx-action@v3 25 | with: 26 | driver-opts: network=host 27 | - name: Login to DockerHub if needed 28 | if: ${{ github.repository == 'p4lang/p4runtime-shell' && github.event_name == 'push' }} 29 | uses: docker/login-action@v3 30 | with: 31 | username: ${{ secrets.DOCKER_USERNAME }} 32 | password: ${{ secrets.DOCKER_PASSWORD }} 33 | - name: Build and push p4lang/p4runtime-sh to local registry 34 | uses: docker/build-push-action@v6 35 | with: 36 | context: . 37 | tags: localhost:5000/p4lang/p4runtime-sh 38 | push: true 39 | cache-from: type=gha,scope=p4runtime-sh 40 | cache-to: type=gha,scope=p4runtime-sh,mode=max 41 | - name: Build and export p4lang/p4runtime-sh-dev to Docker 42 | uses: docker/build-push-action@v6 43 | with: 44 | context: . 45 | file: Dockerfile.dev 46 | build-args: BASE_IMAGE=localhost:5000/p4lang/p4runtime-sh:latest 47 | load: true 48 | tags: p4lang/p4runtime-sh-dev 49 | cache-from: | 50 | type=registry,ref=localhost:5000/p4lang/p4runtime-sh 51 | type=gha,scope=p4runtime-sh-dev 52 | cache-to: type=gha,scope=p4runtime-sh-dev,mode=max 53 | - name: Check Python code formatting 54 | run: docker run p4lang/p4runtime-sh-dev bash -c "source venv/bin/activate && flake8 p4runtime_sh config_builders" 55 | - name: Run unit tests and upload code coverage 56 | run: | 57 | ci_env=`bash <(curl -s https://codecov.io/env)` 58 | docker run $ci_env p4lang/p4runtime-sh-dev bash -c "source venv/bin/activate && nose2 --with-coverage p4runtime_sh config_builders && codecov" 59 | - name: Build and push p4lang/p4runtime-sh to Dockerhub if needed 60 | if: ${{ github.repository == 'p4lang/p4runtime-shell' && github.event_name == 'push' }} 61 | uses: docker/build-push-action@v6 62 | with: 63 | context: . 64 | push: true 65 | tags: p4lang/p4runtime-sh 66 | -------------------------------------------------------------------------------- /usage/packet_io.md: -------------------------------------------------------------------------------- 1 | # Packet IO 2 | 3 | ## Prepare P4 program with packet IO support 4 | 5 | To use packet IO, first we need to define packet IO header in the P4 program, for example: 6 | 7 | ```p4 8 | @controller_header("packet_out") 9 | header packet_out_header_t { 10 | bit<16> egress_port; 11 | } 12 | @controller_header("packet_in") 13 | header packet_out_header_t { 14 | bit<16> ingress_port; 15 | } 16 | ``` 17 | 18 | Once compiled, we can get the following part in the p4info file which describes 19 | the packet IO header: 20 | 21 | ```protobuf 22 | controller_packet_metadata { 23 | preamble { 24 | id: 1 25 | name: "packet_out" 26 | alias: "packet_out" 27 | annotations: "@controller_header(\"packet_out\")" 28 | } 29 | metadata { 30 | id: 1 31 | name: "egress_port" 32 | bitwidth: 16 33 | } 34 | } 35 | controller_packet_metadata { 36 | preamble { 37 | id: 2 38 | name: "packet_in" 39 | alias: "packet_in" 40 | annotations: "@controller_header(\"packet_in\")" 41 | } 42 | metadata { 43 | id: 1 44 | name: "ingress_port" 45 | bitwidth: 16 46 | } 47 | } 48 | ``` 49 | 50 | ## Send a packet-out message 51 | 52 | To send a packet-out message, use following commands: 53 | 54 | ```python 55 | P4Runtime sh >>> p = packet_out() 56 | P4Runtime sh >>> p.payload = b'AAAA' # Note that the payload must be a byte string 57 | P4Runtime sh >>> p.metadata['egress_port'] = '1' # Note that the value must be a string 58 | P4Runtime sh >>> p 59 | Out[1]: 60 | payload: "\\x41\\x41\\x41\\x41" 61 | metadata { 62 | metadata_id: 1 ("egress_port") 63 | value: "\\x00\\x01" 64 | } 65 | P4Runtime sh >>> p.send # send the packet-out message 66 | 67 | # Another way to create a packet_out object 68 | P4Runtime sh >>> p = packet_out(payload=b'AAAA', egress_port='1') 69 | ``` 70 | 71 | ## Receive packet-in messages 72 | 73 | The `sniff` function will return an iterator which contains packet-in messages when: 74 | 75 | - The timeout expires (based on the `timeout` parameter) 76 | - A keyboard interrupt occurs (Ctrl + C) 77 | 78 | ```python 79 | # To print all packet-in messages. 80 | P4Runtime sh >>> for msg in packet_in.sniff(timeout=1): 81 | ...: print(msg) 82 | 83 | 84 | # Prints packet-in messages by using the custom function. 85 | P4Runtime sh >>> packet_in.sniff(lambda m: print(m), timeout=1) 86 | Out[2]: 87 | payload: "AAAA" 88 | metadata { 89 | metadata_id: 1 90 | value: "\000\001" 91 | } 92 | 93 | # By setting timeout to `None`, it will wait until user sends a keyboard 94 | # interrupt(Ctrl + C) to the shell. 95 | P4Runtime sh >>> packet_in.sniff(lambda m: print(m), timeout=None) 96 | ``` 97 | -------------------------------------------------------------------------------- /config_builders/tofino.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2019 Barefoot Networks, Inc. 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | import argparse 20 | import os 21 | import struct 22 | import sys 23 | 24 | 25 | # TODO: update to use manifest 26 | def get_arg_parser(): 27 | parser = argparse.ArgumentParser(description='Tofino binary config builder') 28 | parser.add_argument('--ctx-json', 29 | help='Path to context JSON file', 30 | required=True, 31 | action='store') 32 | parser.add_argument('--tofino-bin', 33 | help='Path to Tofino BIN file', 34 | required=True, 35 | action='store') 36 | parser.add_argument('--out', '-o', 37 | help='Destination binary file', 38 | required=True, 39 | action='store') 40 | parser.add_argument('--name', '-p', 41 | help='P4 Program name', 42 | required=True, 43 | action='store') 44 | return parser 45 | 46 | 47 | def build_config(prog_name, ctx_json_path, tofino_bin_path, out_path): 48 | # we open the context JSON file in binary mode so that no encoding step is required 49 | with open(ctx_json_path, 'rb') as ctx_json_f, \ 50 | open(tofino_bin_path, 'rb') as bin_f, \ 51 | open(out_path, 'wb') as out_f: 52 | prog_name_bytes = prog_name.encode() 53 | out_f.write(struct.pack(""] =