├── .gitignore
├── LICENSE.md
├── README.md
├── docs
├── .buildinfo
├── .nojekyll
├── _images
│ └── l2_topology.png
├── _static
│ ├── ajax-loader.gif
│ ├── basic.css
│ ├── comment-bright.png
│ ├── comment-close.png
│ ├── comment.png
│ ├── css
│ │ ├── badge_only.css
│ │ ├── fonts
│ │ │ ├── Roboto-Slab-Bold.woff
│ │ │ ├── Roboto-Slab-Bold.woff2
│ │ │ ├── Roboto-Slab-Regular.woff
│ │ │ ├── Roboto-Slab-Regular.woff2
│ │ │ ├── fontawesome-webfont.eot
│ │ │ ├── fontawesome-webfont.svg
│ │ │ ├── fontawesome-webfont.ttf
│ │ │ ├── fontawesome-webfont.woff
│ │ │ ├── fontawesome-webfont.woff2
│ │ │ ├── lato-bold-italic.woff
│ │ │ ├── lato-bold-italic.woff2
│ │ │ ├── lato-bold.woff
│ │ │ ├── lato-bold.woff2
│ │ │ ├── lato-normal-italic.woff
│ │ │ ├── lato-normal-italic.woff2
│ │ │ ├── lato-normal.woff
│ │ │ └── lato-normal.woff2
│ │ └── theme.css
│ ├── doctools.js
│ ├── documentation_options.js
│ ├── down-pressed.png
│ ├── down.png
│ ├── file.png
│ ├── jquery.js
│ ├── js
│ │ ├── badge_only.js
│ │ ├── html5shiv-printshiv.min.js
│ │ ├── html5shiv.min.js
│ │ └── theme.js
│ ├── language_data.js
│ ├── minus.png
│ ├── plus.png
│ ├── pygments.css
│ ├── searchtools.js
│ ├── underscore.js
│ ├── up-pressed.png
│ ├── up.png
│ └── websupport.js
├── advanced_usage.html
├── genindex.html
├── index.html
├── installation.html
├── introduction.html
├── objects.inv
├── p4utils.html
├── p4utils.mininetlib.cli.html
├── p4utils.mininetlib.html
├── p4utils.mininetlib.log.html
├── p4utils.mininetlib.net.html
├── p4utils.mininetlib.network_API.html
├── p4utils.mininetlib.node.html
├── p4utils.p4run.html
├── p4utils.utils.client.html
├── p4utils.utils.compiler.html
├── p4utils.utils.helper.html
├── p4utils.utils.html
├── p4utils.utils.monitor.html
├── p4utils.utils.p4runtime_API.api.html
├── p4utils.utils.p4runtime_API.bytes_utils.html
├── p4utils.utils.p4runtime_API.context.html
├── p4utils.utils.p4runtime_API.html
├── p4utils.utils.p4runtime_API.p4runtime.html
├── p4utils.utils.p4runtime_API.utils.html
├── p4utils.utils.sswitch_p4runtime_API.html
├── p4utils.utils.sswitch_thrift_API.html
├── p4utils.utils.task_scheduler.html
├── p4utils.utils.thrift_API.html
├── p4utils.utils.topology.html
├── p4utils.utils.traffic_utils.html
├── py-modindex.html
├── search.html
├── searchindex.js
└── usage.html
├── docsrc
├── Makefile
├── _images
│ └── l2_topology.png
├── advanced_usage.rst
├── conf.py
├── index.rst
├── installation.rst
├── introduction.rst
├── p4utils.mininetlib.cli.rst
├── p4utils.mininetlib.log.rst
├── p4utils.mininetlib.net.rst
├── p4utils.mininetlib.network_API.rst
├── p4utils.mininetlib.node.rst
├── p4utils.mininetlib.rst
├── p4utils.p4run.rst
├── p4utils.rst
├── p4utils.utils.client.rst
├── p4utils.utils.compiler.rst
├── p4utils.utils.helper.rst
├── p4utils.utils.monitor.rst
├── p4utils.utils.p4runtime_API.api.rst
├── p4utils.utils.p4runtime_API.bytes_utils.rst
├── p4utils.utils.p4runtime_API.context.rst
├── p4utils.utils.p4runtime_API.p4runtime.rst
├── p4utils.utils.p4runtime_API.rst
├── p4utils.utils.p4runtime_API.utils.rst
├── p4utils.utils.rst
├── p4utils.utils.sswitch_p4runtime_API.rst
├── p4utils.utils.sswitch_thrift_API.rst
├── p4utils.utils.task_scheduler.rst
├── p4utils.utils.thrift_API.rst
├── p4utils.utils.topology.rst
├── p4utils.utils.traffic_utils.rst
└── usage.rst
├── examples
├── README.md
├── adv-net
│ ├── controller.py
│ ├── default-2.traffic
│ ├── default-3.traffic
│ ├── default.traffic
│ ├── network.py
│ ├── performance.py
│ ├── routers
│ │ ├── r1.conf
│ │ ├── r2.conf
│ │ ├── r3.conf
│ │ └── r4.conf
│ ├── switch.p4
│ └── udp.py
├── frrouters
│ ├── README.md
│ ├── images
│ │ └── frr_example_topo.png
│ ├── l2_learning_controller.py
│ ├── l2_learning_digest.p4
│ ├── network.py
│ ├── p4app.json
│ ├── receive.py
│ ├── routers
│ │ ├── r1.conf
│ │ ├── r2.conf
│ │ ├── r3.conf
│ │ ├── r4.conf
│ │ └── r5.conf
│ ├── send.py
│ └── tasks.txt
├── switches
│ ├── README.md
│ ├── forwarding.p4
│ ├── network.py
│ ├── p4app.json
│ ├── receive.py
│ ├── s1-commands.txt
│ ├── s2-commands.txt
│ ├── s3-commands.txt
│ ├── send.py
│ └── tasks.txt
└── tofino
│ ├── README.md
│ ├── controller_1.py
│ ├── controller_2.py
│ ├── heavy_hitter.p4
│ ├── network.py
│ ├── receive.py
│ └── send.py
├── install-tools
├── README.md
├── conf_files
│ ├── mininet.patch
│ └── tmux.conf
├── install-p4-dev.sh
├── old_installs
│ └── install-p4-dev.sh
└── scripts
│ ├── protoinitfix.py
│ ├── py3localpath.py
│ └── test_veth_intf.py
├── install.sh
├── p4app_example.json
├── p4utils
├── __init__.py
├── mininetlib
│ ├── __init__.py
│ ├── cli.py
│ ├── log.py
│ ├── net.py
│ ├── network_API.py
│ └── node.py
├── p4run.py
└── utils
│ ├── __init__.py
│ ├── client.py
│ ├── compiler.py
│ ├── helper.py
│ ├── monitor.py
│ ├── p4runtime_API
│ ├── README.md
│ ├── __init__.py
│ ├── api.py
│ ├── bytes_utils.py
│ ├── context.py
│ ├── p4runtime.py
│ └── utils.py
│ ├── sswitch_p4runtime_API.py
│ ├── sswitch_thrift_API.py
│ ├── task_scheduler.py
│ ├── thrift_API.py
│ ├── topology.py
│ └── traffic_utils.py
├── setup.py
├── uninstall.sh
├── utils
├── mx
└── mxexec.c
└── vm
├── README.md
├── build-qemu.sh
├── build-qemu20.sh
├── build-virtualbox.sh
├── http
└── preseed.cfg
├── qemu.pkr.hcl
├── qemu20.pkr.hcl
└── virtualbox.pkr.hcl
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python byte code
2 | *.pyc
3 |
4 | # Emacs
5 | *~
6 | ls
7 |
8 | # Compiled JSON
9 | *.json
10 | !*p4app*.json
11 |
12 | # P4 files
13 | *p4i
14 | *p4rt
15 |
16 | # Logs and dumps
17 | log*/
18 | *.pcap
19 |
20 | *.egg-info/
21 |
22 | *mxexec.1
23 | *mxexec
24 |
25 | flows/
26 | *.csv
27 |
28 | # Docs hidden files
29 | docs/.doctrees
30 |
31 | # Packer auto generated files
32 | vm/packer_cache
33 | vm/output-*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # P4-Utils
2 |
3 | P4-Utils is an extension to *Mininet* that makes P4 networks easier to build, run and debug. P4-utils is strongly
4 | inspired by [p4app](https://github.com/p4lang/p4app). Here we only provide a quick summary of the main information
5 | about P4-Utils. **Check out the [online documentation](https://nsg-ethz.github.io/p4-utils/index.html)
6 | for more details about P4-Utils installation and usage.**
7 |
8 | ## Installation
9 |
10 | In order to work, P4-Utils needs different programs coming from different sources as prerequisites.
11 | Since the installation process can be long and cumbersome, we provide different methods to make the
12 | deployment easier for the users:
13 |
14 | - [virtual machine](#virtual-machine) configured to work with P4-Utils,
15 | - [manual installation](#manual-installation) using an installation script.
16 |
17 | ### Virtual Machine
18 |
19 | P4-Utils can run in a virtual machine to keep its environment separated from the rest of the system.
20 | Moreover, since P4-Utils is only available on Linux, other OS users can run it in a linux VM.
21 |
22 | > Running P4-Utils in a completely separated environment can be beneficial: in this way, installation
23 | > and execution errors, that may arise, will not affect the whole system. For this reason, we **recommend**
24 | > using a virtual machine.
25 |
26 | You can choose to download and use one of our
27 | [preconfigured VMs](https://nsg-ethz.github.io/p4-utils/installation.html#use-our-preconfigured-vm)
28 | or to [build it by yourself](./vm).
29 |
30 | ### Manual Installation
31 |
32 | If you have already installed all the [requirements](#requirements), you can simply
33 | install P4-Utils using the following commands:
34 |
35 | ```bash
36 | git clone https://github.com/nsg-ethz/p4-utils.git
37 | cd p4-utils
38 | sudo ./install.sh
39 | ```
40 |
41 | > **Attention!**
42 | > The install script will use `pip -e` to install the project in editable mode, meaning that every time you update the files
43 | > in this repository, either by pulling or doing local edits, the changes will automatically take place without the need of
44 | > installing the package again.
45 |
46 | If you want to uninstall run:
47 |
48 | ```bash
49 | sudo ./uninstall.sh
50 | ```
51 |
52 | This will remove all the scripts that were added to `/usr/bin` as well as uninstall the python package using `pip`.
53 |
54 | #### Requirements
55 |
56 | P4-Utils depends on the following programs in the given order:
57 |
58 | 1. [PI LIBRARY REPOSITORY](https://github.com/p4lang/PI) **is required only for topologies with
59 | P4Runtime switches**
60 | 2. [BEHAVIORAL MODEL (bmv2)](https://github.com/p4lang/behavioral-model)
61 | 3. [p4c](https://github.com/p4lang/p4c)
62 | 4. [Mininet](https://github.com/mininet/mininet)
63 | 5. [FRRouting](https://github.com/FRRouting/FRR) **is required
64 | only for topologies with routers**
65 |
66 | Since the installation process is long and cumbersome, **we provide a [Bash script](./install-tools)
67 | that automatically installs the dependencies.**
68 |
69 | ### How does it work ?
70 |
71 | P4-Utils creates virtual networks using *Mininet* and extended nodes that run P4-enabled switches. To create hosts,
72 | *Mininet* uses a bash process running in a network namespace, in order words, all the processes that run within the
73 | network namespaces have an isolated network stack. Switches are software-based switches like Open vSwitch, Linux Bridge,
74 | or BMV2 switches. Mininet uses virtual ethernet pairs, which live in the Linux kernel to connect the emulated hosts and switches.
75 |
76 | For more information see:
77 |
78 | - [Mininet](http://mininet.org/)
79 | - [Linux Namespaces](https://blogs.igalia.com/dpino/2016/04/10/network-namespaces/)
80 | - [Virtual ethernet interfaces](http://man7.org/linux/man-pages/man4/veth.4.html)
81 | - [BMV2](https://github.com/p4lang/behavioral-model)
82 | - [p4runtime-shell](https://github.com/p4lang/p4runtime-shell)
83 | - [FRRouting](https://frrouting.org/)
84 | - [OVS](https://www.openvswitch.org/)
85 | - [LinuxBridge](https://cloudbuilder.in/blogs/2013/12/02/linux-bridge-virtual-networking/)
86 |
87 | ### Features
88 |
89 | P4-Utils adds on top of *Mininet*:
90 |
91 | - A command-line launcher (`p4run`) to instantiate networks.
92 | - A helper script (`mx`) to run processes in namespaces.
93 | - Custom `P4Host`, `P4Switch`, `P4RuntimeSwitch`, `FFRouter` nodes (based on [BMV2](https://github.com/p4lang/behavioral-model) and [FRRouting](https://github.com/FRRouting/FRR)).
94 | - A very simple way of defining networks using JSON files (see `p4app_example.json` and [related documentation](https://nsg-ethz.github.io/p4-utils/usage.html#json)).
95 | - A very intuitive programmatic way of defining networks using a Python API (see [related documentation](https://nsg-ethz.github.io/p4-utils/usage.html#python)).
96 | - Enhances *Mininet* command-line interface by adding useful commands to manage P4 switches.
97 | - Saves the topology information in an object that can be loaded and queried (see [related documentation](https://nsg-ethz.github.io/p4-utils/advanced_usage.html#topology-database)).
98 | - Re-implementation of the `runtime_CLI` and `simple_switch_CLI` as Python objects to use in controller code.
99 | - Re-implementation of the [p4runtime-shell](https://github.com/p4lang/p4runtime-shell) as Python objects to use in controller code.
100 |
--------------------------------------------------------------------------------
/docs/.buildinfo:
--------------------------------------------------------------------------------
1 | # Sphinx build info version 1
2 | # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3 | config: 3c53dc7865980dcffb1f8ddcd50488c5
4 | tags: 645f666f9bcd5a90fca523b33c5a78b7
5 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/_images/l2_topology.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_images/l2_topology.png
--------------------------------------------------------------------------------
/docs/_static/ajax-loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/ajax-loader.gif
--------------------------------------------------------------------------------
/docs/_static/comment-bright.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/comment-bright.png
--------------------------------------------------------------------------------
/docs/_static/comment-close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/comment-close.png
--------------------------------------------------------------------------------
/docs/_static/comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/comment.png
--------------------------------------------------------------------------------
/docs/_static/css/badge_only.css:
--------------------------------------------------------------------------------
1 | .clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/Roboto-Slab-Bold.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/Roboto-Slab-Bold.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/Roboto-Slab-Regular.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/Roboto-Slab-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/Roboto-Slab-Regular.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-bold-italic.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-bold-italic.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-bold.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-bold.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal-italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-normal-italic.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal-italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-normal-italic.woff2
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-normal.woff
--------------------------------------------------------------------------------
/docs/_static/css/fonts/lato-normal.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/css/fonts/lato-normal.woff2
--------------------------------------------------------------------------------
/docs/_static/documentation_options.js:
--------------------------------------------------------------------------------
1 | var DOCUMENTATION_OPTIONS = {
2 | URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3 | VERSION: '1.0',
4 | LANGUAGE: 'None',
5 | COLLAPSE_INDEX: false,
6 | FILE_SUFFIX: '.html',
7 | HAS_SOURCE: false,
8 | SOURCELINK_SUFFIX: '.txt',
9 | NAVIGATION_WITH_KEYS: false,
10 | };
--------------------------------------------------------------------------------
/docs/_static/down-pressed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/down-pressed.png
--------------------------------------------------------------------------------
/docs/_static/down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/down.png
--------------------------------------------------------------------------------
/docs/_static/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/_static/file.png
--------------------------------------------------------------------------------
/docs/_static/js/badge_only.js:
--------------------------------------------------------------------------------
1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
--------------------------------------------------------------------------------
/docs/_static/js/html5shiv-printshiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=y.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=y.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),y.elements=c+" "+a,j(b)}function f(a){var b=x[a[v]];return b||(b={},w++,a[v]=w,x[w]=b),b}function g(a,c,d){if(c||(c=b),q)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():u.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||t.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),q)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return y.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(y,b.frag)}function j(a){a||(a=b);var d=f(a);return!y.shivCSS||p||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),q||i(a,d),a}function k(a){for(var b,c=a.getElementsByTagName("*"),e=c.length,f=RegExp("^(?:"+d().join("|")+")$","i"),g=[];e--;)b=c[e],f.test(b.nodeName)&&g.push(b.applyElement(l(b)));return g}function l(a){for(var b,c=a.attributes,d=c.length,e=a.ownerDocument.createElement(A+":"+a.nodeName);d--;)b=c[d],b.specified&&e.setAttribute(b.nodeName,b.nodeValue);return e.style.cssText=a.style.cssText,e}function m(a){for(var b,c=a.split("{"),e=c.length,f=RegExp("(^|[\\s,>+~])("+d().join("|")+")(?=[[\\s,>+~#.:]|$)","gi"),g="$1"+A+"\\:$2";e--;)b=c[e]=c[e].split("}"),b[b.length-1]=b[b.length-1].replace(f,g),c[e]=b.join("}");return c.join("{")}function n(a){for(var b=a.length;b--;)a[b].removeNode()}function o(a){function b(){clearTimeout(g._removeSheetTimer),d&&d.removeNode(!0),d=null}var d,e,g=f(a),h=a.namespaces,i=a.parentWindow;return!B||a.printShived?a:("undefined"==typeof h[A]&&h.add(A),i.attachEvent("onbeforeprint",function(){b();for(var f,g,h,i=a.styleSheets,j=[],l=i.length,n=Array(l);l--;)n[l]=i[l];for(;h=n.pop();)if(!h.disabled&&z.test(h.media)){try{f=h.imports,g=f.length}catch(o){g=0}for(l=0;g>l;l++)n.push(f[l]);try{j.push(h.cssText)}catch(o){}}j=m(j.reverse().join("")),e=k(a),d=c(a,j)}),i.attachEvent("onafterprint",function(){n(e),clearTimeout(g._removeSheetTimer),g._removeSheetTimer=setTimeout(b,500)}),a.printShived=!0,a)}var p,q,r="3.7.3",s=a.html5||{},t=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,u=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,v="_html5shiv",w=0,x={};!function(){try{var a=b.createElement("a");a.innerHTML=" ",p="hidden"in a,q=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){p=!0,q=!0}}();var y={elements:s.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:r,shivCSS:s.shivCSS!==!1,supportsUnknownElements:q,shivMethods:s.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=y,j(b);var z=/^$|\b(?:all|print)\b/,A="html5shiv",B=!q&&function(){var c=b.documentElement;return!("undefined"==typeof b.namespaces||"undefined"==typeof b.parentWindow||"undefined"==typeof c.applyElement||"undefined"==typeof c.removeNode||"undefined"==typeof a.attachEvent)}();y.type+=" print",y.shivPrint=o,o(b),"object"==typeof module&&module.exports&&(module.exports=y)}("undefined"!=typeof window?window:this,document);
--------------------------------------------------------------------------------
/docs/_static/js/html5shiv.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @preserve HTML5 Shiv 3.7.3 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
3 | */
4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML=" ",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
--------------------------------------------------------------------------------
/docs/_static/js/theme.js:
--------------------------------------------------------------------------------
1 | !function(n){var e={};function t(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return n[i].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=n,t.c=e,t.d=function(n,e,i){t.o(n,e)||Object.defineProperty(n,e,{enumerable:!0,get:i})},t.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},t.t=function(n,e){if(1&e&&(n=t(n)),8&e)return n;if(4&e&&"object"==typeof n&&n&&n.__esModule)return n;var i=Object.create(null);if(t.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:n}),2&e&&"string"!=typeof n)for(var o in n)t.d(i,o,function(e){return n[e]}.bind(null,o));return i},t.n=function(n){var e=n&&n.__esModule?function(){return n.default}:function(){return n};return t.d(e,"a",e),e},t.o=function(n,e){return Object.prototype.hasOwnProperty.call(n,e)},t.p="",t(t.s=0)}([function(n,e,t){t(1),n.exports=t(3)},function(n,e,t){(function(){var e="undefined"!=typeof window?window.jQuery:t(2);n.exports.ThemeNav={navBar:null,win:null,winScroll:!1,winResize:!1,linkScroll:!1,winPosition:0,winHeight:null,docHeight:null,isRunning:!1,enable:function(n){var t=this;void 0===n&&(n=!0),t.isRunning||(t.isRunning=!0,e((function(e){t.init(e),t.reset(),t.win.on("hashchange",t.reset),n&&t.win.on("scroll",(function(){t.linkScroll||t.winScroll||(t.winScroll=!0,requestAnimationFrame((function(){t.onScroll()})))})),t.win.on("resize",(function(){t.winResize||(t.winResize=!0,requestAnimationFrame((function(){t.onResize()})))})),t.onResize()})))},enableSticky:function(){this.enable(!0)},init:function(n){n(document);var e=this;this.navBar=n("div.wy-side-scroll:first"),this.win=n(window),n(document).on("click","[data-toggle='wy-nav-top']",(function(){n("[data-toggle='wy-nav-shift']").toggleClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift")})).on("click",".wy-menu-vertical .current ul li a",(function(){var t=n(this);n("[data-toggle='wy-nav-shift']").removeClass("shift"),n("[data-toggle='rst-versions']").toggleClass("shift"),e.toggleCurrent(t),e.hashChange()})).on("click","[data-toggle='rst-current-version']",(function(){n("[data-toggle='rst-versions']").toggleClass("shift-up")})),n("table.docutils:not(.field-list,.footnote,.citation)").wrap("
"),n("table.docutils.footnote").wrap(""),n("table.docutils.citation").wrap("
"),n(".wy-menu-vertical ul").not(".simple").siblings("a").each((function(){var t=n(this);expand=n(' '),expand.on("click",(function(n){return e.toggleCurrent(t),n.stopPropagation(),!1})),t.prepend(expand)}))},reset:function(){var n=encodeURI(window.location.hash)||"#";try{var e=$(".wy-menu-vertical"),t=e.find('[href="'+n+'"]');if(0===t.length){var i=$('.document [id="'+n.substring(1)+'"]').closest("div.section");0===(t=e.find('[href="#'+i.attr("id")+'"]')).length&&(t=e.find('[href="#"]'))}if(t.length>0){$(".wy-menu-vertical .current").removeClass("current").attr("aria-expanded","false"),t.addClass("current").attr("aria-expanded","true"),t.closest("li.toctree-l1").parent().addClass("current").attr("aria-expanded","true");for(let n=1;n<=10;n++)t.closest("li.toctree-l"+n).addClass("current").attr("aria-expanded","true");t[0].scrollIntoView()}}catch(n){console.log("Error expanding nav for anchor",n)}},onScroll:function(){this.winScroll=!1;var n=this.win.scrollTop(),e=n+this.winHeight,t=this.navBar.scrollTop()+(n-this.winPosition);n<0||e>this.docHeight||(this.navBar.scrollTop(t),this.winPosition=n)},onResize:function(){this.winResize=!1,this.winHeight=this.win.height(),this.docHeight=$(document).height()},hashChange:function(){this.linkScroll=!0,this.win.one("hashchange",(function(){this.linkScroll=!1}))},toggleCurrent:function(n){var e=n.closest("li");e.siblings("li.current").removeClass("current").attr("aria-expanded","false"),e.siblings().find("li.current").removeClass("current").attr("aria-expanded","false");var t=e.find("> ul li");t.length&&(t.removeClass("current").attr("aria-expanded","false"),e.toggleClass("current").attr("aria-expanded",(function(n,e){return"true"==e?"false":"true"})))}},"undefined"!=typeof window&&(window.SphinxRtdTheme={Navigation:n.exports.ThemeNav,StickyNav:n.exports.ThemeNav}),function(){for(var n=0,e=["ms","moz","webkit","o"],t=0;t
2 |
3 |
4 |
5 |
6 | Introduction — P4-Utils 1.0 documentation
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
64 |
65 |
66 |
67 |
68 | P4-Utils
69 |
70 |
71 |
72 |
73 |
83 |
84 |
85 |
86 |
87 |
Introduction
88 |
89 |
About P4-Utils
90 |
P4-Utils is a Python package that allows the user to create and test virtual networks
91 | that can include P4 switches. The network creation capabilities are inherited from Mininet ,
92 | whereas the P4 targets are taken from the behavioral-model .
93 |
The behavioral-model is a collection of P4 software switches. It is meant to be used as a
94 | tool for developing, testing and debugging P4 data planes and control plane software
95 | written for them. Indeed, P4 programmable hardware switches are still expensive
96 | and operate them might still be somehow cumbersome.
97 |
Mininet , on the other hand, is a very powerful network emulation framework. Indeed, it can
98 | efficiently virtualize nodes (hosts and switches) in a network by exploiting Linux kernel
99 | features. This allows P4-Utils to create a realistic environment in which P4 switches can
100 | be connected together and tested.
101 |
102 |
103 |
P4 Language
104 |
P4 is a domain-specific programming language that specifies how data plane devices
105 | process packets. The key factor that makes it a very useful tool is that it has been
106 | designed to be target-independent (i.e. it can be used with a wide range of both
107 | hardware-based and software-based architecture) and protocol-independent (i.e. targets
108 | are not bound to any specific network protocol).
109 |
110 |
111 |
Previous Work
112 |
The application p4app is the ancestor of P4-Utils: the former was created by the P4
113 | community to provide a testing and prototyping platform based on P4 language, whereas the latter
114 | is an adaptation made by the ETH Networked Systems Group to simplify the application use
115 | and have a tool for P4 teaching.
116 |
117 |
118 |
119 |
120 |
121 |
122 |
139 |
140 |
141 |
142 |
143 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/docs/objects.inv:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docs/objects.inv
--------------------------------------------------------------------------------
/docs/p4utils.mininetlib.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | p4utils.mininetlib package — P4-Utils 1.0 documentation
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
72 |
73 |
74 |
75 |
76 | P4-Utils
77 |
78 |
79 |
80 |
81 |
92 |
93 |
94 |
95 |
96 |
p4utils.mininetlib package
97 |
98 |
Submodules
99 |
108 |
109 |
110 |
Module contents
111 |
112 |
113 |
114 |
115 |
116 |
117 |
134 |
135 |
136 |
137 |
138 |
143 |
144 |
145 |
--------------------------------------------------------------------------------
/docs/p4utils.utils.p4runtime_API.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | p4utils.utils.p4runtime_API package — P4-Utils 1.0 documentation
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
73 |
74 |
75 |
76 |
77 | P4-Utils
78 |
79 |
80 |
81 |
82 |
94 |
95 |
96 |
97 |
98 |
p4utils.utils.p4runtime_API package
99 |
100 |
Submodules
101 |
110 |
111 |
112 |
Module contents
113 |
114 |
115 |
116 |
117 |
118 |
119 |
136 |
137 |
138 |
139 |
140 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/docs/search.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Search — P4-Utils 1.0 documentation
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
60 |
61 |
62 |
63 |
64 | P4-Utils
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | Search
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Please activate JavaScript to enable the search functionality.
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
110 |
111 |
112 |
113 |
114 |
119 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/docsrc/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SPHINXPROJ = P4-Utils
8 | SOURCEDIR = .
9 | BUILDDIR = ../docs
10 | NUM_CPUS = `grep -c ^processor /proc/cpuinfo`
11 |
12 | # Put it first so that "make" without argument is like "make help".
13 | help:
14 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
15 |
16 | .PHONY: help Makefile
17 |
18 | # Catch html target
19 | html:
20 | @$(SPHINXBUILD) -E -j $(NUM_CPUS) -b html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
22 | # Catch-all target: route all unknown targets to Sphinx using the new
23 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
24 | %: Makefile
25 | @$(SPHINXBUILD) -j $(NUM_CPUS) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docsrc/_images/l2_topology.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/docsrc/_images/l2_topology.png
--------------------------------------------------------------------------------
/docsrc/index.rst:
--------------------------------------------------------------------------------
1 | .. P4-Utils documentation master file, created by
2 | sphinx-quickstart on Wed Aug 11 09:01:10 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to P4-Utils's documentation!
7 | ====================================
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 | :caption: General Documentation
12 |
13 | introduction
14 | installation
15 | usage
16 | advanced_usage
17 |
18 | .. toctree::
19 | :maxdepth: 2
20 | :caption: API Reference
21 |
22 | p4utils
23 |
24 |
25 | Indices and tables
26 | ==================
27 |
28 | * :ref:`genindex`
29 | * :ref:`modindex`
30 | * :ref:`search`
31 |
--------------------------------------------------------------------------------
/docsrc/installation.rst:
--------------------------------------------------------------------------------
1 | Installation
2 | ============
3 |
4 | In order to work, P4-Utils needs different programs coming from different sources as prerequisites.
5 | Since the installation process can be long and cumbersome, we provide different methods to make the
6 | deployment easier for the users:
7 |
8 | - __ #virtual-machine
9 |
10 | `virtual machine`__ configured to work with P4-Utils,
11 | - __ #manual-installation
12 |
13 | `manual installation`__ using an installation script.
14 |
15 | __ #virtual-machine
16 |
17 | .. Note::
18 | Running P4-Utils in a completely separated environment can be beneficial: in this way, installation
19 | and execution errors, that may arise, will not affect the whole system. For this reason, we **recommend**
20 | using a `virtual machine`__.
21 |
22 | Virtual Machine
23 | ---------------
24 |
25 | .. _VirtualBox: https://www.virtualbox.org/
26 |
27 | .. _QEMU: https://www.qemu.org/
28 |
29 | P4-Utils can run in a virtual machine to keep its environment separated from the rest of the system.
30 | Moreover, since P4-Utils is only available on Linux, other OS users can run it in a linux VM.
31 | We provide two different solutions for the P4-Utils VM and both are supported by a wide range of
32 | operating systems:
33 |
34 | - VirtualBox_
35 | - QEMU_
36 |
37 | __ #use-our-preconfigured-vm
38 | __ #build-your-own-vm
39 |
40 | You can choose to download and use one of our `preconfigured VMs`__ or to `build it by yourself`__.
41 |
42 | .. Important::
43 | Whether you are building your own VM or you are using the preconfigured images, you still
44 | need to install one of the above virtualizer according to your VM choice.
45 |
46 | Build your own VM
47 | +++++++++++++++++
48 |
49 | .. _Packer: https://www.packer.io/
50 |
51 | To get started, you need to install the required software:
52 |
53 | - VirtualBox_ or QEMU_
54 | - Packer_
55 |
56 | .. Note::
57 | Packer is a handy framework designed to automatically build custom VM images.
58 |
59 | Clone the P4-Utils repository::
60 |
61 | git clone https://github.com/nsg-ethz/p4-utils
62 |
63 | Go to the Packer configurations folder::
64 |
65 | cd p4-utils/vm
66 |
67 | If you want to build the *VirtualBox VM*, execute::
68 |
69 | ./build-virtualbox.sh [--cpus 4] [--disk_size 25000] [--memory 4000] [--vm_name p4] [--username p4] [--password p4]
70 |
71 | On the other hand, if you prefer the *QEMU VM*, run::
72 |
73 | ./build-qemu.sh [--cpus 4] [--disk_size 25000] [--memory 4000] [--vm_name p4] [--username p4] [--password p4]
74 |
75 | .. Important::
76 | The default VMs configuration parameters are shown above. If you do not specify anything,
77 | they will be used to build your VM. However, please pass to the scripts the parameters
78 | that best fit your needs. In particular, we have that:
79 |
80 | - ``--cpus`` specifies the **number of cores** to use,
81 | - ``--disk_size`` is the **size of the disk** reserved by the VM in MBytes,
82 | - ``--memory`` is the amount of **RAM** to assign to the VM in MBytes,
83 | - ``--vm_name`` is the **name of the VM**,
84 | - ``--username`` is the **login username**,
85 | - ``--password`` is the **login password**.
86 |
87 | The building process will generate the following files:
88 |
89 | - If you chose the QEMU VM, in ``p4-utils/vm/output-ubuntu18044_qemu`` you will find
90 | a ``.qcow2`` file to use to set up your VM.
91 | - If you chose the VirtualBox VM, in ``p4-utils/vm/output-ubuntu18044_vb`` you will
92 | find an ``.ova`` file to import in the VirtualBox VM manager.
93 |
94 | Use our preconfigured VM
95 | ++++++++++++++++++++++++
96 |
97 | To download our preconfiugred VMs, please click on the folllwing links:
98 |
99 | .. Important::
100 | VM credentials:
101 | ``username:`` **p4**
102 | ``password:`` **p4**
103 |
104 | - __ https://polybox.ethz.ch/index.php/s/QlrfHm7uYw6vISe
105 |
106 | `QEMU VM (Ubuntu 20)`__
107 |
108 | - __ https://polybox.ethz.ch/index.php/s/9orcmetpNxOAhlI
109 |
110 | `Deprecated: QEMU VM (UBuntu 18.04)`__
111 |
112 | - __ #
113 |
114 | `VirtualBox VM (unavailable)`__
115 |
116 |
117 |
118 | Manual Installation
119 | -------------------
120 |
121 | __ #prerequisites
122 |
123 | If you have already installed all the `requirements`__, you can simply
124 | install P4-Utils using the following commands::
125 |
126 | git clone https://github.com/nsg-ethz/p4-utils
127 | cd p4-utils
128 | sudo ./install.sh
129 |
130 | You can also uninstall it by running the command::
131 |
132 | sudo ./uninstall.sh
133 |
134 | Prerequisites
135 | +++++++++++++
136 |
137 | P4-Utils depends on the following programs in the given order:
138 |
139 | 1. __ https://github.com/p4lang/PI
140 |
141 | `PI LIBRARY REPOSITORY`__ provides an implementation framework
142 | for a P4Runtime server. **It is required only for topologies with
143 | P4Runtime switches.**
144 | 2. __ https://github.com/p4lang/behavioral-model
145 |
146 | `BEHAVIORAL MODEL (bmv2)`__ contains the software implementation several
147 | variations of the behavioral model (e.g. ``simple_switch`` and
148 | ``simple_switch_grpc``).
149 | 3. __ https://github.com/p4lang/p4c
150 |
151 | `p4c`__ is a reference compiler for the P4 programming language that
152 | supports both **P4_14** and **P4_16**.
153 | 4. __ https://github.com/mininet/mininet
154 |
155 | `Mininet`__ allows to create a realistic virtual network, running real
156 | kernel, switch and application code, on a single machine (VM, cloud or native).
157 | 5. __ https://github.com/FRRouting/FRR
158 |
159 | `FRRouting`__ is a free and open source Internet routing protocol suite
160 | for Linux and Unix platforms. It implements BGP, OSPF, RIP, IS-IS, PIM,
161 | LDP, BFD, Babel, PBR, OpenFabric and VRRP, with alpha support for EIGRP
162 | and NHRP. Router nodes in P4-Utils are based on FRRouting. **It is required
163 | only for topologies with routers.**
164 |
165 | __ https://github.com/nsg-ethz/p4-utils/blob/master/install-tools/install-p4-dev.sh
166 |
167 | The manual installation process is quite long and cumbersome because of the
168 | dependencies that are needed by P4-Utils. For this reason, we provide a `Bash
169 | script`__ that automatically goes through every step.
170 |
171 | .. Warning::
172 | The script has been tested with **Ubuntu 20.04 and Ubuntu 22.04** and the compiler
173 | **GCC 9.4**.
174 |
175 | .. Important::
176 | With the following installation methods, you will download and install *Mininet*
177 | and the P4-Tools suite (P4-Utils, P4-Learning and their dependencies) in your
178 | user's home directory.
179 |
180 | One-Step Automated Install
181 | __________________________
182 |
183 | To get started quickly and conveniently, you may want to install the P4-Tools suite
184 | using the following command::
185 |
186 | curl -sSL https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev.sh | bash
187 |
188 | Alternative Installation Method
189 | _______________________________
190 |
191 | The main drawback of piping to `bash` is that you cannot review the code
192 | that is going to run on your system. Therefore, we provide this alternative
193 | methods that allows you to inspect the intallation script::
194 |
195 | wget -O install-p4-dev.sh https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev.sh
196 | bash install-p4-dev.sh
197 |
--------------------------------------------------------------------------------
/docsrc/introduction.rst:
--------------------------------------------------------------------------------
1 | Introduction
2 | ============
3 |
4 | About P4-Utils
5 | --------------
6 |
7 | .. _Mininet: http://mininet.org/
8 | .. _behavioral-model: https://github.com/p4lang/behavioral-model
9 |
10 | P4-Utils is a Python package that allows the user to create and test virtual networks
11 | that can include P4 switches. The network creation capabilities are inherited from Mininet_,
12 | whereas the P4 targets are taken from the behavioral-model_.
13 |
14 | The *behavioral-model* is a collection of P4 software switches. It is meant to be used as a
15 | tool for developing, testing and debugging P4 data planes and control plane software
16 | written for them. Indeed, P4 programmable hardware switches are still expensive
17 | and operate them might still be somehow cumbersome.
18 |
19 | *Mininet*, on the other hand, is a very powerful network emulation framework. Indeed, it can
20 | efficiently virtualize nodes (hosts and switches) in a network by exploiting Linux kernel
21 | features. This allows P4-Utils to create a realistic environment in which P4 switches can
22 | be connected together and tested.
23 |
24 | P4 Language
25 | -----------
26 |
27 | *P4* is a domain-specific programming language that specifies how data plane devices
28 | process packets. The key factor that makes it a very useful tool is that it has been
29 | designed to be *target-independent* (i.e. it can be used with a wide range of both
30 | hardware-based and software-based architecture) and *protocol-independent* (i.e. targets
31 | are not bound to any specific network protocol).
32 |
33 | Previous Work
34 | -------------
35 |
36 | .. _p4app: https://github.com/p4lang/p4app
37 |
38 | The application p4app_ is the ancestor of P4-Utils: the former was created by the P4
39 | community to provide a testing and prototyping platform based on P4 language, whereas the latter
40 | is an adaptation made by the ETH Networked Systems Group to simplify the application use
41 | and have a tool for P4 teaching.
--------------------------------------------------------------------------------
/docsrc/p4utils.mininetlib.cli.rst:
--------------------------------------------------------------------------------
1 | p4utils\.mininetlib\.cli module
2 | ===============================
3 |
4 | .. automodule:: p4utils.mininetlib.cli
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.mininetlib.log.rst:
--------------------------------------------------------------------------------
1 | p4utils\.mininetlib\.log module
2 | ===============================
3 |
4 | .. automodule:: p4utils.mininetlib.log
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.mininetlib.net.rst:
--------------------------------------------------------------------------------
1 | p4utils\.mininetlib\.net module
2 | ===============================
3 |
4 | .. automodule:: p4utils.mininetlib.net
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.mininetlib.network_API.rst:
--------------------------------------------------------------------------------
1 | p4utils\.mininetlib\.network\_API module
2 | ========================================
3 |
4 | .. automodule:: p4utils.mininetlib.network_API
5 | :members:
6 | :undoc-members:
7 | :exclude-members: cleanup, is_multigraph, save_topology, compile, program_switches,
8 | program_hosts, exec_scripts, start_scheduler, start_schedulers,
9 | distribute_tasks, start_net_cli, module, node_ports, node_intfs,
10 | switch_ids, thrift_ports, grpc_ports, mac_addresses, ip_addresses,
11 | check_host_valid_ip_from_name, intf_name, auto_switch_id, auto_grpc_port,
12 | auto_thrift_port, auto_port_num, auto_mac_address, auto_ip_address,
13 | auto_assignment, get_default_intf, is_default_intf
14 | :show-inheritance:
15 |
--------------------------------------------------------------------------------
/docsrc/p4utils.mininetlib.node.rst:
--------------------------------------------------------------------------------
1 | p4utils\.mininetlib\.node module
2 | ================================
3 |
4 | .. automodule:: p4utils.mininetlib.node
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.mininetlib.rst:
--------------------------------------------------------------------------------
1 | p4utils\.mininetlib package
2 | ===========================
3 |
4 | Submodules
5 | ----------
6 |
7 | .. toctree::
8 |
9 | p4utils.mininetlib.cli
10 | p4utils.mininetlib.log
11 | p4utils.mininetlib.net
12 | p4utils.mininetlib.network_API
13 | p4utils.mininetlib.node
14 |
15 | Module contents
16 | ---------------
17 |
18 | .. automodule:: p4utils.mininetlib
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
--------------------------------------------------------------------------------
/docsrc/p4utils.p4run.rst:
--------------------------------------------------------------------------------
1 | p4utils\.p4run module
2 | =====================
3 |
4 | .. automodule:: p4utils.p4run
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.rst:
--------------------------------------------------------------------------------
1 | P4-Utils API reference
2 | ======================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 | :maxdepth: 3
9 |
10 | p4utils.mininetlib
11 | p4utils.utils
12 |
13 | Submodules
14 | ----------
15 |
16 | .. toctree::
17 | :maxdepth: 3
18 |
19 | p4utils.p4run
20 |
21 | Module contents
22 | ---------------
23 |
24 | .. automodule:: p4utils
25 | :members:
26 | :undoc-members:
27 | :show-inheritance:
28 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.client.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.client module
2 | =============================
3 |
4 | .. automodule:: p4utils.utils.client
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.compiler.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.compiler module
2 | ===============================
3 |
4 | .. automodule:: p4utils.utils.compiler
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.helper.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.helper module
2 | =============================
3 |
4 | .. automodule:: p4utils.utils.helper
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.monitor.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.monitor module
2 | ==============================
3 |
4 | .. automodule:: p4utils.utils.monitor
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.p4runtime_API.api.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.p4runtime\_API\.api module
2 | ==========================================
3 |
4 | .. automodule:: p4utils.utils.p4runtime_API.api
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.p4runtime_API.bytes_utils.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.p4runtime\_API\.bytes\_utils module
2 | ===================================================
3 |
4 | .. automodule:: p4utils.utils.p4runtime_API.bytes_utils
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.p4runtime_API.context.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.p4runtime\_API\.context module
2 | ==============================================
3 |
4 | .. automodule:: p4utils.utils.p4runtime_API.context
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.p4runtime_API.p4runtime.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.p4runtime\_API\.p4runtime module
2 | ================================================
3 |
4 | .. automodule:: p4utils.utils.p4runtime_API.p4runtime
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.p4runtime_API.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.p4runtime\_API package
2 | ======================================
3 |
4 | Submodules
5 | ----------
6 |
7 | .. toctree::
8 |
9 | p4utils.utils.p4runtime_API.api
10 | p4utils.utils.p4runtime_API.bytes_utils
11 | p4utils.utils.p4runtime_API.context
12 | p4utils.utils.p4runtime_API.p4runtime
13 | p4utils.utils.p4runtime_API.utils
14 |
15 | Module contents
16 | ---------------
17 |
18 | .. automodule:: p4utils.utils.p4runtime_API
19 | :members:
20 | :undoc-members:
21 | :show-inheritance:
22 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.p4runtime_API.utils.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.p4runtime\_API\.utils module
2 | ============================================
3 |
4 | .. automodule:: p4utils.utils.p4runtime_API.utils
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils package
2 | ======================
3 |
4 | Subpackages
5 | -----------
6 |
7 | .. toctree::
8 |
9 | p4utils.utils.p4runtime_API
10 |
11 | Submodules
12 | ----------
13 |
14 | .. toctree::
15 |
16 | p4utils.utils.client
17 | p4utils.utils.compiler
18 | p4utils.utils.helper
19 | p4utils.utils.monitor
20 | p4utils.utils.sswitch_p4runtime_API
21 | p4utils.utils.sswitch_thrift_API
22 | p4utils.utils.task_scheduler
23 | p4utils.utils.thrift_API
24 | p4utils.utils.topology
25 | p4utils.utils.traffic_utils
26 |
27 | Module contents
28 | ---------------
29 |
30 | .. automodule:: p4utils.utils
31 | :members:
32 | :undoc-members:
33 | :show-inheritance:
34 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.sswitch_p4runtime_API.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.sswitch\_p4runtime\_API module
2 | ==============================================
3 |
4 | .. automodule:: p4utils.utils.sswitch_p4runtime_API
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.sswitch_thrift_API.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.sswitch\_thrift\_API module
2 | ===========================================
3 |
4 | .. automodule:: p4utils.utils.sswitch_thrift_API
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.task_scheduler.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.task\_scheduler module
2 | ======================================
3 |
4 | .. automodule:: p4utils.utils.task_scheduler
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.thrift_API.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.thrift\_API module
2 | ==================================
3 |
4 | .. automodule:: p4utils.utils.thrift_API
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.topology.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.topology module
2 | ===============================
3 |
4 | .. automodule:: p4utils.utils.topology
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/docsrc/p4utils.utils.traffic_utils.rst:
--------------------------------------------------------------------------------
1 | p4utils\.utils\.traffic\_utils module
2 | =====================================
3 |
4 | .. automodule:: p4utils.utils.traffic_utils
5 | :members:
6 | :undoc-members:
7 | :show-inheritance:
8 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | This directory collects some working examples that make use of *P4-Utils*. In particular we have the following subfolders, each one containing a specific topology.
4 |
5 | - [switches](./switches): topology including only `P4Switches`. The goal of this example is ensuring L2 and L3 connectivity among hosts.
6 | - [frrouters](./frrouters): topology including `P4Switches` and `FRRouters`. The goal is ensuring L2 and L3 connectivity among hosts, considering also different ASes.
7 | - [adv-net](./adv-net): topology including both `P4Switches` and `FRRouter` used in the *Advanced Topics in Communication Networks* 2020 project. It is not fully functional for what concerns *Traffic Control*.
--------------------------------------------------------------------------------
/examples/adv-net/controller.py:
--------------------------------------------------------------------------------
1 | """Implement your controller in this file.
2 |
3 | The existing controller already implements L2-forwarding to ensure connectivity.
4 | Use this code as an example to get you started.
5 | You are free to completely change this!
6 |
7 | Tip:
8 | For debugging, you can start this file in interactive mode.
9 | This will execute the whole file, but then *keep* the python interpreter open,
10 | allowing you to inspect objects and try out things!
11 |
12 | ```
13 | $ python3 -i controller.py --topo
14 | ```
15 |
16 | The controller will be available as the variable `control`.
17 | """
18 | # pylint: disable=superfluous-parens,invalid-name
19 |
20 | import argparse
21 | import csv
22 |
23 | from p4utils.utils.helper import load_topo
24 | from p4utils.utils.sswitch_thrift_API import SimpleSwitchThriftAPI
25 |
26 | class Controller(object):
27 | """The central controller for your p4 switches."""
28 |
29 | L2_BROADCAST_GROUP_ID = 1
30 |
31 | def __init__(self, topo, traffic=None):
32 | self.topo = load_topo(topo)
33 | if traffic is not None:
34 | # Parse traffic matrix.
35 | self.traffic = self._parse_traffic_file(traffic)
36 | else:
37 | self.traffic = []
38 |
39 | # Basic initialization. *Do not* change.
40 | self.controllers = {}
41 | self._connect_to_switches()
42 | self._reset_states()
43 |
44 | # Start main loop
45 | self.main()
46 |
47 | # Controller helpers.
48 | # ===================
49 |
50 | def _connect_to_switches(self):
51 | for p4switch in self.topo.get_p4switches():
52 | print("Connecting to %s" % p4switch)
53 | thrift_port = self.topo.get_thrift_port(p4switch)
54 | thrift_ip = self.topo.get_thrift_ip(p4switch)
55 | self.controllers[p4switch] = SimpleSwitchThriftAPI(
56 | thrift_port, thrift_ip)
57 |
58 | def _reset_states(self):
59 | for controller in self.controllers.values():
60 | controller.reset_state()
61 |
62 | @staticmethod
63 | def _parse_traffic_file(trafficpath):
64 | with open(trafficpath, 'r') as csvfile:
65 | dialect = csv.Sniffer().sniff(csvfile.read(1024))
66 | csvfile.seek(0)
67 | reader = csv.DictReader(csvfile, dialect=dialect)
68 | return list(reader)
69 |
70 | # Controller methods.
71 | # ===================
72 |
73 | def main(self):
74 | """Main controller method."""
75 | # Initialization of L2 forwarding. Feel free to modify.
76 | self.create_l2_multicast_group()
77 | self.add_l2_forwarding_rules()
78 |
79 | # while True
80 | # do_something()
81 |
82 | def add_l2_forwarding_rules(self):
83 | """Add L2 forwarding groups to all switches.
84 |
85 | We check the topology object to get all connected nodes and their
86 | MAC addresses, and configure static rules accordingly.
87 | """
88 | for switch, controller in self.controllers.items():
89 | # Add broadcast rule.
90 | controller.table_add("l2_forward", "broadcast",
91 | ["ff:ff:ff:ff:ff:ff/48"])
92 |
93 | # Add rule for connected host.
94 | my_host = self.topo.get_hosts_connected_to(switch)[0]
95 | host_mac = self.topo.node_to_node_mac(my_host, switch)
96 | host_port = self.topo.node_to_node_port_num(switch, my_host)
97 | controller.table_add("l2_forward", "l2_forward_action",
98 | [str(host_mac)+"/48"], [str(host_port)])
99 |
100 | # Add rules for connected routers.
101 | for router in self.topo.get_routers_connected_to(switch):
102 | router_mac = self.topo.node_to_node_mac(router, switch)
103 | router_port = self.topo.node_to_node_port_num(switch, router)
104 | controller.table_add("l2_forward", "l2_forward_action",
105 | [str(router_mac)+"/48"], [str(router_port)])
106 |
107 | def create_l2_multicast_group(self):
108 | """Create a multicast group to enable L2 broadcasting."""
109 | for switch, controller in self.controllers.items():
110 | controller.mc_mgrp_create(self.L2_BROADCAST_GROUP_ID)
111 | port_list = []
112 |
113 | # Get host port.
114 | my_host = self.topo.get_hosts_connected_to(switch)[0]
115 | port_list.append(self.topo.node_to_node_port_num(switch, my_host))
116 |
117 | # Get router ports.
118 | for router in self.topo.get_routers_connected_to(switch):
119 | port_list.append(
120 | self.topo.node_to_node_port_num(switch, router))
121 |
122 | # Update group.
123 | controller.mc_node_create(0, port_list)
124 | controller.mc_node_associate(1, 0)
125 |
126 |
127 | if __name__ == "__main__":
128 | parser = argparse.ArgumentParser()
129 | parser.add_argument('--topo', help='Path of topology.db.',
130 | type=str, required=False,
131 | default="./topology.json")
132 | parser.add_argument('--traffic', help='Path of traffic scenario.',
133 | type=str, required=False,
134 | default=None)
135 | args = parser.parse_args()
136 |
137 | control = Controller(args.topo, args.traffic)
138 |
--------------------------------------------------------------------------------
/examples/adv-net/default-2.traffic:
--------------------------------------------------------------------------------
1 | src, dst, sport, dport, tos, rate, duration, packet_size, start_time
2 | h2, h3, 5000, 5001, 128, 1M, 50, 1400, 45
3 | h5, h2, 5000, 5002, 64, 6M, 50, 1400, 45
4 | h6, h4, 5000, 5003, 64, 6M, 50, 1400, 45
5 | h4, h6, 5000, 5004, 32, 12M, 50, 1400, 45
6 | h1, h5, 5000, 5005, 32, 12M, 50, 1400, 45
7 | h3, h1, 5000, 5006, 32, 12M, 50, 1400, 45
8 |
--------------------------------------------------------------------------------
/examples/adv-net/default-3.traffic:
--------------------------------------------------------------------------------
1 | src, dst, sport, dport, tos, rate, duration, packet_size, start_time
2 | h6, h2, 5000, 5001, 128, 1M, 50, 1400, 45
3 | h2, h4, 5000, 5002, 64, 6M, 50, 1400, 45
4 | h1, h3, 5000, 5003, 64, 6M, 50, 1400, 45
5 | h3, h5, 5000, 5004, 32, 12M, 50, 1400, 45
6 | h4, h6, 5000, 5005, 32, 12M, 50, 1400, 45
7 | h5, h1, 5000, 5006, 32, 12M, 50, 1400, 45
8 |
--------------------------------------------------------------------------------
/examples/adv-net/default.traffic:
--------------------------------------------------------------------------------
1 | src, dst, sport, dport, tos, rate, duration, packet_size, start_time
2 | h1, h4, 5000, 5001, 128, 1M, 50, 1400, 45
3 | h2, h5, 5000, 5002, 64, 6M, 50, 1400, 45
4 | h3, h6, 5000, 5003, 64, 6M, 50, 1400, 45
5 | h4, h1, 5000, 5004, 32, 12M, 50, 1400, 45
6 | h5, h2, 5000, 5005, 32, 12M, 50, 1400, 45
7 | h6, h3, 5000, 5006, 32, 12M, 50, 1400, 45
8 |
--------------------------------------------------------------------------------
/examples/adv-net/network.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import csv
3 | from udp import *
4 | from p4utils.mininetlib.network_API import NetworkAPI
5 |
6 | HOSTS_TO_IP = {
7 | 'h1': '1.0.0.1',
8 | 'h2': '2.0.0.1',
9 | 'h3': '3.0.0.1',
10 | 'h4': '4.0.0.1',
11 | 'h5': '5.0.0.1',
12 | 'h6': '6.0.0.1'
13 | }
14 |
15 | def load_flows_file(config_file):
16 | flows = []
17 | with open(config_file, 'r') as csvfile:
18 | dialect = csv.Sniffer().sniff(csvfile.read(1024))
19 | csvfile.seek(0)
20 | reader = csv.DictReader(csvfile, dialect=dialect)
21 | return list(reader)
22 |
23 | def main(config_file):
24 | net = NetworkAPI()
25 |
26 | # Network general options
27 | net.enableCli()
28 | net.disableGwArp()
29 | net.disableArpTables()
30 | net.setLogLevel('info')
31 |
32 | # Network definition
33 | # Switches
34 | net.addP4Switch('s1')
35 | net.addP4Switch('s2')
36 | net.addP4Switch('s3')
37 | net.addP4Switch('s4')
38 | net.addP4Switch('s5')
39 | net.addP4Switch('s6')
40 | net.setP4SourceAll('switch.p4')
41 |
42 | net.execScript('python controller.py --traffic {}'.format(config_file), reboot=True)
43 | net.execScript('sleep 100 && python performance.py --traffic-spec {} &'.format(config_file), reboot=True)
44 |
45 | # Hosts
46 | net.addHost('h1')
47 | net.setDefaultRoute('h1', "1.0.0.2")
48 | net.addHost('h2')
49 | net.setDefaultRoute('h2', "2.0.0.2")
50 | net.addHost('h3')
51 | net.setDefaultRoute('h3', "3.0.0.2")
52 | net.addHost('h4')
53 | net.setDefaultRoute('h4', '4.0.0.2')
54 | net.addHost('h5')
55 | net.setDefaultRoute('h5', '5.0.0.2')
56 | net.addHost('h6')
57 | net.setDefaultRoute('h6', '6.0.0.2')
58 |
59 | # Routers
60 | net.addRouter('r1', int_conf='./routers/r1.conf', bgpd=False)
61 | net.addRouter('r2', int_conf='./routers/r2.conf', bgpd=False)
62 | net.addRouter('r3', int_conf='./routers/r3.conf', bgpd=False)
63 | net.addRouter('r4', int_conf='./routers/r4.conf', bgpd=False)
64 |
65 | # Links
66 | net.addLink('h1', 's1', bw=12, intfName2='s1-host')
67 | net.setIntfIp('h1', 's1', HOSTS_TO_IP['h1']+'/24')
68 | net.setIntfMac('h1', 's1', '00:00:01:00:00:01')
69 |
70 | net.addLink('h2', 's2', bw=12, intfName2='s2-host')
71 | net.setIntfIp('h2', 's2', HOSTS_TO_IP['h2']+'/24')
72 | net.setIntfMac('h2', 's2', '00:00:02:00:00:01')
73 |
74 | net.addLink('h3', 's3', bw=12, intfName2='s3-host')
75 | net.setIntfIp('h3', 's3', HOSTS_TO_IP['h3']+'/24')
76 | net.setIntfMac('h3', 's3', '00:00:03:00:00:01')
77 |
78 | net.addLink('h4', 's4', bw=12, intfName2='s4-host')
79 | net.setIntfIp('h4', 's4', HOSTS_TO_IP['h4']+'/24')
80 | net.setIntfMac('h4', 's4', '00:00:04:00:00:01')
81 |
82 | net.addLink('h5', 's5', bw=12, intfName2='s5-host')
83 | net.setIntfIp('h5', 's5', HOSTS_TO_IP['h5']+'/24')
84 | net.setIntfMac('h5', 's5', '00:00:05:00:00:01')
85 |
86 | net.addLink('h6', 's6', bw=12, intfName2='s6-host')
87 | net.setIntfIp('h6', 's6', HOSTS_TO_IP['h6']+'/24')
88 | net.setIntfMac('h6', 's6', '00:00:06:00:00:01')
89 |
90 | net.addLink('s1', 'r1', port2=1, intfName1='s1-port_R1', intfName2='port_S1', bw=6)
91 | net.addLink('s1', 'r4', port2=5, intfName1='s1-port_R4', intfName2='port_S1', bw=4)
92 | net.addLink('s1', 's6', intfName1='s1-port_S6', intfName2='s6-port_S1',bw=6)
93 | net.addLink('s6', 'r1', port2=5, intfName1='s6-port_R1', intfName2='port_S6', bw=4)
94 | net.addLink('s6', 'r4', port2=3, intfName1='s6-port_R4', intfName2='port_S6', bw=6)
95 |
96 | net.addLink('s3', 'r2', port2=1, intfName1='s3-port_R2', intfName2='port_S3', bw=6)
97 | net.addLink('s3', 'r3', port2=5, intfName1='s3-port_R3', intfName2='port_S3', bw=4)
98 | net.addLink('s3', 's4', intfName1='s3-port_S4', intfName2='s4-port_S3', bw=6)
99 | net.addLink('s4', 'r2', port2=4, intfName1='s4-port_R2', intfName2='port_S4', bw=4)
100 | net.addLink('s4', 'r3', port2=4, intfName1='s4-port_R3', intfName2='port_S4', bw=6)
101 |
102 | net.addLink('s2', 'r1', port2=2, intfName1='s2-port_R1', intfName2='port_S2', bw=4)
103 | net.addLink('s2', 'r2', port2=6, intfName1='s2-port_R2', intfName2='port_S2', bw=4)
104 |
105 | net.addLink('s5', 'r3', port2=3, intfName1='s5-port_R3', intfName2='port_S5', bw=4)
106 | net.addLink('s5', 'r4', port2=6, intfName1='s5-port_R4', intfName2='port_S5', bw=4)
107 |
108 | net.addLink('r1', 'r2', port1=3, port2=2, intfName1='port_R2', intfName2='port_R1', bw=4)
109 | net.addLink('r1', 'r3', port1=4, port2=2, intfName1='port_R3', intfName2='port_R1', bw=6)
110 | net.addLink('r1', 'r4', port1=6, port2=4, intfName1='port_R4', intfName2='port_R1', bw=4)
111 | net.addLink('r2', 'r3', port1=5, port2=6, intfName1='port_R3', intfName2='port_R2', bw=4)
112 | net.addLink('r2', 'r4', port1=3, port2=2, intfName1='port_R4', intfName2='port_R2', bw=6)
113 | net.addLink('r3', 'r4', port1=1, port2=1, intfName1='port_R4', intfName2='port_R3', bw=4)
114 |
115 | # Nodes general options
116 | net.disablePcapDumpAll()
117 | net.enableLogAll()
118 |
119 | # Add tasks for traffic generation
120 | flows = load_flows_file(config_file)
121 | for kwargs in flows:
122 | kwargs['d'] = kwargs['duration']
123 | kwargs['start'] = float(kwargs['start_time'])
124 | kwargs['src_name'] = kwargs['src']
125 | kwargs['dst_name'] = kwargs['dst']
126 | kwargs['src'] = HOSTS_TO_IP[kwargs['src_name']]
127 | kwargs['dst'] = HOSTS_TO_IP[kwargs['dst_name']]
128 | del kwargs['duration']
129 | del kwargs['start_time']
130 |
131 | out_file = "{}/sender_{}_{}_{}_{}.txt".format(
132 | './flows/', kwargs['src_name'], kwargs['dst_name'], kwargs["sport"], kwargs["dport"])
133 | net.addTask(kwargs['src_name'], send_udp_flow, out_file=out_file, **kwargs)
134 |
135 | out_file = "{}/receiver_{}_{}_{}_{}.txt".format(
136 | './flows/', kwargs['src_name'], kwargs['dst_name'], kwargs["sport"], kwargs["dport"])
137 | net.addTask(kwargs['dst_name'], recv_udp_flow, src=kwargs["src"], dport=int(kwargs["dport"]), out_file=out_file)
138 |
139 | # Start the network
140 | net.startNetwork()
141 |
142 | if __name__ == "__main__":
143 |
144 | main(sys.argv[1])
145 |
--------------------------------------------------------------------------------
/examples/adv-net/performance.py:
--------------------------------------------------------------------------------
1 | """Computes the performance of your run
2 |
3 | Example usage: python2 performance.py --traffic-spec ../scenarios/default.traffic
4 |
5 | """
6 |
7 | import os
8 | import argparse
9 | import csv
10 | from collections import OrderedDict
11 |
12 |
13 | traffic_weights = {
14 | "128": 10,
15 | "64": 4,
16 | "32": 1
17 | }
18 |
19 | traffic_names = OrderedDict([
20 | ("128", "Gold"),
21 | ("64", "Silver"),
22 | ("32", "Bronze")
23 | ])
24 |
25 | class Performance(object):
26 | """Load the traffic matrix and generate everything."""
27 |
28 | def __init__(self, traffic, out_path):
29 | self.traffic_spec = traffic
30 | self.out_path = out_path
31 | self.flows = self._load_traffic_spec()
32 | self._count_points()
33 |
34 | def _load_traffic_spec(self):
35 | """Loads traffic matrix spec"""
36 |
37 | flows = []
38 | with open(self.traffic_spec, 'r') as csvfile:
39 | dialect = csv.Sniffer().sniff(csvfile.read(1024))
40 | csvfile.seek(0)
41 | reader = csv.DictReader(csvfile, dialect=dialect)
42 | return list(reader)
43 |
44 | def _read_sequences(self, file_name):
45 | """Helper function to read"""
46 |
47 | with open(file_name, "r") as f:
48 | return set([int(x) for x in f.read().split()])
49 |
50 | def _count_flow(self, flow):
51 | """ Counts the packet in out for a given flow"""
52 |
53 | sender_path = "{}/sender_{}_{}_{}_{}.txt".format(self.out_path,
54 | flow["src"], flow["dst"], flow["sport"], flow["dport"])
55 | sender_seq = self._read_sequences(sender_path)
56 | receiver_path = "{}/receiver_{}_{}_{}_{}.txt".format(self.out_path,
57 | flow["src"], flow["dst"], flow["sport"], flow["dport"])
58 | receiver_seq = self._read_sequences(receiver_path)
59 | received_and_sent = sender_seq.intersection(receiver_seq)
60 |
61 | # returns pkt_in and pkt_out (sent from this sender and not repeated)
62 | return len(sender_seq), len(received_and_sent)
63 |
64 | def _count_points(self):
65 | """ Counts all flows in/out """
66 |
67 | self._traffic_counts = {
68 | "128":
69 | {"pkts_in": 0.0, "pkts_out": 0.0},
70 | "64":
71 | {"pkts_in": 0.0, "pkts_out": 0.0},
72 | "32":
73 | {"pkts_in": 0.0, "pkts_out": 0.0},
74 | }
75 | for flow in self.flows:
76 | tos = flow["tos"]
77 | pkt_in, pkt_out = self._count_flow(flow)
78 |
79 | self._traffic_counts[tos]["pkts_in"] += pkt_in
80 | self._traffic_counts[tos]["pkts_out"] += pkt_out
81 |
82 | def get_weighted_perfomance(self):
83 | """ computes final performance """
84 |
85 | self._count_points()
86 | weighted_performance = 0
87 | for traffic_type, packets in self._traffic_counts.items():
88 | # type weighted performance
89 | if packets["pkts_in"] == 0:
90 | _performance = 0
91 | else:
92 | _performance = (packets["pkts_out"]/packets["pkts_in"]) * (traffic_weights[traffic_type])/(sum(traffic_weights.values()))
93 | weighted_performance += _performance
94 |
95 | return weighted_performance
96 |
97 | def print_performance(self):
98 | """ Print per traffic class and weighted performance """
99 |
100 | print("\n===============================")
101 | print("""Your last run performances are:\n===============================""")
102 |
103 | weighted_performance = self.get_weighted_perfomance()
104 | for traffic_type, traffic_name in traffic_names.items():
105 | packets = self._traffic_counts[traffic_type]
106 | warning = ""
107 | if packets["pkts_in"] == 0:
108 | _performance = 0
109 | warning = "\033[31m(warning: you did not send traffic for this type)\033[39m"
110 | else:
111 | _performance = (packets["pkts_out"]/packets["pkts_in"])
112 | print("{:10} {:.5f} {}".format(traffic_names[traffic_type], _performance, warning))
113 | print("-------------------------------")
114 | print("Weighted {:.5f}".format(weighted_performance))
115 |
116 |
117 | if __name__ == "__main__":
118 | # pylint: disable=invalid-name
119 | parser = argparse.ArgumentParser()
120 | parser.add_argument('--traffic-spec',
121 | help='Traffic generation specification',
122 | type=str, required=True)
123 | parser.add_argument('--out-path',
124 | help='Path to flows logs',
125 | type=str, required=False, default="./flows")
126 |
127 | args = parser.parse_args()
128 |
129 | performance = Performance(
130 | args.traffic_spec, args.out_path
131 | )
132 |
133 | performance.print_performance()
134 |
--------------------------------------------------------------------------------
/examples/adv-net/routers/r1.conf:
--------------------------------------------------------------------------------
1 | conf t
2 | interface lo
3 | ip address 1.151.0.1/32
4 | exit
5 | router ospf
6 | ospf router-id 1.151.0.1
7 | network 1.151.0.1/32 area 0
8 | exit
9 |
10 | interface port_R2
11 | ip address 10.1.0.1/24
12 | ip ospf cost 1
13 | exit
14 | router ospf
15 | network 10.1.0.1/24 area 0
16 | exit
17 |
18 | interface port_R3
19 | ip address 10.2.0.1/24
20 | ip ospf cost 1
21 | exit
22 | router ospf
23 | network 10.2.0.1/24 area 0
24 | exit
25 |
26 | interface port_R4
27 | ip address 10.3.0.1/24
28 | ip ospf cost 1
29 | exit
30 | router ospf
31 | network 10.3.0.1/24 area 0
32 | exit
33 |
34 | interface port_S1
35 | ip address 1.0.0.2/24
36 | ip ospf cost 1
37 | exit
38 | router ospf
39 | network 1.0.0.2/24 area 0
40 | exit
41 |
42 | interface port_S2
43 | ip address 2.0.0.2/24
44 | ip ospf cost 1
45 | exit
46 | router ospf
47 | network 2.0.0.2/24 area 0
48 | exit
49 |
50 | interface port_S6
51 | ip address 6.0.0.3/24
52 | ip ospf cost 1
53 | exit
54 | router ospf
55 | network 6.0.0.3/24 area 0
56 | exit
57 |
--------------------------------------------------------------------------------
/examples/adv-net/routers/r2.conf:
--------------------------------------------------------------------------------
1 | conf t
2 | interface lo
3 | ip address 1.152.0.1/32
4 | exit
5 | router ospf
6 | ospf router-id 1.152.0.1
7 | network 1.152.0.1/32 area 0
8 | exit
9 | interface port_R1
10 | ip address 10.1.0.2/24
11 | ip ospf cost 1
12 | exit
13 | router ospf
14 | network 10.1.0.2/24 area 0
15 | exit
16 | interface port_R3
17 | ip address 10.5.0.1/24
18 | ip ospf cost 1
19 | exit
20 | router ospf
21 | network 10.5.0.1/24 area 0
22 | exit
23 | interface port_R4
24 | ip address 10.4.0.1/24
25 | ip ospf cost 1
26 | exit
27 | router ospf
28 | network 10.4.0.1/24 area 0
29 | exit
30 |
31 | interface port_S2
32 | ip address 2.0.0.3/24
33 | ip ospf cost 1
34 | exit
35 | router ospf
36 | network 2.0.0.3/24 area 0
37 | exit
38 |
39 | interface port_S3
40 | ip address 3.0.0.2/24
41 | ip ospf cost 1
42 | exit
43 | router ospf
44 | network 3.0.0.2/24 area 0
45 | exit
46 |
47 | interface port_S4
48 | ip address 4.0.0.3/24
49 | ip ospf cost 1
50 | exit
51 | router ospf
52 | network 4.0.0.3/24 area 0
53 | exit
54 |
--------------------------------------------------------------------------------
/examples/adv-net/routers/r3.conf:
--------------------------------------------------------------------------------
1 | conf t
2 | interface lo
3 | ip address 1.153.0.1/32
4 | exit
5 | router ospf
6 | ospf router-id 1.153.0.1
7 | network 1.153.0.1/32 area 0
8 | exit
9 | interface port_R1
10 | ip address 10.2.0.2/24
11 | ip ospf cost 1
12 | exit
13 | router ospf
14 | network 10.2.0.2/24 area 0
15 | exit
16 | interface port_R2
17 | ip address 10.5.0.2/24
18 | ip ospf cost 1
19 | exit
20 | router ospf
21 | network 10.5.0.2/24 area 0
22 | exit
23 | interface port_R4
24 | ip address 10.6.0.1/24
25 | ip ospf cost 1
26 | exit
27 | router ospf
28 | network 10.6.0.1/24 area 0
29 | exit
30 |
31 | interface port_S3
32 | ip address 3.0.0.3/24
33 | ip ospf cost 1
34 | exit
35 | router ospf
36 | network 3.0.0.3/24 area 0
37 | exit
38 |
39 | interface port_S4
40 | ip address 4.0.0.2/24
41 | ip ospf cost 1
42 | exit
43 | router ospf
44 | network 4.0.0.2/24 area 0
45 | exit
46 |
47 | interface port_S5
48 | ip address 5.0.0.3/24
49 | ip ospf cost 1
50 | exit
51 | router ospf
52 | network 5.0.0.3/24 area 0
53 | exit
54 |
--------------------------------------------------------------------------------
/examples/adv-net/routers/r4.conf:
--------------------------------------------------------------------------------
1 | conf t
2 | interface lo
3 | ip address 1.154.0.1/32
4 | exit
5 | router ospf
6 | ospf router-id 1.154.0.1
7 | network 1.154.0.1/32 area 0
8 | exit
9 | interface port_R1
10 | ip address 10.3.0.2/24
11 | ip ospf cost 1
12 | exit
13 | router ospf
14 | network 10.3.0.2/24 area 0
15 | exit
16 | interface port_R2
17 | ip address 10.4.0.2/24
18 | ip ospf cost 1
19 | exit
20 | router ospf
21 | network 10.4.0.2/24 area 0
22 | exit
23 | interface port_R3
24 | ip address 10.6.0.2/24
25 | ip ospf cost 1
26 | exit
27 | router ospf
28 | network 10.6.0.2/24 area 0
29 | exit
30 |
31 | interface port_S5
32 | ip address 5.0.0.2/24
33 | ip ospf cost 1
34 | exit
35 | router ospf
36 | network 5.0.0.2/24 area 0
37 | exit
38 |
39 | interface port_S6
40 | ip address 6.0.0.2/24
41 | ip ospf cost 1
42 | exit
43 | router ospf
44 | network 6.0.0.2/24 area 0
45 | exit
46 |
47 | interface port_S1
48 | ip address 1.0.0.3/24
49 | ip ospf cost 1
50 | exit
51 | router ospf
52 | network 1.0.0.3/24 area 0
53 | exit
--------------------------------------------------------------------------------
/examples/adv-net/udp.py:
--------------------------------------------------------------------------------
1 | import time
2 | import socket
3 | import math
4 |
5 | # min udp packet size
6 | minSizeUDP = 42
7 | maxUDPSize = 1400
8 | DEFAULT_BATCH_SIZE = 1
9 |
10 |
11 | def setSizeToInt(size):
12 | """" Converts the sizes string notation to the corresponding integer
13 | (in bytes). Input size can be given with the following
14 | magnitudes: B, K, M and G.
15 | """
16 | if isinstance(size, int):
17 | return size
18 | elif isinstance(size, float):
19 | return int(size)
20 | try:
21 | conversions = {'B': 1, 'K': 1e3, 'M': 1e6, 'G': 1e9}
22 | digits_list = list(range(48, 58)) + [ord(".")]
23 | magnitude = chr(
24 | sum([ord(x) if (ord(x) not in digits_list) else 0 for x in size]))
25 | digit = float(size[0:(size.index(magnitude))])
26 | magnitude = conversions[magnitude]
27 | return int(magnitude*digit)
28 | except:
29 | print("Conversion Fail")
30 | return 0
31 |
32 |
33 | def send_udp_flow(dst="10.0.1.2", sport=5000, dport=5001, tos=0, rate='10M', d=10,
34 | packet_size=maxUDPSize, batch_size=DEFAULT_BATCH_SIZE, out_file="send.txt", **kwargs):
35 | """Udp sending function that keeps a constant rate and logs sent packets to a file.
36 | Args:
37 | dst (str, optional): [description]. Defaults to "10.0.1.2".
38 | sport (int, optional): [description]. Defaults to 5000.
39 | dport (int, optional): [description]. Defaults to 5001.
40 | tos (int, optional): [description]. Defaults to 0.
41 | rate (str, optional): [description]. Defaults to '10M'.
42 | d (int, optional): [description]. Defaults to 10.
43 | packet_size ([type], optional): [description]. Defaults to maxUDPSize.
44 | batch_size (int, optional): [description]. Defaults to 5.
45 | out_file (str, optional): [description]. Defaults to "send.txt".
46 | """
47 |
48 | sport = int(sport)
49 | dport = int(dport)
50 | packet_size = int(packet_size)
51 | tos = int(tos)
52 | if packet_size > maxUDPSize:
53 | packet_size = maxUDPSize
54 |
55 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
56 | s.setsockopt(socket.SOL_IP, socket.IP_TOS, tos)
57 | s.bind(('', sport))
58 |
59 | rate = int(setSizeToInt(rate)/8)
60 | totalTime = float(d)
61 |
62 | # we use 17 to correct a bit the bw
63 | packet = b"A" * int((packet_size - 17))
64 | seq = 0
65 | output_log = open(out_file, "w")
66 |
67 | try:
68 | startTime = time.time()
69 | while (time.time() - startTime < totalTime):
70 |
71 | packets_to_send = rate/packet_size
72 | times = math.ceil((float(rate) / (packet_size))/batch_size)
73 | time_step = 1/times
74 | start = time.time()
75 | i = 0
76 | packets_sent = 0
77 | # batches of 1 sec
78 | while packets_sent < packets_to_send:
79 | for _ in range(batch_size):
80 | s.sendto(seq.to_bytes(4, byteorder='big') +
81 | packet, (dst, dport))
82 | output_log.write("{}\n".format(seq))
83 | output_log.flush()
84 | # sequence_numbers.append(seq)
85 | packets_sent += 1
86 | seq += 1
87 |
88 | i += 1
89 | next_send_time = start + (i * time_step)
90 | time.sleep(max(0, next_send_time - time.time()))
91 | # return
92 | time.sleep(max(0, 1-(time.time()-start)))
93 |
94 | finally:
95 | s.close()
96 | output_log.close()
97 |
98 |
99 | def recv_udp_flow(src, dport, out_file="recv.txt"):
100 | """Receiving function. It blocks reciving packets and store the first
101 | 4 bytes into out file
102 |
103 | Args:
104 | src ([str]): source ip address to listen
105 | dport ([int]): port to listen
106 | out_file ([str]): out file to log
107 | """
108 |
109 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
110 | s.bind(("", dport))
111 | output_log = open(out_file, "w")
112 | c = 0
113 | try:
114 | while True:
115 | data, address = s.recvfrom(2048)
116 | # only accept packets from the expected source
117 | if address[0] == src:
118 | c += 1
119 | output_log.write("{}\n".format(
120 | int.from_bytes(data[:4], 'big')))
121 | output_log.flush()
122 |
123 | except:
124 | print("Packets received {}".format(c))
125 | s.close()
126 | output_log.close()
127 |
128 |
129 | def save_sequences(sequences, file_name):
130 | """Helper function to save sequence numbers in bulk
131 |
132 | Args:
133 | sequences ([list]): list of sequences
134 | file_name ([str]): output file
135 | """
136 |
137 | with open(file_name, "w") as f:
138 | for seq in sequences:
139 | f.write("{}\n".format(seq))
140 |
--------------------------------------------------------------------------------
/examples/frrouters/README.md:
--------------------------------------------------------------------------------
1 | # Fundamental network operations
2 |
3 |
4 |
5 |
6 |
7 | ## Introduction
8 |
9 | The objective of this example is to provide an overview of the capabilities of **p4-utils**. According to the topology above, we have 2 ASes. AS 1 is responsible for the prefix `1.0.0.0/8`, while AS 2 `2.0.0.0/8`. In each one of them we have serveral hosts, routers and P4 switches. The goal is using all the main protocols and technologies to gain full connectivity among the hosts. In particular, this involves the following:
10 | - *L2 learning* for P4 switches, in order to have L2 connectivity within each network segment;
11 | - *OSPF* for routers, in order to have connectivity within the same AS;
12 | - *BGP* for routers, in order to have connectivity among different ASes;
13 | - *LDP* for routers, in order to make the core of AS 1 (R3) *BGP*-free and still have full connectivity.
14 |
15 | ## Execution
16 |
17 | The scenario can be executed with both the following commands:
18 | ```
19 | sudo p4run
20 | ```
21 | or
22 | ```
23 | sudo python network.py
24 | ```
25 |
26 | Please notice that *OSPF*, *LDP* and *BGP* take some time to converge so, at the beginning, the network can still be partitioned.
--------------------------------------------------------------------------------
/examples/frrouters/images/frr_example_topo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/examples/frrouters/images/frr_example_topo.png
--------------------------------------------------------------------------------
/examples/frrouters/l2_learning_controller.py:
--------------------------------------------------------------------------------
1 | import nnpy
2 | import struct
3 | from p4utils.utils.helper import load_topo
4 | from p4utils.utils.sswitch_thrift_API import SimpleSwitchThriftAPI
5 | from scapy.all import Ether, sniff, Packet, BitField, raw
6 |
7 |
8 | class CpuHeader(Packet):
9 | name = 'CpuPacket'
10 | fields_desc = [BitField('macAddr',0,48), BitField('ingress_port', 0, 16)]
11 |
12 |
13 | class L2Controller(object):
14 |
15 | def __init__(self, sw_name):
16 | self.topo = load_topo('topology.json')
17 | self.sw_name = sw_name
18 | self.thrift_port = self.topo.get_thrift_port(sw_name)
19 | self.cpu_port = self.topo.get_cpu_port_index(self.sw_name)
20 | self.controller = SimpleSwitchThriftAPI(self.thrift_port)
21 | self.init()
22 |
23 | def init(self):
24 | self.controller.reset_state()
25 | self.add_boadcast_groups()
26 | self.add_mirror()
27 |
28 | def add_mirror(self):
29 | if self.cpu_port:
30 | self.controller.mirroring_add(100, self.cpu_port)
31 |
32 | def add_boadcast_groups(self):
33 | interfaces_to_port = self.topo.get_node_intfs(fields=['port'])[self.sw_name].copy()
34 | # Filter lo and cpu port
35 | interfaces_to_port.pop('lo', None)
36 | interfaces_to_port.pop(self.topo.get_cpu_port_intf(self.sw_name), None)
37 |
38 | mc_grp_id = 1
39 | rid = 0
40 | for ingress_port in interfaces_to_port.values():
41 | port_list = list(interfaces_to_port.values())
42 | del(port_list[port_list.index(ingress_port)])
43 | #add multicast group
44 | self.controller.mc_mgrp_create(mc_grp_id)
45 | #add multicast node group
46 | handle = self.controller.mc_node_create(rid, port_list)
47 | #associate with mc grp
48 | self.controller.mc_node_associate(mc_grp_id, handle)
49 | #fill broadcast table
50 | self.controller.table_add("broadcast", "set_mcast_grp", [str(ingress_port)], [str(mc_grp_id)])
51 | mc_grp_id +=1
52 | rid +=1
53 |
54 | def learn(self, learning_data):
55 | for mac_addr, ingress_port in learning_data:
56 | print("mac: %012X ingress_port: %s " % (mac_addr, ingress_port))
57 | self.controller.table_add("smac", "NoAction", [str(mac_addr)])
58 | self.controller.table_add("dmac", "forward", [str(mac_addr)], [str(ingress_port)])
59 |
60 | def unpack_digest(self, msg, num_samples):
61 | digest = []
62 | starting_index = 32
63 | for sample in range(num_samples):
64 | mac0, mac1, ingress_port = struct.unpack(">LHH", msg[starting_index:starting_index+8])
65 | starting_index +=8
66 | mac_addr = (mac0 << 16) + mac1
67 | digest.append((mac_addr, ingress_port))
68 | return digest
69 |
70 | def recv_msg_digest(self, msg):
71 | topic, device_id, ctx_id, list_id, buffer_id, num = struct.unpack("
3 | #include
4 |
5 | const bit<16> TYPE_IPV4 = 0x800;
6 | const bit<16> TYPE_BROADCAST = 0x1234;
7 |
8 | /*************************************************************************
9 | *********************** H E A D E R S ***********************************
10 | *************************************************************************/
11 |
12 | typedef bit<9> egressSpec_t;
13 | typedef bit<48> macAddr_t;
14 | typedef bit<32> ip4Addr_t;
15 |
16 | header ethernet_t {
17 | macAddr_t dstAddr;
18 | macAddr_t srcAddr;
19 | bit<16> etherType;
20 | }
21 |
22 | struct learn_t {
23 |
24 | bit<48> srcAddr;
25 | bit<9> ingress_port;
26 |
27 | }
28 |
29 | struct metadata {
30 | /* empty */
31 | learn_t learn;
32 | }
33 |
34 | struct headers {
35 | ethernet_t ethernet;
36 | }
37 |
38 |
39 | /*************************************************************************
40 | *********************** P A R S E R ***********************************
41 | *************************************************************************/
42 |
43 | parser MyParser(packet_in packet,
44 | out headers hdr,
45 | inout metadata meta,
46 | inout standard_metadata_t standard_metadata) {
47 |
48 | state start {
49 | packet.extract(hdr.ethernet);
50 | transition accept;
51 | }
52 | }
53 |
54 |
55 | /*************************************************************************
56 | ************ C H E C K S U M V E R I F I C A T I O N *************
57 | *************************************************************************/
58 |
59 | control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
60 | apply { }
61 | }
62 |
63 |
64 | /*************************************************************************
65 | ************** I N G R E S S P R O C E S S I N G *******************
66 | *************************************************************************/
67 |
68 | control MyIngress(inout headers hdr,
69 | inout metadata meta,
70 | inout standard_metadata_t standard_metadata) {
71 |
72 | action drop() {
73 |
74 | mark_to_drop(standard_metadata);
75 | }
76 |
77 | action mac_learn(){
78 | meta.learn.srcAddr = hdr.ethernet.srcAddr;
79 | meta.learn.ingress_port = standard_metadata.ingress_port;
80 | digest(1, meta.learn);
81 | }
82 |
83 | table smac {
84 |
85 | key = {
86 | hdr.ethernet.srcAddr: exact;
87 | }
88 |
89 | actions = {
90 | mac_learn;
91 | NoAction;
92 | }
93 | size = 256;
94 | default_action = mac_learn;
95 | }
96 |
97 | action forward(bit<9> egress_port) {
98 | standard_metadata.egress_spec = egress_port;
99 | }
100 |
101 | table dmac {
102 | key = {
103 | hdr.ethernet.dstAddr: exact;
104 | }
105 |
106 | actions = {
107 | forward;
108 | NoAction;
109 | }
110 | size = 256;
111 | default_action = NoAction;
112 | }
113 |
114 | action set_mcast_grp(bit<16> mcast_grp) {
115 | standard_metadata.mcast_grp = mcast_grp;
116 | }
117 |
118 | table broadcast {
119 | key = {
120 | standard_metadata.ingress_port: exact;
121 | }
122 |
123 | actions = {
124 | set_mcast_grp;
125 | NoAction;
126 | }
127 | size = 256;
128 | default_action = NoAction;
129 | }
130 |
131 | apply {
132 |
133 | smac.apply();
134 | if (dmac.apply().hit){
135 | //
136 | }
137 | else {
138 | broadcast.apply();
139 | }
140 | }
141 | }
142 |
143 | /*************************************************************************
144 | **************** E G R E S S P R O C E S S I N G *******************
145 | *************************************************************************/
146 |
147 | control MyEgress(inout headers hdr,
148 | inout metadata meta,
149 | inout standard_metadata_t standard_metadata) {
150 |
151 |
152 | apply { }
153 | }
154 |
155 | /*************************************************************************
156 | ************* C H E C K S U M C O M P U T A T I O N **************
157 | *************************************************************************/
158 |
159 | control MyComputeChecksum(inout headers hdr, inout metadata meta) {
160 | apply {
161 |
162 | }
163 | }
164 |
165 |
166 | /*************************************************************************
167 | *********************** D E P A R S E R *******************************
168 | *************************************************************************/
169 |
170 | control MyDeparser(packet_out packet, in headers hdr) {
171 | apply {
172 | //parsed headers have to be added again into the packet.
173 | packet.emit(hdr.ethernet);
174 | }
175 | }
176 |
177 | /*************************************************************************
178 | *********************** S W I T C H *******************************
179 | *************************************************************************/
180 |
181 | //switch architecture
182 | V1Switch(
183 | MyParser(),
184 | MyVerifyChecksum(),
185 | MyIngress(),
186 | MyEgress(),
187 | MyComputeChecksum(),
188 | MyDeparser()
189 | ) main;
--------------------------------------------------------------------------------
/examples/frrouters/network.py:
--------------------------------------------------------------------------------
1 | from p4utils.mininetlib.network_API import NetworkAPI
2 |
3 | net = NetworkAPI()
4 |
5 | # Network general options
6 | net.setLogLevel('info')
7 | net.execScript('python l2_learning_controller.py s1 digest &', reboot=True)
8 | net.execScript('python l2_learning_controller.py s2 digest &', reboot=True)
9 | net.execScript('python l2_learning_controller.py s3 digest &', reboot=True)
10 | net.enableCli()
11 | net.disableArpTables()
12 | net.disableGwArp()
13 |
14 | # Network definition
15 |
16 | # Switches
17 | # AS 1
18 | net.addP4Switch('s1')
19 | net.addP4Switch('s2')
20 | # AS 2
21 | net.addP4Switch('s3')
22 | net.setP4SourceAll('l2_learning_digest.p4')
23 |
24 | # Hosts
25 | # AS 1
26 | net.addHost('h1')
27 | net.setDefaultRoute('h1', "1.0.0.1")
28 | net.addHost('h4')
29 | net.setDefaultRoute('h4', "1.0.0.1")
30 | net.addHost('h2')
31 | net.setDefaultRoute('h2', "1.7.0.1")
32 | net.addHost('h5')
33 | net.setDefaultRoute('h5', '1.7.0.1')
34 |
35 | # AS 2
36 | net.addHost('h3')
37 | net.setDefaultRoute('h3', "2.0.0.1")
38 | net.addHost('h6')
39 | net.setDefaultRoute('h6', '2.0.0.1')
40 |
41 | # Routers
42 | # AS 1
43 | net.addRouter('r1', int_conf='./routers/r1.conf', ldpd=True)
44 | net.addRouter('r2', int_conf='./routers/r2.conf', ldpd=True)
45 | net.addRouter('r3', int_conf='./routers/r3.conf', ldpd=True)
46 | net.addRouter('r4', int_conf='./routers/r4.conf', ldpd=True)
47 |
48 | # AS 2
49 | net.addRouter('r5', int_conf='./routers/r5.conf', ldpd=True)
50 |
51 | # Links
52 | # AS 1
53 | net.addLink('h1', 's1')
54 | net.setIntfIp('h1', 's1', '1.0.0.2/24')
55 | net.addLink('h4', 's1')
56 | net.setIntfIp('h4', 's1', '1.0.0.3/24')
57 | net.addLink('s1', 'r1', intfName2='port_S1')
58 |
59 | net.addLink('h2', 's2')
60 | net.setIntfIp('h2', 's2', '1.7.0.2/24')
61 | net.addLink('h5', 's2')
62 | net.setIntfIp('h5', 's2', '1.7.0.3/24')
63 | net.addLink('s2', 'r2', intfName2='port_S2')
64 |
65 | net.addLink('r1', 'r2', intfName1='port_R2', intfName2='port_R1')
66 | net.addLink('r1', 'r3', intfName1='port_R3', intfName2='port_R1')
67 | net.addLink('r1', 'r4', intfName1='port_R4', intfName2='port_R1')
68 | net.addLink('r2', 'r3', intfName1='port_R3', intfName2='port_R2')
69 | net.addLink('r2', 'r4', intfName1='port_R4', intfName2='port_R2')
70 | net.addLink('r3', 'r4', intfName1='port_R4', intfName2='port_R3')
71 |
72 | # Inter-AS
73 | net.addLink('r4', 'r5', intfName1='port_AS2', intfName2='port_AS1')
74 |
75 | # AS 2
76 | net.addLink('h3', 's3')
77 | net.setIntfIp('h3', 's3', '2.0.0.2/24')
78 | net.addLink('h6', 's3')
79 | net.setIntfIp('h6', 's3', '2.0.0.3/24')
80 | net.addLink('s3', 'r5', intfName2='port_S3')
81 |
82 | # Links general options
83 | net.setBwAll(5)
84 |
85 | # Nodes general options
86 | net.addTaskFile('tasks.txt')
87 | net.enablePcapDumpAll()
88 | net.enableLogAll()
89 |
90 | # Start the network
91 | net.startNetwork()
--------------------------------------------------------------------------------
/examples/frrouters/p4app.json:
--------------------------------------------------------------------------------
1 | {
2 | "p4_src": "l2_learning_digest.p4",
3 | "cli": true,
4 | "pcap_dump": true,
5 | "enable_log": true,
6 | "tasks_file": "tasks.txt",
7 | "exec_scripts": [
8 | {
9 | "cmd": "python l2_learning_controller.py s1 digest &",
10 | "reboot_run": true
11 | },
12 | {
13 | "cmd": "python l2_learning_controller.py s2 digest &",
14 | "reboot_run": true
15 | },
16 | {
17 | "cmd": "python l2_learning_controller.py s3 digest &",
18 | "reboot_run": true
19 | }
20 | ],
21 | "topology": {
22 | "default":{
23 | "auto_arp_tables": false,
24 | "auto_gw_arp": false,
25 | "bw": 5
26 | },
27 | "links": [
28 | ["h1", "s1",
29 | {"params1" : {"ip": "1.0.0.2/24"}}
30 | ],
31 | ["h4", "s1",
32 | {"params1" : {"ip": "1.0.0.3/24"}}
33 | ],
34 | ["h2", "s2",
35 | {"params1" : {"ip": "1.7.0.2/24"}}
36 | ],
37 | ["h5", "s2",
38 | {"params1" : {"ip": "1.7.0.3/24"}}
39 | ],
40 | ["h3", "s3",
41 | {"params1" : {"ip": "2.0.0.2/24"}}
42 | ],
43 | ["h6", "s3",
44 | {"params1" : {"ip": "2.0.0.3/24"}}
45 | ],
46 | ["s1", "r1",
47 | {"intfName2" : "port_S1"}
48 | ],
49 | ["s2", "r2",
50 | {"intfName2" : "port_S2"}
51 | ],
52 | ["s3", "r5",
53 | {"intfName2" : "port_S3"}
54 | ],
55 | ["r1", "r2",
56 | {"intfName1" : "port_R2", "intfName2": "port_R1"}
57 | ],
58 | ["r1", "r3",
59 | {"intfName1" : "port_R3", "intfName2": "port_R1"}
60 | ],
61 | ["r1", "r4",
62 | {"intfName1" : "port_R4", "intfName2": "port_R1"}
63 | ],
64 | ["r2", "r3",
65 | {"intfName1" : "port_R3", "intfName2": "port_R2"}
66 | ],
67 | ["r2", "r4",
68 | {"intfName1" : "port_R4", "intfName2": "port_R2"}
69 | ],
70 | ["r3", "r4",
71 | {"intfName1" : "port_R4", "intfName2": "port_R3"}
72 | ],
73 | ["r4", "r5",
74 | {"intfName1" : "port_AS2", "intfName2": "port_AS1"}
75 | ]
76 | ],
77 | "hosts": {
78 | "h1": {
79 | "defaultRoute": "via 1.0.0.1"
80 | },
81 | "h2": {
82 | "defaultRoute": "via 1.7.0.1"
83 | },
84 | "h3": {
85 | "defaultRoute": "via 2.0.0.1"
86 | },
87 | "h4": {
88 | "defaultRoute": "via 1.0.0.1"
89 | },
90 | "h5": {
91 | "defaultRoute": "via 1.7.0.1"
92 | },
93 | "h6": {
94 | "defaultRoute": "via 2.0.0.1"
95 | }
96 | },
97 | "switches": {
98 | "s1": {
99 | },
100 | "s2": {
101 | },
102 | "s3": {
103 | }
104 | },
105 | "routers": {
106 | "r1": {
107 | "int_conf": "./routers/r1.conf",
108 | "ldpd": "True"
109 | },
110 | "r2": {
111 | "int_conf": "./routers/r2.conf",
112 | "ldpd": "True"
113 | },
114 | "r3": {
115 | "int_conf": "./routers/r3.conf",
116 | "ldpd": "True"
117 | },
118 | "r4": {
119 | "int_conf": "./routers/r4.conf",
120 | "ldpd": "True"
121 | },
122 | "r5": {
123 | "int_conf": "./routers/r5.conf",
124 | "ldpd": "True"
125 | }
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/examples/frrouters/receive.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import struct
4 | import os
5 |
6 | from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr
7 | from scapy.all import Packet, IPOption
8 | from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
9 | from scapy.all import IP, UDP, Raw
10 | from scapy.layers.inet import _IPOption_HDR
11 |
12 | def get_if():
13 | ifs=get_if_list()
14 | iface=None
15 | for i in get_if_list():
16 | if "eth0" in i:
17 | iface=i
18 | break
19 | if not iface:
20 | print("Cannot find eth0 interface")
21 | exit(1)
22 | return iface
23 |
24 | class IPOption_MRI(IPOption):
25 | name = "MRI"
26 | option = 31
27 | fields_desc = [ _IPOption_HDR,
28 | FieldLenField("length", None, fmt="B",
29 | length_of="swids",
30 | adjust=lambda pkt,l:l+4),
31 | ShortField("count", 0),
32 | FieldListField("swids",
33 | [],
34 | IntField("", 0),
35 | length_from=lambda pkt:pkt.count*4) ]
36 | def handle_pkt(pkt):
37 | print("got a packet")
38 | pkt.show2()
39 | # hexdump(pkt)
40 | sys.stdout.flush()
41 |
42 |
43 | def main():
44 | ifaces = [i for i in os.listdir('/sys/class/net/') if 'eth' in i]
45 | iface = ifaces[0]
46 | print("sniffing on %s" % iface)
47 | sys.stdout.flush()
48 | sniff(filter="tcp", iface = iface,
49 | prn = lambda x: handle_pkt(x))
50 |
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/examples/frrouters/routers/r1.conf:
--------------------------------------------------------------------------------
1 | ip forwarding
2 | !
3 | no ipv6 forwarding
4 | !
5 | interface lo
6 | ip address 1.0.151.1/32
7 | !
8 | interface port_S1
9 | ip address 1.0.0.1/24
10 | ip ospf cost 1
11 | !
12 | interface port_R2
13 | ip address 1.1.0.1/24
14 | ip ospf cost 3
15 | !
16 | interface port_R3
17 | ip address 1.2.0.1/24
18 | ip ospf cost 1
19 | !
20 | interface port_R4
21 | ip address 1.5.0.1/24
22 | ip ospf cost 3
23 | !
24 | router ospf
25 | ospf router-id 1.0.151.1
26 | network 1.0.0.0/8 area 0
27 | !
28 | router bgp 1
29 | neighbor 1.0.152.1 remote-as 1
30 | neighbor 1.0.152.1 update-source lo
31 | neighbor 1.0.154.1 remote-as 1
32 | neighbor 1.0.154.1 update-source lo
33 | !
34 | address-family ipv4 unicast
35 | network 1.0.0.0/8
36 | no bgp network import-check
37 | exit-address-family
38 | !
39 | mpls ldp
40 | router-id 1.0.151.1
41 | !
42 | address-family ipv4
43 | discovery transport-address 1.0.151.1
44 | !
45 | interface port_R2
46 | !
47 | interface port_R3
48 | !
49 | interface port_R4
50 | !
51 | exit-address-family
52 | !
53 | !
54 | line vty
55 | !
--------------------------------------------------------------------------------
/examples/frrouters/routers/r2.conf:
--------------------------------------------------------------------------------
1 | ip forwarding
2 | !
3 | no ipv6 forwarding
4 | !
5 | interface lo
6 | ip address 1.0.152.1/32
7 | !
8 | interface port_S2
9 | ip address 1.7.0.1/24
10 | ip ospf cost 1
11 | !
12 | interface port_R1
13 | ip address 1.1.0.2/24
14 | ip ospf cost 3
15 | !
16 | interface port_R3
17 | ip address 1.3.0.1/24
18 | ip ospf cost 1
19 | !
20 | interface port_R4
21 | ip address 1.6.0.1/24
22 | ip ospf cost 3
23 | !
24 | router ospf
25 | ospf router-id 1.0.152.1
26 | network 1.0.0.0/8 area 0
27 | !
28 | router bgp 1
29 | neighbor 1.0.151.1 remote-as 1
30 | neighbor 1.0.151.1 update-source lo
31 | neighbor 1.0.154.1 remote-as 1
32 | neighbor 1.0.154.1 update-source lo
33 | !
34 | address-family ipv4 unicast
35 | network 1.0.0.0/8
36 | no bgp network import-check
37 | exit-address-family
38 | !
39 | mpls ldp
40 | router-id 1.0.152.1
41 | !
42 | address-family ipv4
43 | discovery transport-address 1.0.152.1
44 | !
45 | interface port_R1
46 | !
47 | interface port_R3
48 | !
49 | interface port_R4
50 | !
51 | exit-address-family
52 | !
53 | !
54 | line vty
55 | !
--------------------------------------------------------------------------------
/examples/frrouters/routers/r3.conf:
--------------------------------------------------------------------------------
1 | ip forwarding
2 | !
3 | no ipv6 forwarding
4 | !
5 | interface lo
6 | ip address 1.0.153.1/32
7 | !
8 | interface port_R1
9 | ip address 1.2.0.2/24
10 | ip ospf cost 1
11 | !
12 | interface port_R2
13 | ip address 1.3.0.2/24
14 | ip ospf cost 1
15 | !
16 | interface port_R4
17 | ip address 1.4.0.2/24
18 | ip ospf cost 1
19 | !
20 | router ospf
21 | ospf router-id 1.0.153.1
22 | network 1.0.0.0/8 area 0
23 | !
24 | mpls ldp
25 | router-id 1.0.153.1
26 | !
27 | address-family ipv4
28 | discovery transport-address 1.0.153.1
29 | !
30 | interface port_R1
31 | !
32 | interface port_R2
33 | !
34 | interface port_R4
35 | !
36 | exit-address-family
37 | !
38 | !
39 | line vty
40 | !
--------------------------------------------------------------------------------
/examples/frrouters/routers/r4.conf:
--------------------------------------------------------------------------------
1 | ip forwarding
2 | !
3 | no ipv6 forwarding
4 | !
5 | interface lo
6 | ip address 1.0.154.1/32
7 | !
8 | interface port_R1
9 | ip address 1.5.0.2/24
10 | ip ospf cost 3
11 | !
12 | interface port_R2
13 | ip address 1.6.0.2/24
14 | ip ospf cost 3
15 | !
16 | interface port_R3
17 | ip address 1.4.0.1/24
18 | ip ospf cost 1
19 | !
20 | interface port_AS2
21 | ip address 179.0.0.1/24
22 | !
23 | router ospf
24 | ospf router-id 1.0.154.1
25 | network 1.0.0.0/8 area 0
26 | !
27 | router bgp 1
28 | neighbor 1.0.151.1 remote-as 1
29 | neighbor 1.0.151.1 update-source lo
30 | neighbor 1.0.152.1 remote-as 1
31 | neighbor 1.0.152.1 update-source lo
32 | neighbor 179.0.0.2 remote-as 2
33 | !
34 | address-family ipv4 unicast
35 | network 1.0.0.0/8
36 | no bgp network import-check
37 | neighbor 1.0.151.1 next-hop-self
38 | neighbor 1.0.152.1 next-hop-self
39 | neighbor 179.0.0.2 route-map ACCEPT in
40 | neighbor 179.0.0.2 route-map ACCEPT out
41 | exit-address-family
42 | !
43 | mpls ldp
44 | router-id 1.0.154.1
45 | !
46 | address-family ipv4
47 | discovery transport-address 1.0.154.1
48 | !
49 | interface port_R1
50 | !
51 | interface port_R2
52 | !
53 | interface port_R3
54 | !
55 | exit-address-family
56 | !
57 | !
58 | route-map ACCEPT permit 10
59 | !
60 | line vty
61 | !
--------------------------------------------------------------------------------
/examples/frrouters/routers/r5.conf:
--------------------------------------------------------------------------------
1 | ip forwarding
2 | !
3 | no ipv6 forwarding
4 | !
5 | interface lo
6 | ip address 2.0.155.1/32
7 | !
8 | interface port_S3
9 | ip address 2.0.0.1/24
10 | ip ospf cost 2
11 | !
12 | interface port_AS1
13 | ip address 179.0.0.2/24
14 | ip ospf cost 2
15 | !
16 | router ospf
17 | ospf router-id 2.0.155.1
18 | network 2.0.0.0/8 area 0
19 | !
20 | router bgp 2
21 | neighbor 179.0.0.1 remote-as 1
22 | !
23 | address-family ipv4 unicast
24 | network 2.0.0.0/8
25 | no bgp network import-check
26 | neighbor 179.0.0.1 route-map ACCEPT in
27 | neighbor 179.0.0.1 route-map ACCEPT out
28 | exit-address-family
29 | !
30 | route-map ACCEPT permit 10
31 | !
32 | line vty
33 | !
--------------------------------------------------------------------------------
/examples/frrouters/send.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import argparse
3 | import sys
4 | import socket
5 | import random
6 | import struct
7 |
8 | from scapy.all import sendp, send, get_if_list, get_if_hwaddr
9 | from scapy.all import Packet
10 | from scapy.all import Ether, IP, UDP, TCP
11 |
12 | def get_if():
13 | ifs=get_if_list()
14 | iface=None # "h1-eth0"
15 | for i in get_if_list():
16 | if "eth0" in i:
17 | iface=i
18 | break
19 | if not iface:
20 | print("Cannot find eth0 interface")
21 | exit(1)
22 | return iface
23 |
24 | def main():
25 |
26 | if len(sys.argv)<3:
27 | print('pass 2 arguments: ""')
28 | exit(1)
29 |
30 | addr = socket.gethostbyname(sys.argv[1])
31 | iface = get_if()
32 |
33 | print("sending on interface %s to %s" % (iface, str(addr)))
34 | pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff')
35 | pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2]
36 | pkt.show2()
37 | sendp(pkt, iface=iface, verbose=False)
38 |
39 |
40 | if __name__ == '__main__':
41 | main()
42 |
--------------------------------------------------------------------------------
/examples/frrouters/tasks.txt:
--------------------------------------------------------------------------------
1 | # Communication in the same subnet
2 | h1 15 30 send_udp_flow --dst 1.0.0.3 --sport 5000 --dport 5051 --rate 10M
3 | h4 15 30 recv_udp_flow --dport 5051
4 | # Monitor bandwitdth
5 | s1 0 0 "python -m p4utils.utils.monitor -i s1-eth1 -d 100 h1.csv"
6 | s1 0 0 "python -m p4utils.utils.monitor -i s1-eth2 -d 100 h4.csv"
7 |
8 | # Communication in the same AS
9 | h4 45 30 send_udp_flow --dst 1.7.0.2 --sport 5001 --dport 5051 --rate 10M
10 | h2 45 30 recv_udp_flow --dport 5051
11 | # Monitor bandwitdth
12 | s2 0 0 "python -m p4utils.utils.monitor -i s2-eth1 -d 100 h2.csv"
13 |
14 | # Communication among ASes
15 | h2 60 30 send_udp_flow --dst 2.0.0.2 --sport 5002 --dport 5051 --rate 10M
16 | h3 60 30 recv_udp_flow --dport 5051
17 | # Monitor bandwitdth
18 | s3 0 0 "python -m p4utils.utils.monitor -i s3-eth1 -d 100 h3.csv"
19 |
--------------------------------------------------------------------------------
/examples/switches/README.md:
--------------------------------------------------------------------------------
1 | # Implementing Basic Forwarding
2 |
3 | ```
4 | +--+
5 | |h4|
6 | ++-+
7 | |
8 | |
9 | +--+ +--+ ++-+ +--+
10 | |h1+------+s1+-----+s3+-----+h3|
11 | +--+ +-++ +--+ +--+
12 | |
13 | |
14 | +-++
15 | |s2|
16 | +-++
17 | |
18 | |
19 | +-++
20 | |h2|
21 | +--+
22 | ```
23 |
24 | ## Introduction
25 |
26 | The objective of this exercise is to write a P4 program that
27 | implements basic forwarding. To keep things simple, we will just
28 | implement forwarding for IPv4.
29 |
30 | With IPv4 forwarding, the switch must perform the following actions
31 | for every packet: (i) update the source and destination MAC addresses,
32 | (ii) decrement the time-to-live (TTL) in the IP header, and (iii)
33 | forward the packet out the appropriate port.
34 |
35 | Your switch will have a single table, which the control plane will
36 | populate with static rules. Each rule will map an IP address to the
37 | MAC address and output port for the next hop. We have already defined
38 | the control plane rules, so you only need to implement the data plane
39 | logic of your P4 program.
40 |
41 |
42 | # Parser
43 |
44 | The parser describes a state machine with one `start` state and two possible final states: `accept`
45 | or `reject`. Explain the basic state machine used to parse ethernet and ipv4, and explain that this
46 | can be used later to access those headers fields.
--------------------------------------------------------------------------------
/examples/switches/forwarding.p4:
--------------------------------------------------------------------------------
1 | /* -*- P4_16 -*- */
2 | #include
3 | #include
4 |
5 | const bit<16> TYPE_IPV4 = 0x800;
6 |
7 | /*************************************************************************
8 | *********************** H E A D E R S ***********************************
9 | *************************************************************************/
10 |
11 | typedef bit<9> egressSpec_t;
12 | typedef bit<48> macAddr_t;
13 | typedef bit<32> ip4Addr_t;
14 |
15 | header ethernet_t {
16 | macAddr_t dstAddr;
17 | macAddr_t srcAddr;
18 | bit<16> etherType;
19 | }
20 |
21 | header ipv4_t {
22 | bit<4> version;
23 | bit<4> ihl;
24 | bit<8> diffserv;
25 | bit<16> totalLen;
26 | bit<16> identification;
27 | bit<3> flags;
28 | bit<13> fragOffset;
29 | bit<8> ttl;
30 | bit<8> protocol;
31 | bit<16> hdrChecksum;
32 | ip4Addr_t srcAddr;
33 | ip4Addr_t dstAddr;
34 | }
35 |
36 | struct metadata {
37 | /* empty */
38 | }
39 |
40 | struct headers {
41 | ethernet_t ethernet;
42 | ipv4_t ipv4;
43 | }
44 |
45 | /*************************************************************************
46 | *********************** P A R S E R ***********************************
47 | *************************************************************************/
48 |
49 | parser MyParser(packet_in packet,
50 | out headers hdr,
51 | inout metadata meta,
52 | inout standard_metadata_t standard_metadata) {
53 |
54 | state start {
55 |
56 | packet.extract(hdr.ethernet);
57 | transition select(hdr.ethernet.etherType){
58 |
59 | TYPE_IPV4: ipv4;
60 | default: accept;
61 |
62 | }
63 |
64 | }
65 |
66 | state ipv4 {
67 |
68 | packet.extract(hdr.ipv4);
69 | transition accept;
70 | }
71 |
72 | }
73 |
74 |
75 | /*************************************************************************
76 | ************ C H E C K S U M V E R I F I C A T I O N *************
77 | *************************************************************************/
78 |
79 | control MyVerifyChecksum(inout headers hdr, inout metadata meta) {
80 | apply { }
81 | }
82 |
83 |
84 | /*************************************************************************
85 | ************** I N G R E S S P R O C E S S I N G *******************
86 | *************************************************************************/
87 |
88 | control MyIngress(inout headers hdr,
89 | inout metadata meta,
90 | inout standard_metadata_t standard_metadata) {
91 |
92 | action drop() {
93 | mark_to_drop(standard_metadata);
94 | }
95 |
96 | action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
97 |
98 | //set the src mac address as the previous dst, this is not correct right?
99 | hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
100 |
101 | //set the destination mac address that we got from the match in the table
102 | hdr.ethernet.dstAddr = dstAddr;
103 |
104 | //set the output port that we also get from the table
105 | standard_metadata.egress_spec = port;
106 |
107 | //decrease ttl by 1
108 | hdr.ipv4.ttl = hdr.ipv4.ttl -1;
109 |
110 | }
111 |
112 | table ipv4_lpm {
113 | key = {
114 | hdr.ipv4.dstAddr: lpm;
115 | }
116 | actions = {
117 | ipv4_forward;
118 | drop;
119 | NoAction;
120 | }
121 | size = 1024;
122 | default_action = NoAction();
123 | }
124 |
125 | apply {
126 |
127 | //only if IPV4 the rule is applied. Therefore other packets will not be forwarded.
128 | if (hdr.ipv4.isValid()){
129 | ipv4_lpm.apply();
130 |
131 | }
132 | }
133 | }
134 |
135 | /*************************************************************************
136 | **************** E G R E S S P R O C E S S I N G *******************
137 | *************************************************************************/
138 |
139 | control MyEgress(inout headers hdr,
140 | inout metadata meta,
141 | inout standard_metadata_t standard_metadata) {
142 | apply { }
143 | }
144 |
145 | /*************************************************************************
146 | ************* C H E C K S U M C O M P U T A T I O N **************
147 | *************************************************************************/
148 |
149 | control MyComputeChecksum(inout headers hdr, inout metadata meta) {
150 | apply {
151 | update_checksum(
152 | hdr.ipv4.isValid(),
153 | { hdr.ipv4.version,
154 | hdr.ipv4.ihl,
155 | hdr.ipv4.diffserv,
156 | hdr.ipv4.totalLen,
157 | hdr.ipv4.identification,
158 | hdr.ipv4.flags,
159 | hdr.ipv4.fragOffset,
160 | hdr.ipv4.ttl,
161 | hdr.ipv4.protocol,
162 | hdr.ipv4.srcAddr,
163 | hdr.ipv4.dstAddr },
164 | hdr.ipv4.hdrChecksum,
165 | HashAlgorithm.csum16);
166 | }
167 | }
168 |
169 |
170 | /*************************************************************************
171 | *********************** D E P A R S E R *******************************
172 | *************************************************************************/
173 |
174 | control MyDeparser(packet_out packet, in headers hdr) {
175 | apply {
176 |
177 | //parsed headers have to be added again into the packet.
178 | packet.emit(hdr.ethernet);
179 | packet.emit(hdr.ipv4);
180 |
181 | }
182 | }
183 |
184 | /*************************************************************************
185 | *********************** S W I T C H *******************************
186 | *************************************************************************/
187 |
188 | //switch architecture
189 | V1Switch(
190 | MyParser(),
191 | MyVerifyChecksum(),
192 | MyIngress(),
193 | MyEgress(),
194 | MyComputeChecksum(),
195 | MyDeparser()
196 | ) main;
--------------------------------------------------------------------------------
/examples/switches/network.py:
--------------------------------------------------------------------------------
1 | from p4utils.mininetlib.network_API import NetworkAPI
2 |
3 | net = NetworkAPI()
4 |
5 | # Network general options
6 | net.setLogLevel('info')
7 | net.enableCli()
8 |
9 | # Network definition
10 | net.addP4Switch('s1')
11 | net.setP4CliInput('s1', 's1-commands.txt')
12 | net.addP4Switch('s2')
13 | net.setP4CliInput('s2', 's2-commands.txt')
14 | net.addP4Switch('s3')
15 | net.setP4CliInput('s3', 's3-commands.txt')
16 | net.setP4SourceAll('forwarding.p4')
17 |
18 | net.addHost('h1')
19 | net.addHost('h2')
20 | net.addHost('h3')
21 | net.addHost('h4')
22 |
23 | net.addLink('h1', 's1', weight=5)
24 | net.setBw('h1', 's1', 20)
25 | net.setDelay('h1', 's1', 20)
26 | net.setMaxQueueSize('h1', 's1', 100)
27 | net.setLoss('h1', 's1', 0.01)
28 | net.addLink('h2', 's2')
29 | net.addLink('s1', 's2')
30 | net.addLink('h3', 's3')
31 | net.addLink('h4', 's3')
32 | net.addLink('s1', 's3')
33 |
34 | # Assignment strategy
35 | net.mixed()
36 |
37 | # Nodes general options
38 | net.addTaskFile('tasks.txt')
39 | net.enablePcapDumpAll()
40 | net.enableLogAll()
41 |
42 | # Start the network
43 | net.startNetwork()
--------------------------------------------------------------------------------
/examples/switches/p4app.json:
--------------------------------------------------------------------------------
1 | {
2 | "p4_src": "forwarding.p4",
3 | "cli": true,
4 | "pcap_dump": true,
5 | "enable_log": true,
6 | "tasks_file": "tasks.txt",
7 | "topology": {
8 | "assignment_strategy": "mixed",
9 | "links": [["h1", "s1", {"delay":"20ms", "loss": 1, "bw": 20, "max_queue_size": 100, "weight":5}], ["h2", "s2"], ["s1", "s2"], ["h3", "s3"], ["h4", "s3"], ["s1", "s3"]],
10 | "hosts": {
11 | "h1": {
12 | },
13 | "h2": {
14 | },
15 | "h3": {
16 | },
17 | "h4": {
18 | }
19 | },
20 | "switches": {
21 | "s1": {
22 | "cli_input": "s1-commands.txt"
23 | },
24 | "s2": {
25 | "cli_input": "s2-commands.txt"
26 | },
27 | "s3": {
28 | "cli_input": "s3-commands.txt"
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/switches/receive.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import sys
3 | import struct
4 | import os
5 |
6 | from scapy.all import sniff, sendp, hexdump, get_if_list, get_if_hwaddr
7 | from scapy.all import Packet, IPOption
8 | from scapy.all import ShortField, IntField, LongField, BitField, FieldListField, FieldLenField
9 | from scapy.all import IP, UDP, Raw
10 | from scapy.layers.inet import _IPOption_HDR
11 |
12 | def get_if():
13 | ifs=get_if_list()
14 | iface=None
15 | for i in get_if_list():
16 | if "eth0" in i:
17 | iface=i
18 | break
19 | if not iface:
20 | print("Cannot find eth0 interface")
21 | exit(1)
22 | return iface
23 |
24 | class IPOption_MRI(IPOption):
25 | name = "MRI"
26 | option = 31
27 | fields_desc = [ _IPOption_HDR,
28 | FieldLenField("length", None, fmt="B",
29 | length_of="swids",
30 | adjust=lambda pkt,l:l+4),
31 | ShortField("count", 0),
32 | FieldListField("swids",
33 | [],
34 | IntField("", 0),
35 | length_from=lambda pkt:pkt.count*4) ]
36 | def handle_pkt(pkt):
37 | print("got a packet")
38 | pkt.show2()
39 | # hexdump(pkt)
40 | sys.stdout.flush()
41 |
42 |
43 | def main():
44 | ifaces = [i for i in os.listdir('/sys/class/net/') if 'eth' in i]
45 | iface = ifaces[0]
46 | print("sniffing on %s" % iface)
47 | sys.stdout.flush()
48 | sniff(filter="tcp", iface = iface,
49 | prn = lambda x: handle_pkt(x))
50 |
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/examples/switches/s1-commands.txt:
--------------------------------------------------------------------------------
1 | table_set_default ipv4_lpm drop
2 | table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:0a:00:01:01 1
3 | table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:00:02:01:00 2
4 | table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:03:01:00 3
5 |
6 |
--------------------------------------------------------------------------------
/examples/switches/s2-commands.txt:
--------------------------------------------------------------------------------
1 | table_set_default ipv4_lpm drop
2 | table_add ipv4_lpm ipv4_forward 10.0.1.1/32 => 00:00:00:01:02:00 2
3 | table_add ipv4_lpm ipv4_forward 10.0.2.2/32 => 00:00:0a:00:02:02 1
4 | table_add ipv4_lpm ipv4_forward 10.0.3.0/24 => 00:00:00:01:02:00 2
5 |
--------------------------------------------------------------------------------
/examples/switches/s3-commands.txt:
--------------------------------------------------------------------------------
1 | table_set_default ipv4_lpm drop
2 | table_add ipv4_lpm ipv4_forward 10.0.1.0/24 => 00:00:00:01:03:00 3
3 | table_add ipv4_lpm ipv4_forward 10.0.2.0/24 => 00:00:00:01:03:00 3
4 | table_add ipv4_lpm ipv4_forward 10.0.3.3/32 => 00:00:0a:00:03:03 1
5 | table_add ipv4_lpm ipv4_forward 10.0.3.4/32 => 00:00:0a:00:03:04 2
6 |
7 |
--------------------------------------------------------------------------------
/examples/switches/send.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import argparse
3 | import sys
4 | import socket
5 | import random
6 | import struct
7 |
8 | from scapy.all import sendp, send, get_if_list, get_if_hwaddr
9 | from scapy.all import Packet
10 | from scapy.all import Ether, IP, UDP, TCP
11 |
12 | def get_if():
13 | ifs=get_if_list()
14 | iface=None # "h1-eth0"
15 | for i in get_if_list():
16 | if "eth0" in i:
17 | iface=i
18 | break
19 | if not iface:
20 | print("Cannot find eth0 interface")
21 | exit(1)
22 | return iface
23 |
24 | def main():
25 |
26 | if len(sys.argv)<3:
27 | print('pass 2 arguments: ""')
28 | exit(1)
29 |
30 | addr = socket.gethostbyname(sys.argv[1])
31 | iface = get_if()
32 |
33 | print("sending on interface %s to %s" % (iface, str(addr)))
34 | pkt = Ether(src=get_if_hwaddr(iface), dst='ff:ff:ff:ff:ff:ff')
35 | pkt = pkt /IP(dst=addr) / TCP(dport=1234, sport=random.randint(49152,65535)) / sys.argv[2]
36 | pkt.show2()
37 | sendp(pkt, iface=iface, verbose=False)
38 |
39 |
40 | if __name__ == '__main__':
41 | main()
42 |
--------------------------------------------------------------------------------
/examples/switches/tasks.txt:
--------------------------------------------------------------------------------
1 | h2 10.5 30 "ping 10.0.3.3"
2 | h1 20 41 recv_udp_flow --dport 5051
3 | h3 20.9 40 send_udp_flow --dst 10.0.1.1 --dport 5051 --rate 1M
--------------------------------------------------------------------------------
/examples/tofino/README.md:
--------------------------------------------------------------------------------
1 | # Heavy hitter with tofino model switches
2 |
3 | You can use p4-utils to start the topology with Tofino switches and hosts. As
4 | first parameter you have to set the path to the SDE.
5 |
6 | ```
7 | # start network
8 | sudo python network.py
9 |
10 | # controller code
11 | mx s1
12 | $SDE/run_bfshell.sh -b `pwd`/controller_1.py
13 |
14 | # controller code
15 | mx s2
16 | $SDE/run_bfshell.sh -b `pwd`/controller_2.py
17 |
18 | # start receiver
19 | mx h2
20 | python receive.py
21 |
22 | # send 1500 packets, and observe how only 1000 are received
23 | mx h1
24 | python send.py 10.2.2.2 1500
25 | ```
26 |
27 |
--------------------------------------------------------------------------------
/examples/tofino/controller_1.py:
--------------------------------------------------------------------------------
1 | from netaddr import IPAddress
2 |
3 | # Pipe variable
4 | p4 = bfrt.heavy_hitter.pipe
5 |
6 | # Table variable
7 | l3 = p4.Ingress.ipv4_lpm
8 |
9 | # Add forwarding rules
10 | l3.add_with_ipv4_forward(dstAddr=IPAddress('10.1.1.2'), dstAddr_p_length=32, port=1, dst_addr='00:00:0a:01:01:02')
11 | l3.add_with_ipv4_forward(dstAddr=IPAddress('10.2.2.2'), dstAddr_p_length=32, port=2, dst_addr='42:dc:00:d2:d6:9f')
--------------------------------------------------------------------------------
/examples/tofino/controller_2.py:
--------------------------------------------------------------------------------
1 | from netaddr import IPAddress
2 |
3 | # Pipe variable
4 | p4 = bfrt.heavy_hitter.pipe
5 |
6 | # Table variable
7 | l3 = p4.Ingress.ipv4_lpm
8 |
9 | # Add forwarding rules
10 | l3.add_with_ipv4_forward(dstAddr=IPAddress('10.2.2.2'), dstAddr_p_length=32, port=1, dst_addr='00:00:0a:02:02:02')
11 | l3.add_with_ipv4_forward(dstAddr=IPAddress('10.1.1.2'), dstAddr_p_length=32, port=2, dst_addr='3a:e6:5d:d5:f5:0b')
--------------------------------------------------------------------------------
/examples/tofino/network.py:
--------------------------------------------------------------------------------
1 | from p4utils.utils.compiler import BF_P4C
2 | from p4utils.mininetlib.network_API import NetworkAPI
3 |
4 | import sys
5 |
6 | SDE = sys.argv[1]
7 | SDE_INSTALL = SDE + "/install"
8 |
9 | net = NetworkAPI()
10 |
11 | # Network general options
12 | net.setLogLevel('info')
13 | net.enableCli()
14 |
15 | # Tofino compiler
16 | net.setCompiler(compilerClass=BF_P4C, sde=SDE, sde_install=SDE_INSTALL)
17 |
18 | # Network definition
19 | net.addTofino('s1', sde=SDE, sde_install=SDE_INSTALL)
20 | net.addTofino('s2', sde=SDE, sde_install=SDE_INSTALL)
21 | net.setP4SourceAll('heavy_hitter.p4')
22 |
23 | net.addHost('h1')
24 | net.addHost('h2')
25 |
26 | net.addLink('h1', 's1', port2=1)
27 | net.addLink('s1', 's2', port1=2, port2=2)
28 | net.addLink('s2', 'h2', port1=1)
29 |
30 | # Assignment strategy
31 | net.l3()
32 |
33 | # Nodes general options
34 | net.enableLogAll()
35 |
36 | # Start the network
37 | net.startNetwork()
38 |
--------------------------------------------------------------------------------
/examples/tofino/receive.py:
--------------------------------------------------------------------------------
1 | from scapy.all import sniff, get_if_list, get_if_hwaddr
2 | from scapy.all import IP, TCP, Ether
3 |
4 | def get_if():
5 | ifs=get_if_list()
6 | iface=None # "h1-eth0"
7 | for i in get_if_list():
8 | if "eth0" in i:
9 | iface=i
10 | break
11 | if not iface:
12 | print("Cannot find eth0 interface")
13 | exit(1)
14 | return iface
15 |
16 |
17 | totals = {}
18 | iface = get_if()
19 |
20 |
21 | def handle_pkt(pkt):
22 | if IP in pkt and TCP in pkt:
23 | src_ip = pkt[IP].src
24 | dst_ip = pkt[IP].dst
25 | proto = pkt[IP].proto
26 | sport = pkt[TCP].sport
27 | dport = pkt[TCP].dport
28 | id_tup = (src_ip, dst_ip, proto, sport, dport)
29 |
30 | #filter packets that are sent from this interface. This is done to just focus on the receiving ones.
31 | #Some people had problems with this line since they set the src mac address to be the same than the destination, thus
32 | #all packets got filtered here.
33 | if get_if_hwaddr(iface) == pkt[Ether].src:
34 | return
35 |
36 | if id_tup not in totals:
37 | totals[id_tup] = 0
38 | totals[id_tup] += 1
39 | print("Received from %s total: %s" %
40 | (id_tup, totals[id_tup]))
41 |
42 | def main():
43 | sniff(iface = iface,
44 | prn = lambda x: handle_pkt(x))
45 |
46 | if __name__ == '__main__':
47 | main()
--------------------------------------------------------------------------------
/examples/tofino/send.py:
--------------------------------------------------------------------------------
1 | from scapy.all import Ether, IP, sendp, get_if_hwaddr, get_if_list, TCP, Raw
2 | import sys, socket, random
3 |
4 | def get_if():
5 | ifs=get_if_list()
6 | iface=None # "h1-eth0"
7 | for i in get_if_list():
8 | if "eth0" in i:
9 | iface=i
10 | break
11 | if not iface:
12 | print("Cannot find eth0 interface")
13 | exit(1)
14 | return iface
15 |
16 | def send_random_traffic(dst_ip, num_packets):
17 |
18 | dst_addr = socket.gethostbyname(dst_ip)
19 | total_pkts = 0
20 | random_port = random.randint(1024,65000)
21 | iface = get_if()
22 | #For this exercise the destination mac address is not important. Just ignore the value we use.
23 | p = Ether(dst="00:01:0a:02:02:00", src=get_if_hwaddr(iface)) / IP(dst=dst_addr)
24 | p = p / TCP(dport=random_port)
25 | for i in range(num_packets):
26 | sendp(p, iface = iface)
27 | total_pkts += 1
28 | print("Sent %s packets in total" % total_pkts)
29 |
30 | if __name__ == '__main__':
31 | if len(sys.argv) < 2:
32 | print("Usage: python send.py ")
33 | sys.exit(1)
34 | else:
35 | dst_name = sys.argv[1]
36 | num_packets = int(sys.argv[2])
37 | send_random_traffic(dst_name, num_packets)
--------------------------------------------------------------------------------
/install-tools/README.md:
--------------------------------------------------------------------------------
1 | # Installation tools
2 |
3 | The manual installation process is quite long and cumbersome because of the
4 | dependencies that are needed by P4-Utils. For this reason, we provide a
5 | [Bash script](https://github.com/nsg-ethz/p4-utils/blob/master/install-tools/install-p4-dev.sh)
6 | that automatically goes through every step.
7 |
8 | > **Warning!**
9 | > The script has been tested with **Ubuntu 20.04.6** and **Ubuntu 22.04.3**.
10 | > To install for **Ubuntu 18.04** refer to [this older script](./old_installs/install-p4-dev.sh).
11 |
12 | With the following installation methods, you will download and install *Mininet*
13 | and the P4-Tools suite (P4-Utils, P4-Learning and their dependencies) in your
14 | user's home directory.
15 |
16 | ## One-Step Automated Install
17 |
18 | To get started quickly and conveniently, you may want to install the P4-Tools suite
19 | using the following command:
20 |
21 | ```bash
22 | curl -sSL https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev.sh | bash
23 | ```
24 |
25 | ## Alternative Installation Method
26 |
27 | The main drawback of piping to `bash` is that you cannot review the code
28 | that is going to run on your system. Therefore, we provide this alternative
29 | methods that allows you to inspect the intallation script:
30 |
31 | ```bash
32 | wget -O install-p4-dev.sh https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev.sh
33 | bash install-p4-dev.sh
34 | ```
35 |
--------------------------------------------------------------------------------
/install-tools/conf_files/mininet.patch:
--------------------------------------------------------------------------------
1 | diff --git a/util/install.sh b/util/install.sh
2 | index 04f85b0..6cd4221 100755
3 | --- a/util/install.sh
4 | +++ b/util/install.sh
5 | @@ -5,6 +5,7 @@
6 |
7 | # Fail on error
8 | set -e
9 | +set -x
10 |
11 | # Fail on unset var usage
12 | set -o nounset
13 | @@ -165,7 +166,7 @@ function mn_deps {
14 | $install gcc make socat psmisc xterm openssh-clients iperf \
15 | iproute telnet python-setuptools libcgroup-tools \
16 | ethtool help2man net-tools
17 | - $install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming
18 | + $install ${PYPKG}-pyflakes pylint ${PYPKG}-pep8-naming \
19 | ${PYPKG}-pexpect
20 | elif [ "$DIST" = "SUSE LINUX" ]; then
21 | $install gcc make socat psmisc xterm openssh iperf \
22 | @@ -175,6 +176,7 @@ function mn_deps {
23 | else # Debian/Ubuntu
24 | pf=pyflakes
25 | pep8=pep8
26 | + pylint=pylint
27 | # Starting around 20.04, installing pyflakes instead of pyflakes3
28 | # causes Python 2 to be installed, which is exactly NOT what we want.
29 | if [ "$DIST" = "Ubuntu" -a `expr $RELEASE '>=' 20.04` = "1" ]; then
30 | @@ -187,10 +189,14 @@ function mn_deps {
31 | pf=pyflakes3
32 | pep8=python3-pep8
33 | fi
34 | + if [ "$DIST" = "Ubuntu" -a `expr $RELEASE '<=' 20.04` = "1" ]; then
35 | + pylint=pylint3
36 | + fi
37 |
38 | $install gcc make socat psmisc xterm ssh iperf telnet \
39 | - ethtool help2man $pf pylint $pep8 \
40 | + ethtool help2man $pylint \
41 | net-tools ${PYPKG}-tk
42 | + $install --no-install-recommends $pf $pep8
43 |
44 | # Install pip
45 | $install ${PYPKG}-pip || $install ${PYPKG}-pip-whl
46 | @@ -303,7 +309,7 @@ function install_wireshark {
47 | if ! which wireshark; then
48 | echo "Installing Wireshark"
49 | if [ "$DIST" = "Fedora" -o "$DIST" = "RedHatEnterpriseServer" ]; then
50 | - $install wireshark wireshark-gnome
51 | + $install wireshark
52 | elif [ "$DIST" = "SUSE LINUX" ]; then
53 | $install wireshark
54 | else
--------------------------------------------------------------------------------
/install-tools/conf_files/tmux.conf:
--------------------------------------------------------------------------------
1 | unbind C-b
2 | set -g prefix C-a
3 | bind-key C-a send-prefix
4 | set -g mouse on
5 | set-option -g history-limit 50000
6 | set-option -g renumber-windows on
7 |
8 | set -g default-terminal "screen-256color"
9 | set -g status-keys emacs
10 |
11 | set -g aggressive-resize on
12 |
13 | bind '"' split-window -c "#{pane_current_path}"
14 | bind % split-window -h -c "#{pane_current_path}"
15 | bind c new-window -c "#{pane_current_path}"
16 |
--------------------------------------------------------------------------------
/install-tools/scripts/protoinitfix.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # This scripts creates a __init__.py file in the main directory of
3 | # the google module if it is not present already. Indeed the lack of such
4 | # file prevent sphinx from working correctly.
5 |
6 | import os
7 | import re
8 | import google
9 |
10 |
11 | path = re.findall(r"\['(.*)'\]", str(google.__path__))[0]
12 | init_file = os.path.join(path, '__init__.py')
13 |
14 | # If __init__.py is not present in the google module directory
15 | if not os.path.isfile(init_file):
16 | # Create __init__.py file
17 | with open(init_file, 'w') as f:
18 | pass
19 | print('File {} correctly created.'.format(init_file))
20 | else:
21 | print('File {} already present.'.format(init_file))
--------------------------------------------------------------------------------
/install-tools/scripts/py3localpath.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified file from
3 | # https://github.com/jafingerhut/p4-guide/blob/master/bin/py3localpath.py
4 |
5 | import re
6 | import sys
7 |
8 | l1=[x for x in sys.path if re.match(r'/usr/local/lib/python3.[0-9]+/dist-packages$', x)]
9 |
10 | if len(l1) == 1:
11 | m = re.match(r'(/usr/local/lib/python3.[0-9]+)/dist-packages$', l1[0])
12 | if m:
13 | print(m.group(1))
14 | else:
15 | print("Inconceivable! Somehow the second pattern did not match but the first did.")
16 | sys.exit(1)
17 | else:
18 | print("Found %d matching entries in Python3 sys.path instead of 1: %s."
19 | % (len(l1), l1))
20 | sys.exit(1)
21 |
22 | sys.exit(0)
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # p4-utils branch
4 | P4_UTILS_BRANCH="master"
5 |
6 | install_p4utils() {
7 | pip3 install -e "."
8 | }
9 |
10 | mx() {
11 |
12 | BINDIR=/usr/bin
13 | MANDIR=/usr/share/man/man1
14 |
15 | UTILSDIR=utils
16 |
17 | cd ${UTILSDIR}
18 |
19 | #compile mxexec
20 | cc -Wall -Wextra -DVERSION=\"1.4\" mxexec.c -o mxexec
21 | #create man page
22 | help2man -N -n "Mininet namespace execution utility" -h "-h" -v "-v" --no-discard-stderr ./mxexec -o mxexec.1
23 |
24 | #install
25 | install mxexec ${BINDIR}
26 | install mx ${BINDIR}
27 | install mxexec.1 ${MANDIR}
28 |
29 | cd ..
30 | }
31 |
32 | PY3LOCALPATH=`curl -sSL https://raw.githubusercontent.com/nsg-ethz/p4-utils/${P4_UTILS_BRANCH}/install-tools/scripts/py3localpath.py | python3`
33 | function site_packages_fix {
34 | local SRC_DIR
35 | local DST_DIR
36 |
37 | SRC_DIR="${PY3LOCALPATH}/site-packages"
38 | DST_DIR="${PY3LOCALPATH}/dist-packages"
39 |
40 | # When I tested this script on Ubunt 16.04, there was no
41 | # site-packages directory. Return without doing anything else if
42 | # this is the case.
43 | if [ ! -d ${SRC_DIR} ]; then
44 | return 0
45 | fi
46 |
47 | echo "Adding ${SRC_DIR} to Python3 path..."
48 | sudo su -c "echo '${SRC_DIR}' > ${DST_DIR}/p4-tools.pth"
49 | echo "Done!"
50 | }
51 |
52 | install_p4utils
53 | mx
54 | site_packages_fix
55 |
--------------------------------------------------------------------------------
/p4app_example.json:
--------------------------------------------------------------------------------
1 | {
2 | "p4_src": "" (string),
3 | "cli": "" (bool),
4 | "pcap_dump": "" (bool),
5 | "enable_log": "" (bool),
6 | "tasks_file": "" (string),
7 | "host_node":
8 | {
9 | "file_path": "" (string),
10 | "module_name": "" (string),
11 | "object_name": "" (string)
12 | },
13 | "switch_node":
14 | {
15 | "file_path": "" (string),
16 | "module_name": "" (string),
17 | "object_name": "" (string)
18 | },
19 | "router_node":
20 | {
21 | "file_path": "" (string),
22 | "module_name": "" (string),
23 | "object_name": "" (string)
24 | },
25 | "compiler_module":
26 | {
27 | "file_path": "" (string),
28 | "module_name": "" (string),
29 | "object_name": "" (string),
30 | "options": "" (dict)
31 | },
32 | "client_module":
33 | {
34 | "file_path": "" (string),
35 | "module_name": "" (string),
36 | "object_name": "" (string),
37 | "options": "" (dict)
38 | },
39 | "mininet_module":
40 | {
41 | "file_path": "" (string),
42 | "module_name": "" (string),
43 | "object_name": "" (string)
44 | },
45 | "exec_scripts":
46 | [
47 | {
48 | "cmd": "" (string),
49 | "reboot_run": "" (bool)
50 | },
51 | ...
52 | ],
53 | "topology":
54 | {
55 | "assignment_strategy": "" (string),
56 | "default":
57 | {
58 | "weight": "" (int) (*),
59 | "bw": "" (int) (*),
60 | "delay": "" (int) (*),
61 | "loss": "" (float) (*),
62 | "max_queue_size": "" (int) (*),
63 | "auto_arp_tables": "" (bool) (*),
64 | "auto_gw_arp": "" (bool) (*)
65 | },
66 | "links":
67 | [
68 | [
69 | "r1",
70 | "h1",
71 | {
72 | "weight": " " (int) (*),
73 | "port1": "" (int) (*),
74 | "port2": "" (int) (*),
75 | "intfName1": "" (string) (*),
76 | "intfName2": "" (string) (*),
77 | "addr1": "" (string) (*),
78 | "addr2": "" (string) (*),
79 | "params1": "" (dict) (*),
80 | "params2": "" (dict) (*),
81 | "bw": "" (int) (*),
82 | "delay": "" (int) (*),
83 | "loss": " " (float) (*),
84 | "max_queue_size": "" (int) (*)
85 | }
86 | ],
87 | ...
88 | ],
89 | "hosts":
90 | {
91 | "h1":
92 | {
93 | "scheduler": "" (bool) (*),
94 | "socket_path": "" (string) (*),
95 | "defaultRoute": "via """ (string) (*),
96 | "dhcp": "" (bool) (*),
97 | "log_enabled" : "" (bool) (*),
98 | "log_dir": "" (string) (*),
99 | "host_node": "" (dict) (*)
100 | },
101 | ...
102 | },
103 | "switches":
104 | {
105 | "s1":
106 | {
107 | "p4_src": "" (string) (*),
108 | "cpu_port": "" (bool) (*),
109 | "cli_input": "" (string) (*),
110 | "switch_node": "" (dict) (*),
111 | "log_enabled" : "" (bool) (*),
112 | "log_dir": "" (string) (*),
113 | "pcap_dump": "" (bool) (*),
114 | "pcap_dir": "" (string) (*),
115 | "sw_bin": "" (string) (*),
116 | "thrift_port": "" (int) (*),
117 | "grpc_port": "" (int) (*)
118 | },
119 | ...
120 | },
121 | "routers":
122 | {
123 | "r1":
124 | {
125 | "int_conf": "" (string),
126 | "conf_dir": "" (string),
129 | "router_node": "" (dict) (*),
130 | "zebra": "" (bool) (*),
131 | "bgpd": "" (bool) (*),
132 | "ospfd": "" (bool) (*),
133 | "ospf6d": "" (bool) (*),
134 | "ripd": "" (bool) (*),
135 | "ripngd": "" (bool) (*),
136 | "isisd": "" (bool) (*),
137 | "pimd": "" (bool) (*),
138 | "ldpd": "" (bool) (*),
139 | "nhrpd": "" (bool) (*),
140 | "eigrpd": "" (bool) (*),
141 | "babeld": "" (bool) (*),
142 | "sharpd": "" (bool) (*),
143 | "staticd": "" (bool) (*),
144 | "pbrd": "" (bool) (*),
145 | "bfdd": "" (bool) (*),
146 | "fabricd" : "" (bool) (*)
147 | },
148 | ...
149 | }
150 | }
151 | }
--------------------------------------------------------------------------------
/p4utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/p4utils/__init__.py
--------------------------------------------------------------------------------
/p4utils/mininetlib/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/p4utils/mininetlib/__init__.py
--------------------------------------------------------------------------------
/p4utils/mininetlib/log.py:
--------------------------------------------------------------------------------
1 | """__ https://github.com/mininet/mininet/blob/master/mininet/log.py
2 |
3 | This module is an extension of `mininet.log`__ that implements colored logs.
4 | """
5 |
6 | import sys
7 | import logging
8 | from mininet.log import *
9 |
10 |
11 | class ShellStyles:
12 | """Shell styles."""
13 | reset='\033[0m'
14 | bold='\033[01m'
15 | disable='\033[02m'
16 | underline='\033[04m'
17 | reverse='\033[07m'
18 | strikethrough='\033[09m'
19 | invisible='\033[08m'
20 |
21 |
22 | class ShellFGColors:
23 | """Shell foreground colors."""
24 | black='\033[30m'
25 | red='\033[31m'
26 | green='\033[32m'
27 | orange='\033[33m'
28 | blue='\033[34m'
29 | purple='\033[35m'
30 | cyan='\033[36m'
31 | lightgrey='\033[37m'
32 | darkgrey='\033[90m'
33 | lightred='\033[91m'
34 | lightgreen='\033[92m'
35 | yellow='\033[93m'
36 | lightblue='\033[94m'
37 | pink='\033[95m'
38 | lightcyan='\033[96m'
39 |
40 |
41 | class ShellBGColors:
42 | """Shell background colors."""
43 | black='\033[40m'
44 | red='\033[41m'
45 | green='\033[42m'
46 | orange='\033[43m'
47 | blue='\033[44m'
48 | purple='\033[45m'
49 | cyan='\033[46m'
50 | lightgrey='\033[47m'
51 | darkgrey='\033[100m'
52 | lightred='\033[101m'
53 | lightgreen='\033[102m'
54 | yellow='\033[103m'
55 | lightblue='\033[104m'
56 | pink='\033[105m'
57 | lightcyan='\033[106m'
58 |
59 |
60 | LOG_FORMAT = {
61 | LEVELS['debug']: ShellStyles.disable,
62 | LEVELS['info']: ShellStyles.reset,
63 | LEVELS['output']: ShellStyles.bold,
64 | LEVELS['warning']: ShellStyles.bold + ShellFGColors.yellow,
65 | LEVELS['warn']: ShellStyles.bold + ShellFGColors.yellow,
66 | LEVELS['error']: ShellStyles.bold + ShellFGColors.red,
67 | LEVELS['critical']: ShellStyles.bold + ShellBGColors.red
68 | }
69 |
70 |
71 | class ColoredFormatter(logging.Formatter):
72 | """Get colored logs."""
73 | def format(self, record):
74 | s = super().format(record)
75 | if record.levelno in LOG_FORMAT:
76 | s = LOG_FORMAT[record.levelno] + s
77 | if record.levelno == LEVELS['critical']:
78 | s += '\n'
79 | if s[-1] == '\n':
80 | s = s[:-1] + ShellStyles.reset + '\n'
81 | else:
82 | s += ShellStyles.reset
83 | return s
84 |
85 |
86 | # Add critical level
87 | critical = lg.critical
88 |
89 | # Set formatter
90 | formatter = ColoredFormatter( LOGMSGFORMAT )
91 | lg.ch.setFormatter( formatter )
92 |
93 | # Handle exceptions as critical
94 | def excepthook(type, value, traceback):
95 | critical('', exc_info=(type, value, traceback))
96 |
97 | sys.excepthook = excepthook
--------------------------------------------------------------------------------
/p4utils/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/p4utils/utils/__init__.py
--------------------------------------------------------------------------------
/p4utils/utils/client.py:
--------------------------------------------------------------------------------
1 | import os
2 | import subprocess
3 |
4 | from p4utils.utils.helper import *
5 | from p4utils.mininetlib.log import debug, info, error
6 |
7 | class ThriftClient:
8 | """
9 | This controller reads commands from a thrift configuration
10 | file and uses it to set up the thrift switch.
11 |
12 |
13 | Args:
14 | cli_bin (string) : client binary file path.
15 | cli_input (string) : path of the configuration text file.
16 | log_enabled (bool) : whether to enable logs.
17 | log_dir (string) : directory to store logs.
18 | thrift_port (int) : thrift server thrift_port number.
19 | sw_name (string) : name of the switch to configure.
20 | """
21 | cli_bin = 'simple_switch_CLI'
22 |
23 | @classmethod
24 | def set_binary(self, cli_bin):
25 | """Set class default binary."""
26 | ThriftClient.cli_bin = cli_bin
27 |
28 | def __init__(self, thrift_port,
29 | sw_name,
30 | cli_bin=None,
31 | cli_input=None,
32 | log_enabled=True,
33 | log_dir='/tmp',
34 | **kwargs):
35 |
36 | self.set_conf(cli_input)
37 | self.sw_name = sw_name
38 | self.thrift_port = thrift_port
39 | self.log_enabled = log_enabled
40 | self.log_dir = log_dir
41 |
42 | if self.log_enabled:
43 | # Make sure that the provided log path is not pointing to a file
44 | # and, if necessary, create an empty log dir
45 | if not os.path.isdir(self.log_dir):
46 | if os.path.exists(self.log_dir):
47 | raise NotADirectoryError("'{}' exists and is not a directory.".format(self.log_dir))
48 | else:
49 | os.mkdir(self.log_dir)
50 |
51 | if cli_bin is not None:
52 | self.set_binary(cli_bin)
53 |
54 | def get_conf(self):
55 | """Returns self.cli_input"""
56 | return self.cli_input
57 |
58 | def set_conf(self, cli_input):
59 | """Set the configuration file path."""
60 | # Check whether the conf file is valid
61 | if cli_input is not None:
62 | self.cli_input = os.path.realpath(cli_input)
63 | else:
64 | self.cli_input = None
65 |
66 | def configure(self):
67 | """This method configures the switch with the provided file."""
68 | if self.cli_input is not None:
69 | if not os.path.isfile(self.cli_input):
70 | raise FileNotFoundError('could not find file {} for switch {}.'.format(self.cli_input, self.sw_name))
71 | elif check_listening_on_port(self.thrift_port):
72 | log_path = self.log_dir + '/{}_cli_output.log'.format(self.sw_name)
73 | with open(self.cli_input, 'r') as fin:
74 | entries = [x.strip() for x in fin.readlines() if x.strip() != '']
75 | # Remove comments
76 | entries = [x for x in entries if ( not x.startswith('//') and not x.startswith('#')) ]
77 | # Join commands
78 | entries = '\n'.join(entries)
79 | # Execute commands
80 | debug(self.cli_bin + ' --thrift-port ' + str(self.thrift_port) + '\n')
81 | p = subprocess.Popen([self.cli_bin, '--thrift-port', str(self.thrift_port)],
82 | stdin=subprocess.PIPE,
83 | stdout=subprocess.PIPE,
84 | stderr=subprocess.STDOUT)
85 | stdout, _ = p.communicate(input=entries.encode())
86 | stdout = stdout.decode(errors='backslashreplace')
87 | # Save logs
88 | if self.log_enabled:
89 | with open(log_path, 'w') as log_file:
90 | log_file.write(stdout)
91 | # Successful configuration
92 | success = True
93 | # Check for errors in the commands (they are in the stdout)
94 | if 'Invalid' in stdout or 'Error' in stdout:
95 | error('Switch {}: error in file {}, '
96 | 'check {} for details.\n'.format(self.sw_name,
97 | self.cli_input,
98 | log_path))
99 | success = False
100 | # Check returncode
101 | if p.returncode != 0:
102 | error('Switch {}: thrift client exited with error, '
103 | 'check {} for details.\n'.format(self.sw_name,
104 | log_path))
105 |
106 | success = False
107 | # Print success
108 | if success:
109 | info('Switch {}: successfully configured with file {}.\n'.format(self.sw_name,
110 | self.cli_input))
111 | else:
112 | raise ConnectionRefusedError('could not connect to switch {} on port {}.'.format(self.sw_name, self.thrift_port))
113 | else:
114 | raise FileNotFoundError('could not find file {} for switch {}.'.format(self.cli_input, self.sw_name))
115 |
--------------------------------------------------------------------------------
/p4utils/utils/monitor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import csv
3 | import time
4 | import argparse
5 |
6 | class Monitor:
7 | """Monitor the interface bandwidth and dump a .csv file with
8 | the rates in Mbps.
9 |
10 | Args:
11 | csv_file (string): path to the output file
12 | i (string) : name of the interface to monitor
13 | t (float) : interval between data points
14 | d (float) : monitoring duration
15 | """
16 |
17 | def __init__(self, csv_file, i, t=0.5, d=60):
18 |
19 | current_time = time.time()
20 | start_time = current_time
21 | stop_time = current_time + d
22 | old_tx = None
23 | old_rx = None
24 | data = []
25 |
26 | while current_time < stop_time:
27 | current_time = time.time()
28 | with open('/sys/class/net/{}/statistics/tx_bytes'.format(i), 'r') as tx:
29 | with open('/sys/class/net/{}/statistics/rx_bytes'.format(i), 'r') as rx:
30 | tx = int(tx.read()) * 8
31 | rx = int(rx.read()) * 8
32 | if not (old_tx is None or old_rx is None):
33 | delta_tx = tx - old_tx
34 | delta_rx = rx - old_rx
35 | tx_rate = (delta_tx / t) / 10**6
36 | rx_rate = (delta_rx / t) / 10**6
37 | row = {
38 | 'time': current_time-start_time,
39 | 'tx_rate': tx_rate,
40 | 'rx_rate': rx_rate
41 | }
42 | data.append(row)
43 | old_tx = tx
44 | old_rx = rx
45 | time.sleep(max(current_time + t - time.time(), 0))
46 |
47 | with open(csv_file, 'w', newline='') as f:
48 | fieldnames = ['time', 'tx_rate', 'rx_rate']
49 | writer = csv.DictWriter(f, fieldnames=fieldnames)
50 | writer.writeheader()
51 | for row in data:
52 | writer.writerow(row)
53 |
54 | def get_args():
55 | parser = argparse.ArgumentParser()
56 |
57 | parser.add_argument('-i', metavar='intf', help='interface to monitor', type=str, required=True)
58 | parser.add_argument('-t', metavar='interval', help='interval between data points in seconds', type=float, required=False, default=0.5)
59 | parser.add_argument('-d', metavar='duration', help='monitoring duration in seconds', required=False, type=float, default=60)
60 | parser.add_argument('csv', metavar='OUTFILE', type=str, help='csv dump file')
61 |
62 | return parser.parse_args()
63 |
64 | if __name__ == '__main__':
65 |
66 | args = get_args()
67 | monitor = Monitor(args.csv,
68 | args.i,
69 | t=args.t,
70 | d=args.d)
--------------------------------------------------------------------------------
/p4utils/utils/p4runtime_API/README.md:
--------------------------------------------------------------------------------
1 | # P4Runtime Client
2 | This is a pure API and multi-switch version of the [p4runtime-shell](https://github.com/p4lang/p4runtime-shell) repository.
3 |
--------------------------------------------------------------------------------
/p4utils/utils/p4runtime_API/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nsg-ethz/p4-utils/83b118bbae530b31cc74e7fa32f9174f7c0a1184/p4utils/utils/p4runtime_API/__init__.py
--------------------------------------------------------------------------------
/p4utils/utils/p4runtime_API/bytes_utils.py:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Barefoot Networks, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | from ipaddr import IPv4Address, IPv6Address, AddressValueError
17 |
18 | from p4utils.utils.p4runtime_API.utils import UserError
19 |
20 |
21 | class UserBadIPv4Error(UserError):
22 | def __init__(self, addr):
23 | self.addr = addr
24 |
25 | def __str__(self):
26 | return "'{}' is not a valid IPv4 address".format(self.addr)
27 |
28 | def _render_traceback_(self):
29 | return [str(self)]
30 |
31 |
32 | class UserBadIPv6Error(UserError):
33 | def __init__(self, addr):
34 | self.addr = addr
35 |
36 | def __str__(self):
37 | return "'{}' is not a valid IPv6 address".format(self.addr)
38 |
39 | def _render_traceback_(self):
40 | return [str(self)]
41 |
42 |
43 | class UserBadMacError(UserError):
44 | def __init__(self, addr):
45 | self.addr = addr
46 |
47 | def __str__(self):
48 | return "'{}' is not a valid MAC address".format(self.addr)
49 |
50 | def _render_traceback_(self):
51 | return [str(self)]
52 |
53 |
54 | class UserBadValueError(UserError):
55 | def __init__(self, info=""):
56 | self.info = info
57 |
58 | def __str__(self):
59 | return self.info
60 |
61 | def _render_traceback_(self):
62 | return [str(self)]
63 |
64 |
65 | def ipv4Addr_to_bytes(addr):
66 | try:
67 | ip = IPv4Address(addr)
68 | except AddressValueError:
69 | raise UserBadIPv4Error(addr)
70 | return ip.packed
71 |
72 |
73 | def ipv6Addr_to_bytes(addr):
74 | try:
75 | ip = IPv6Address(addr)
76 | except AddressValueError:
77 | raise UserBadIPv6Error(addr)
78 | return ip.packed
79 |
80 |
81 | def macAddr_to_bytes(addr):
82 | bytes_ = [int(b, 16) for b in addr.split(':')]
83 | if len(bytes_) != 6:
84 | raise UserBadMacError(addr)
85 | return bytes(bytes_)
86 |
87 |
88 | def parse_value(value_str, bitwidth, base=0):
89 | if bitwidth == 32 and '.' in value_str:
90 | return ipv4Addr_to_bytes(value_str)
91 | elif bitwidth == 48 and ':' in value_str:
92 | return macAddr_to_bytes(value_str)
93 | elif bitwidth == 128 and ':' in value_str:
94 | return ipv6Addr_to_bytes(value_str)
95 | try:
96 | value = int(value_str, base)
97 | except ValueError:
98 | raise UserBadValueError(
99 | "Invalid value '{}': could not cast to integer, try in hex with 0x prefix".format(
100 | value_str))
101 | nbytes = (bitwidth + 7) // 8
102 | try:
103 | return value.to_bytes(nbytes, byteorder='big')
104 | except OverflowError:
105 | raise UserBadValueError(
106 | "Invalid value '{}': cannot be represented with '{}' bytes".format(
107 | value_str, nbytes))
108 |
--------------------------------------------------------------------------------
/p4utils/utils/p4runtime_API/context.py:
--------------------------------------------------------------------------------
1 | # Copyright 2019 Barefoot Networks, Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | #
15 |
16 | from collections import Counter
17 | import enum
18 | from functools import partialmethod
19 |
20 |
21 | @enum.unique
22 | class P4Type(enum.Enum):
23 | table = 1
24 | action = 2
25 | action_profile = 3
26 | counter = 4
27 | direct_counter = 5
28 | meter = 6
29 | direct_meter = 7
30 | digest = 8
31 |
32 |
33 | P4Type.table.p4info_name = "tables"
34 | P4Type.action.p4info_name = "actions"
35 | P4Type.action_profile.p4info_name = "action_profiles"
36 | P4Type.counter.p4info_name = "counters"
37 | P4Type.direct_counter.p4info_name = "direct_counters"
38 | P4Type.meter.p4info_name = "meters"
39 | P4Type.direct_meter.p4info_name = "direct_meters"
40 | P4Type.digest.p4info_name = "digests"
41 |
42 |
43 | for obj_type in P4Type:
44 | obj_type.pretty_name = obj_type.name.replace('_', ' ')
45 | obj_type.pretty_names = obj_type.pretty_name + 's'
46 |
47 |
48 | @enum.unique
49 | class P4RuntimeEntity(enum.Enum):
50 | table_entry = 1
51 | action_profile_member = 2
52 | action_profile_group = 3
53 | meter_entry = 4
54 | direct_meter_entry = 5
55 | counter_entry = 6
56 | direct_counter_entry = 7
57 | packet_replication_engine_entry = 8
58 | digest_entry = 9
59 |
60 |
61 | class Context:
62 | def __init__(self):
63 | self.p4info = None
64 |
65 | def set_p4info(self, p4info):
66 | self.p4info = p4info
67 | self.p4info_obj_map = {}
68 | self.p4info_obj_map_by_id = {}
69 | self.p4info_objs_by_type = {}
70 | self._import_p4info_names()
71 |
72 | def get_obj(self, obj_type, name):
73 | key = (obj_type, name)
74 | return self.p4info_obj_map.get(key, None)
75 |
76 | def get_obj_id(self, obj_type, name):
77 | obj = self.get_obj(obj_type, name)
78 | if obj is None:
79 | return None
80 | return obj.preamble.id
81 |
82 | def get_param(self, action_name, name):
83 | a = self.get_obj(P4Type.action, action_name)
84 | if a is None:
85 | return None
86 | for p in a.params:
87 | if p.name == name:
88 | return p
89 |
90 | def get_param_len(self, action_name):
91 | a = self.get_obj(P4Type.action, action_name)
92 | if a is None:
93 | return None
94 | return len(a.params)
95 |
96 | def get_mf(self, table_name, name):
97 | t = self.get_obj(P4Type.table, table_name)
98 | if t is None:
99 | return None
100 | for mf in t.match_fields:
101 | if mf.name == name:
102 | return mf
103 |
104 | def get_mf_len(self, table_name):
105 | t = self.get_obj(P4Type.table, table_name)
106 | if t is None:
107 | return None
108 | return len(t.match_fields)
109 |
110 | def get_param_id(self, action_name, name):
111 | p = self.get_param(action_name, name)
112 | return None if p is None else p.id
113 |
114 | def get_mf_id(self, table_name, name):
115 | mf = self.get_mf(table_name, name)
116 | return None if mf is None else mf.id
117 |
118 | def get_param_name(self, action_name, id_):
119 | a = self.get_obj(P4Type.action, action_name)
120 | if a is None:
121 | return None
122 | for p in a.params:
123 | if p.id == id_:
124 | return p.name
125 |
126 | def get_mf_name(self, table_name, id_):
127 | t = self.get_obj(P4Type.table, table_name)
128 | if t is None:
129 | return None
130 | for mf in t.match_fields:
131 | if mf.id == id_:
132 | return mf.name
133 |
134 | def get_objs(self, obj_type):
135 | m = self.p4info_objs_by_type[obj_type]
136 | for name, obj in m.items():
137 | yield name, obj
138 |
139 | def get_name_from_id(self, id_):
140 | return self.p4info_obj_map_by_id[id_].preamble.name
141 |
142 | def get_obj_by_id(self, id_):
143 | return self.p4info_obj_map_by_id[id_]
144 |
145 | # In order to make the CLI easier to use, we accept any suffix that
146 | # uniquely identifies the object among p4info objects of the same type.
147 | def _import_p4info_names(self):
148 | suffix_count = Counter()
149 | for obj_type in P4Type:
150 | self.p4info_objs_by_type[obj_type] = {}
151 | for obj in getattr(self.p4info, obj_type.p4info_name):
152 | pre = obj.preamble
153 | self.p4info_obj_map_by_id[pre.id] = obj
154 | self.p4info_objs_by_type[obj_type][pre.name] = obj
155 | suffix = None
156 | for s in reversed(pre.name.split(".")):
157 | suffix = s if suffix is None else s + "." + suffix
158 | key = (obj_type, suffix)
159 | self.p4info_obj_map[key] = obj
160 | suffix_count[key] += 1
161 | for key, c in suffix_count.items():
162 | if c > 1:
163 | del self.p4info_obj_map[key]
164 |
165 |
166 | # Add p4info object and object id "getters" for each object type; these are just
167 | # wrappers around Context.get_obj and Context.get_obj_id.
168 | # For example: get_table(x) and get_table_id(x) respectively call
169 | # get_obj(P4Type.table, x) and get_obj_id(P4Type.table, x)
170 | for obj_type in P4Type:
171 | name = "_".join(["get", obj_type.name])
172 | setattr(Context, name, partialmethod(
173 | Context.get_obj, obj_type))
174 | name = "_".join(["get", obj_type.name, "id"])
175 | setattr(Context, name, partialmethod(
176 | Context.get_obj_id, obj_type))
177 |
178 | for obj_type in P4Type:
179 | name = "_".join(["get", obj_type.p4info_name])
180 | setattr(Context, name, partialmethod(Context.get_objs, obj_type))
181 |
--------------------------------------------------------------------------------
/p4utils/utils/p4runtime_API/utils.py:
--------------------------------------------------------------------------------
1 | # See https://stackoverflow.com/a/32997046
2 | def my_partialmethod(func, *args1, **kwargs1):
3 | def method(self, *args2, **kwargs2):
4 | return func(self, *args1, *args2, **kwargs1, **kwargs2)
5 | return method
6 |
7 |
8 | class UserError(Exception):
9 | def __init__(self, info=""):
10 | self.info = info
11 |
12 | def __str__(self):
13 | return self.info
14 |
15 | # TODO(antonin): is this the best way to get a custom traceback?
16 | def _render_traceback_(self):
17 | return [str(self)]
18 |
19 |
20 | class InvalidP4InfoError(Exception):
21 | def __init__(self, info=""):
22 | self.info = info
23 |
24 | def __str__(self):
25 | return "Invalid P4Info message: {}".format(self.info)
26 |
27 | def _render_traceback_(self):
28 | return [str(self)]
29 |
--------------------------------------------------------------------------------
/p4utils/utils/traffic_utils.py:
--------------------------------------------------------------------------------
1 | import math
2 | import time
3 | import socket
4 |
5 |
6 | def setSizeToInt(size):
7 | """Converts the sizes string notation to the corresponding integer
8 | (in bytes). Input size can be given with the following magnitudes: B, K, M and G.
9 | """
10 | if isinstance(size, int):
11 | return size
12 | elif isinstance(size, float):
13 | return int(size)
14 | try:
15 | conversions = {'B': 1, 'K': 1e3, 'M': 1e6, 'G': 1e9}
16 | digits_list = list(range(48, 58)) + [ord(".")]
17 | magnitude = chr(
18 | sum([ord(x) if (ord(x) not in digits_list) else 0 for x in size]))
19 | digit = float(size[0:(size.index(magnitude))])
20 | magnitude = conversions[magnitude]
21 | return int(magnitude*digit)
22 | except:
23 | print("Conversion Fail")
24 | return 0
25 |
26 |
27 | def send_udp_flow(dst="10.0.0.2", sport=5000, dport=5001, tos=0, rate='10M', duration=0,
28 | packet_size=1400, batch_size=1, **kwargs):
29 | """Udp sending function that keeps a constant rate and logs sent packets to a file.
30 |
31 | Args:
32 | dst (str, optional): destination IP. Defaults to "10.0.0.2".
33 | sport (int, optional): destination port. Defaults to 5000.
34 | dport (int, optional): source port. Defaults to 5001.
35 | tos (int, optional): type of service. Defaults to 0.
36 | rate (str, optional): flow rate. Defaults to '10M'.
37 | duration (int, optional): flow duration. Defaults to 0, i.e. no time limit.
38 | packet_size (int, optional): packet size. Defaults to 1400.
39 | batch_size (int, optional): batch size. Defaults to 1.
40 | """
41 |
42 | sport = int(sport)
43 | dport = int(dport)
44 | packet_size = int(packet_size)
45 | tos = int(tos)
46 |
47 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
48 | s.setsockopt(socket.SOL_IP, socket.IP_TOS, tos)
49 | s.bind(('', sport))
50 |
51 | rate = int(setSizeToInt(rate)/8)
52 | totalTime = float(duration)
53 |
54 | # we use 17 to correct a bit the bw
55 | packet = b"A" * int((packet_size - 17))
56 | seq = 0
57 |
58 | try:
59 | startTime = time.time()
60 | while True:
61 |
62 | # If a finite duration is given
63 | if totalTime > 0:
64 | if time.time() - startTime >= totalTime:
65 | break
66 |
67 | packets_to_send = rate/packet_size
68 | times = math.ceil((float(rate) / (packet_size))/batch_size)
69 | time_step = 1/times
70 | start = time.time()
71 | i = 0
72 | packets_sent = 0
73 | # batches of 1 sec
74 | while packets_sent < packets_to_send:
75 | for _ in range(batch_size):
76 | s.sendto(seq.to_bytes(4, byteorder='big') +
77 | packet, (dst, dport))
78 | # sequence_numbers.append(seq)
79 | packets_sent += 1
80 | seq += 1
81 |
82 | i += 1
83 | next_send_time = start + (i * time_step)
84 | time.sleep(max(0, next_send_time - time.time()))
85 | # return
86 | time.sleep(max(0, 1-(time.time()-start)))
87 |
88 | finally:
89 | s.close()
90 |
91 |
92 | def recv_udp_flow(dport):
93 | """
94 | Receiving function.
95 |
96 | Args:
97 | dport (int): port to listen
98 | """
99 | dport = int(dport)
100 | s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
101 | s.bind(("", dport))
102 | try:
103 | while True:
104 | data, address = s.recvfrom(2048)
105 | except:
106 | s.close()
107 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | "Setuptools params"
4 |
5 | from setuptools import setup, find_packages
6 |
7 | VERSION = '0.2'
8 |
9 | modname = distname = 'p4utils'
10 |
11 | def readme():
12 |
13 | with open('README.md','r') as f:
14 | return f.read()
15 |
16 | setup(
17 | name=distname,
18 | version=VERSION,
19 | description='P4 language and bmv2 model utilities',
20 | author='Edgar Costa Molero',
21 | author_email='cedgar@ethz.ch',
22 | packages=find_packages(),
23 | long_description=readme(),
24 | entry_points={'console_scripts': ['p4run = p4utils.p4run:main']},
25 | include_package_data = True,
26 | classifiers=[
27 | "License :: OSI Approved :: BSD License",
28 | "Programming Language :: Python 3",
29 | "Development Status :: 2 - Pre-Alpha",
30 | "Intended Audience :: Developers",
31 | "Topic :: System :: Networking",
32 | ],
33 | keywords='networking p4 mininet',
34 | license='GPLv2',
35 | install_requires=[
36 | 'googleapis-common-protos >= 1.52',
37 | 'grpcio == 1.44.0',
38 | 'ipaddr',
39 | 'ipaddress',
40 | 'networkx',
41 | 'p4runtime',
42 | 'protobuf == 3.20.3',
43 | 'psutil',
44 | 'scapy >= 2.5.0',
45 | 'setuptools',
46 | ],
47 | extras_require={}
48 | )
49 |
--------------------------------------------------------------------------------
/uninstall.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | uninstall_p4utils() {
4 | pip uninstall p4utils
5 | }
6 |
7 | remove_mx() {
8 |
9 | BINDIR=/usr/bin
10 | MANDIR=/usr/share/man/man1
11 |
12 | rm -f ${BINDIR}/"mxexec"
13 | rm -f ${BINDIR}/"mx"
14 | rm -rf ${MANDIR}/"mxexec.1"
15 |
16 | }
17 |
18 | uninstall_p4utils
19 | remove_mx
--------------------------------------------------------------------------------
/utils/mx:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Attach to a MiniNExT host and run a command
4 | # (Extends existing script to provide support for PID namespaces)
5 | # Credit to MiniNext: https://github.com/USC-NSL/miniNExT
6 |
7 | if [ -z $1 ]; then
8 | echo "usage: $0 host cmd [args...]"
9 | exit 1
10 | else
11 | host=$1
12 | fi
13 |
14 | pid=`ps ax | grep "mininet:$host$" | grep bash | grep -v mxexec | awk '{print $1};'`
15 |
16 | if echo $pid | grep -q ' '; then
17 | echo "Error: found multiple mininet:$host processes"
18 | exit 2
19 | fi
20 |
21 | if [ "$pid" == "" ]; then
22 | echo "Could not find Mininet host $host"
23 | exit 3
24 | fi
25 |
26 | if [ -z $2 ]; then
27 | cmd="bash -c 'cd `pwd`; bash'"
28 | else
29 | shift
30 | cmd=$*
31 | cmd="bash -c 'cd `pwd`; $cmd'"
32 | fi
33 |
34 | cgroup=/sys/fs/cgroup/cpu/$host
35 | if [ -d "$cgroup" ]; then
36 | cg="-g $host"
37 | fi
38 |
39 | # Check whether host should be running in a chroot dir
40 | rootdir="/var/run/mn/$host/root"
41 | if [ -d $rootdir -a -x $rootdir/bin/bash ]; then
42 | cmd="'cd `pwd`; exec $cmd'"
43 | cmd="chroot $rootdir /bin/bash -c $cmd"
44 | fi
45 |
46 | cmd="exec sudo mxexec -a $pid -b $pid -k $pid $cg $cmd"
47 | eval $cmd
--------------------------------------------------------------------------------
/vm/README.md:
--------------------------------------------------------------------------------
1 | # P4-Utils Virtual Machine
2 |
3 | > **Important**
4 | > In this section we explain how to build a VM with all the needed tools from scratch.
5 | > However, since some part of the installation depends on Packer, and this tool sometimes gives problems
6 | > we recommend to simply use the install script provided [here](../install-tools) and run it in a clean Ubuntu machine or VM.
7 |
8 | Although P4-Utils can be installed directly on your system, running P4-Utils in a completely separated
9 | environment can be beneficial: in this way, installation and execution errors, that may arise, will not
10 | affect the whole system. For this reason, **we recommend using one of the VM methods that we provide**.
11 | In addition, since P4-Utils is only available on Linux, other OS users can run it in a Linux virtual machine.
12 |
13 | We provide two different solutions for the P4-Utils VM and both are supported by a wide range of
14 | operating systems:
15 |
16 | - [VirtualBox](https://www.virtualbox.org/)
17 | - [QEMU](https://www.qemu.org/)
18 |
19 | You can choose to download and use one of our
20 | [preconfigured VMs](https://nsg-ethz.github.io/p4-utils/installation.html#use-our-preconfigured-vm)
21 | or to [build it yourself](#build-your-own-vm).
22 |
23 | > **Important**
24 | > Whether you are building your own VM or you are using the preconfigured images, you still
25 | > need to install one of the above virtualizer according to your VM choice.
26 |
27 | ## Build your own VM
28 |
29 | To get started, you need to install the required software:
30 |
31 | - [VirtualBox](https://www.virtualbox.org/) or [QEMU](https://www.qemu.org/)
32 | - [Packer](https://www.packer.io/)
33 |
34 | > **Note**
35 | > Packer is a handy framework designed to automatically build custom VM images.
36 |
37 | Clone the P4-Utils repository:
38 |
39 | ```
40 | git clone https://github.com/nsg-ethz/p4-utils
41 | ```
42 |
43 | Go to the Packer configurations folder:
44 |
45 | ```
46 | cd p4-utils/vm
47 | ```
48 |
49 | If you want to build the *VirtualBox VM*, execute:
50 |
51 | ```
52 | ./build-virtualbox.sh [--cpus 4] [--disk_size 25000] [--memory 4000] [--vm_name p4] [--username p4] [--password p4]
53 | ```
54 |
55 | On the other hand, if you prefer the *QEMU VM*, run:
56 |
57 | ```
58 | ./build-qemu.sh [--cpus 4] [--disk_size 25000] [--memory 4000] [--vm_name p4] [--username p4] [--password p4]
59 | ```
60 |
61 | According to the commands shown above, some parameters can be passed to the building scripts
62 | to customize the VM:
63 |
64 | - ``--cpus`` specifies the **number of cores** to use,
65 | - ``--disk_size`` is the **size of the disk** reserved by the VM in MBytes,
66 | - ``--memory`` is the amount of **RAM** to assign to the VM in MBytes,
67 | - ``--vm_name`` is the **name of the VM**,
68 | - ``--username`` is the **login username**,
69 | - ``--password`` is the **login password**.
70 |
71 | > **Attention**
72 | > The default VMs configuration parameters are shown above between square brakets. If you do not
73 | > specify anything, they will be used to build your VM. However, please pass to the scripts the
74 | > parameters that best fit your needs.
75 |
76 | The building process will generate the following files:
77 |
78 | - If you chose the QEMU VM, in `p4-utils/vm/output-ubuntu18044_qemu` you will find
79 | a `.qcow2` file to use to set up your VM.
80 | - If you chose the VirtualBox VM, in `p4-utils/vm/output-ubuntu18044_vb` you will
81 | find an `.ova` file to import in the VirtualBox VM manager.
--------------------------------------------------------------------------------
/vm/build-qemu.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | SCRIPT_DIR=$(dirname "$(realpath $0)")
3 |
4 | OPTS=""
5 | while [[ $# -gt 0 ]]; do
6 | OPTS="$OPTS -var ${1:2}=$2 "
7 | shift # past argument
8 | shift # past value
9 | done
10 |
11 | packer init "$SCRIPT_DIR/qemu.pkr.hcl"
12 | packer validate "$SCRIPT_DIR/qemu.pkr.hcl"
13 | packer build $OPTS "$SCRIPT_DIR/qemu.pkr.hcl"
14 |
--------------------------------------------------------------------------------
/vm/build-qemu20.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | SCRIPT_DIR=$(dirname "$(realpath $0)")
3 |
4 | OPTS=""
5 | while [[ $# -gt 0 ]]; do
6 | OPTS="$OPTS -var ${1:2}=$2 "
7 | shift # past argument
8 | shift # past value
9 | done
10 |
11 | packer init "$SCRIPT_DIR/qemu20.pkr.hcl"
12 | packer validate "$SCRIPT_DIR/qemu20.pkr.hcl"
13 | packer build -debug $OPTS "$SCRIPT_DIR/qemu20.pkr.hcl"
14 |
--------------------------------------------------------------------------------
/vm/build-virtualbox.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | SCRIPT_DIR=$(dirname "$(realpath $0)")
3 |
4 | OPTS=""
5 | while [[ $# -gt 0 ]]; do
6 | OPTS="$OPTS -var ${1:2}=$2 "
7 | shift # past argument
8 | shift # past value
9 | done
10 |
11 | packer init "$SCRIPT_DIR/virtualbox.pkr.hcl"
12 | packer validate "$SCRIPT_DIR/virtualbox.pkr.hcl"
13 | packer build $OPTS "$SCRIPT_DIR/virtualbox.pkr.hcl"
14 |
--------------------------------------------------------------------------------
/vm/http/preseed.cfg:
--------------------------------------------------------------------------------
1 | # Preseeding only locale sets language, country and locale.
2 | d-i debian-installer/locale string en_US
3 |
4 | # Keyboard selection.
5 | d-i console-setup/ask_detect boolean false
6 | d-i keyboard-configuration/xkb-keymap select us
7 |
8 | choose-mirror-bin mirror/http/proxy string
9 |
10 | ### Clock and time zone setup
11 | d-i clock-setup/utc boolean true
12 | d-i time/zone string UTC
13 |
14 | # Avoid that last message about the install being complete.
15 | d-i finish-install/reboot_in_progress note
16 |
17 | # This is fairly safe to set, it makes grub install automatically to the MBR
18 | # if no other operating system is detected on the machine.
19 | d-i grub-installer/only_debian boolean true
20 |
21 | # This one makes grub-installer install to the MBR if it also finds some other
22 | # OS, which is less safe as it might not be able to boot that other OS.
23 | d-i grub-installer/with_other_os boolean true
24 |
25 | ### Mirror settings
26 | # If you select ftp, the mirror/country string does not need to be set.
27 | d-i mirror/country string manual
28 | d-i mirror/http/directory string /ubuntu/
29 | d-i mirror/http/hostname string archive.ubuntu.com
30 | d-i mirror/http/proxy string
31 |
32 | ### Partitioning
33 | d-i partman-auto/method string lvm
34 |
35 | # This makes partman automatically partition without confirmation.
36 | d-i partman-lvm/confirm boolean true
37 | d-i partman-lvm/confirm_nooverwrite boolean true
38 | d-i partman-lvm/device_remove_lvm boolean true
39 | d-i partman/choose_partition select finish
40 | d-i partman/confirm boolean true
41 | d-i partman/confirm_nooverwrite boolean true
42 | d-i partman/confirm_write_new_label boolean true
43 |
44 | ### Account setup
45 | d-i passwd/user-fullname string p4
46 | d-i passwd/user-uid string 1000
47 | d-i passwd/user-password password p4
48 | d-i passwd/user-password-again password p4
49 | d-i passwd/username string p4
50 |
51 | # The installer will warn about weak passwords. If you are sure you know
52 | # what you're doing and want to override it, uncomment this.
53 | d-i user-setup/allow-password-weak boolean true
54 | d-i user-setup/encrypt-home boolean false
55 |
56 | ### Package selection
57 | tasksel tasksel/first standard
58 | d-i pkgsel/include string openssh-server build-essential
59 | d-i pkgsel/install-language-support boolean false
60 |
61 | # disable automatic package updates
62 | d-i pkgsel/update-policy select none
63 | d-i pkgsel/upgrade select full-upgrade
64 |
65 | # make sure ssh is started
66 | d-i preseed/late_command string in-target systemctl enable ssh
--------------------------------------------------------------------------------
/vm/qemu.pkr.hcl:
--------------------------------------------------------------------------------
1 | variable "memory" {
2 | default = 4096
3 | }
4 |
5 | variable "cpus" {
6 | default = 4
7 | }
8 |
9 | variable "disk_size" {
10 | default = 25000
11 | }
12 |
13 | variable "vm_name" {
14 | default = "p4"
15 | }
16 |
17 | variable "username" {
18 | default = "p4"
19 | }
20 |
21 | variable "password" {
22 | default = "p4"
23 | }
24 |
25 | variable "iso_url" {
26 | default = "http://old-releases.ubuntu.com/releases/18.04.4/ubuntu-18.04.4-server-amd64.iso"
27 | }
28 |
29 | variable "iso_checksum" {
30 | default = "sha256:e2ecdace33c939527cbc9e8d23576381c493b071107207d2040af72595f8990b"
31 | }
32 |
33 | variable "target" {
34 | default = "sources.qemu.ubuntu18044_qemu"
35 | }
36 |
37 | packer {
38 | required_plugins {
39 | qemu = {
40 | version = ">= 0.0.1"
41 | source = "github.com/hashicorp/qemu"
42 | }
43 | }
44 | }
45 |
46 | source "qemu" "ubuntu18044_qemu" {
47 | vm_name = "${var.vm_name}.qcow2"
48 | headless = true
49 | iso_url = var.iso_url
50 | iso_checksum = var.iso_checksum
51 | http_directory = "http"
52 | cpus = var.cpus
53 | memory = var.memory
54 | disk_size = "${var.disk_size}"
55 | accelerator = "kvm"
56 | ssh_username = var.username
57 | ssh_password = var.password
58 | ssh_timeout = "1h"
59 | shutdown_command = "echo ${var.password} | sudo -S shutdown -P now"
60 | format = "qcow2"
61 | boot_wait = "10s"
62 | boot_command = [
63 | "",
64 | "",
65 | "",
66 | "/install/vmlinuz",
67 | " initrd=/install/initrd.gz",
68 | " auto-install/enable=true",
69 | " debconf/priority=critical",
70 | " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
71 | " hostname=${var.vm_name}",
72 | " -- ",
73 | ""
74 | ]
75 | }
76 |
77 | build {
78 | sources = [
79 | "sources.qemu.ubuntu18044_qemu"
80 | ]
81 | provisioner "shell" {
82 | inline = [
83 | "echo ${var.password} | sudo -S bash -c \"echo '${var.username} ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/99_vm\"",
84 | "echo ${var.password} | sudo -S sudo chmod 440 /etc/sudoers.d/99_vm",
85 | "sudo bash -c 'cat << EOF > /etc/netplan/01-netcfg.yaml",
86 | "network:",
87 | " version: 2",
88 | " renderer: networkd",
89 | " ethernets:",
90 | " id0:",
91 | " match:",
92 | " name: e*",
93 | " dhcp4: yes",
94 | "EOF'",
95 | "sudo apt-get install -y git curl",
96 | "curl -sSL https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev.sh | bash"
97 | ]
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/vm/qemu20.pkr.hcl:
--------------------------------------------------------------------------------
1 | variable "memory" {
2 | default = 4096
3 | }
4 |
5 | variable "cpus" {
6 | default = 4
7 | }
8 |
9 | variable "disk_size" {
10 | default = 25000
11 | }
12 |
13 | variable "vm_name" {
14 | default = "p4"
15 | }
16 |
17 | variable "username" {
18 | default = "p4"
19 | }
20 |
21 | variable "password" {
22 | default = "p4"
23 | }
24 |
25 | variable "iso_url" {
26 | default = "https://releases.ubuntu.com/20.04.6/ubuntu-20.04.6-live-server-amd64.iso"
27 | }
28 |
29 | variable "iso_checksum" {
30 | default = "sha256:b8f31413336b9393ad5d8ef0282717b2ab19f007df2e9ed5196c13d8f9153c8b"
31 | }
32 |
33 | variable "target" {
34 | default = "sources.qemu.ubuntu20046_qemu"
35 | }
36 |
37 | packer {
38 | required_plugins {
39 | qemu = {
40 | version = ">= 0.0.1"
41 | source = "github.com/hashicorp/qemu"
42 | }
43 | }
44 | }
45 |
46 | source "qemu" "ubuntu20046_qemu" {
47 | vm_name = "${var.vm_name}.qcow2"
48 | headless = true
49 | iso_url = var.iso_url
50 | iso_checksum = var.iso_checksum
51 | http_directory = "http"
52 | cpus = var.cpus
53 | memory = var.memory
54 | disk_size = "${var.disk_size}"
55 | accelerator = "kvm"
56 | ssh_username = var.username
57 | ssh_password = var.password
58 | ssh_timeout = "1h"
59 | shutdown_command = "echo ${var.password} | sudo -S shutdown -P now"
60 | format = "qcow2"
61 | boot_wait = "20s"
62 | boot_command = [
63 | "",
64 | "",
65 | "",
66 | "/install/vmlinuz",
67 | " initrd=/install/initrd.gz",
68 | " auto-install/enable=true",
69 | " debconf/priority=critical",
70 | " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
71 | " hostname=${var.vm_name}",
72 | " -- ",
73 | ""
74 | ]
75 | }
76 |
77 | build {
78 | sources = [
79 | "sources.qemu.ubuntu20046_qemu"
80 | ]
81 | provisioner "shell" {
82 | inline = [
83 | "echo ${var.password} | sudo -S bash -c \"echo '${var.username} ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/99_vm\"",
84 | "echo ${var.password} | sudo -S sudo chmod 440 /etc/sudoers.d/99_vm",
85 | "sudo bash -c 'cat << EOF > /etc/netplan/01-netcfg.yaml",
86 | "network:",
87 | " version: 2",
88 | " renderer: networkd",
89 | " ethernets:",
90 | " id0:",
91 | " match:",
92 | " name: e*",
93 | " dhcp4: yes",
94 | "EOF'",
95 | "sudo apt-get install -y git curl",
96 | "curl -sSL https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev-ubuntu20.sh | bash"
97 | ]
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/vm/virtualbox.pkr.hcl:
--------------------------------------------------------------------------------
1 | variable "memory" {
2 | default = 4096
3 | }
4 |
5 | variable "cpus" {
6 | default = 4
7 | }
8 |
9 | variable "disk_size" {
10 | default = 25000
11 | }
12 |
13 | variable "vm_name" {
14 | default = "p4"
15 | }
16 |
17 | variable "username" {
18 | default = "p4"
19 | }
20 |
21 | variable "password" {
22 | default = "p4"
23 | }
24 |
25 | variable "iso_url" {
26 | default = "http://old-releases.ubuntu.com/releases/18.04.4/ubuntu-18.04.4-server-amd64.iso"
27 | }
28 |
29 | variable "iso_checksum" {
30 | default = "sha256:e2ecdace33c939527cbc9e8d23576381c493b071107207d2040af72595f8990b"
31 | }
32 |
33 | variable "target" {
34 | default = "sources.qemu.ubuntu18044_qemu"
35 | }
36 |
37 | packer {
38 | required_plugins {
39 | virtualbox = {
40 | version = ">= 0.0.1"
41 | source = "github.com/hashicorp/virtualbox"
42 | }
43 | qemu = {
44 | version = ">= 0.0.1"
45 | source = "github.com/hashicorp/qemu"
46 | }
47 | }
48 | }
49 |
50 | source "virtualbox-iso" "ubuntu18044_vb" {
51 | vm_name = var.vm_name
52 | headless = true
53 | guest_os_type = "Ubuntu_64"
54 | iso_url = var.iso_url
55 | iso_checksum = var.iso_checksum
56 | http_directory = "http"
57 | cpus = var.cpus
58 | memory = var.memory
59 | disk_size = var.disk_size
60 | ssh_username = var.username
61 | ssh_password = var.password
62 | ssh_timeout = "1h"
63 | shutdown_command = "echo ${var.password} | sudo -S shutdown -P now"
64 | format = "ova"
65 | boot_wait = "10s"
66 | boot_command = [
67 | "",
68 | "",
69 | "",
70 | "/install/vmlinuz",
71 | " initrd=/install/initrd.gz",
72 | " auto-install/enable=true",
73 | " debconf/priority=critical",
74 | " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
75 | " hostname={{ .Name }}",
76 | " -- ",
77 | ""
78 | ]
79 | }
80 |
81 | build {
82 | sources = [
83 | "sources.virtualbox-iso.ubuntu18044_vb"
84 | ]
85 | provisioner "shell" {
86 | inline = [
87 | "echo ${var.password} | sudo -S bash -c \"echo '${var.username} ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/99_advnet\"",
88 | "echo ${var.password} | sudo -S sudo chmod 440 /etc/sudoers.d/99_advnet",
89 | "sudo apt-get install -y git curl",
90 | "curl -sSL https://raw.githubusercontent.com/nsg-ethz/p4-utils/master/install-tools/install-p4-dev.sh | bash"
91 | ]
92 | }
93 | }
--------------------------------------------------------------------------------