summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/automake.mk12
-rw-r--r--Documentation/faq/index.rst1
-rw-r--r--Documentation/faq/ovn.rst90
-rw-r--r--Documentation/howto/docker.rst326
-rw-r--r--Documentation/howto/firewalld.rst107
-rw-r--r--Documentation/howto/index.rst9
-rw-r--r--Documentation/howto/openstack-containers.rst135
-rw-r--r--Documentation/index.rst20
-rw-r--r--Documentation/intro/install/fedora.rst12
-rw-r--r--Documentation/intro/install/index.rst8
-rw-r--r--Documentation/intro/install/ovn-upgrades.rst115
-rw-r--r--Documentation/ref/ovs-sim.1.rst100
-rw-r--r--Documentation/topics/high-availability.rst440
-rw-r--r--Documentation/topics/index.rst18
-rw-r--r--Documentation/topics/ovn-news-2.8.rst278
-rw-r--r--Documentation/topics/role-based-access-control.rst101
-rw-r--r--Documentation/tutorials/index.rst7
-rw-r--r--Documentation/tutorials/ovn-ipsec.rst146
-rw-r--r--Documentation/tutorials/ovn-openstack.rst1922
-rw-r--r--Documentation/tutorials/ovn-rbac.rst134
-rw-r--r--Documentation/tutorials/ovn-sandbox.rst177
-rw-r--r--NEWS5
-rw-r--r--configure.ac2
-rw-r--r--debian/.gitignore5
-rw-r--r--debian/automake.mk22
-rw-r--r--debian/control78
-rw-r--r--debian/ovn-central.dirs1
-rwxr-xr-xdebian/ovn-central.init60
-rw-r--r--debian/ovn-central.install3
-rw-r--r--debian/ovn-central.manpages1
-rwxr-xr-xdebian/ovn-central.postinst49
-rwxr-xr-xdebian/ovn-central.postrm48
-rw-r--r--debian/ovn-central.template5
-rw-r--r--debian/ovn-common.install7
-rw-r--r--debian/ovn-common.manpages8
-rw-r--r--debian/ovn-common.postinst24
-rw-r--r--debian/ovn-common.postrm23
-rwxr-xr-xdebian/ovn-controller-vtep.init54
-rw-r--r--debian/ovn-controller-vtep.install1
-rw-r--r--debian/ovn-controller-vtep.manpages1
-rw-r--r--debian/ovn-docker.install2
-rw-r--r--debian/ovn-host.dirs1
-rwxr-xr-xdebian/ovn-host.init54
-rw-r--r--debian/ovn-host.install1
-rw-r--r--debian/ovn-host.manpages1
-rwxr-xr-xdebian/ovn-host.postinst49
-rwxr-xr-xdebian/ovn-host.postrm44
-rw-r--r--debian/ovn-host.template5
-rwxr-xr-xdebian/rules6
-rw-r--r--include/automake.mk1
-rw-r--r--include/ovn/actions.h622
-rw-r--r--include/ovn/automake.mk6
-rw-r--r--include/ovn/expr.h518
-rw-r--r--include/ovn/lex.h152
-rw-r--r--include/ovn/logical-fields.h130
-rw-r--r--lib/db-ctl-base.xml12
-rw-r--r--manpages.mk10
-rw-r--r--ovn/TODO.rst147
-rw-r--r--ovn/automake.mk10
-rw-r--r--ovn/controller-vtep/.gitignore2
-rw-r--r--ovn/controller-vtep/automake.mk14
-rw-r--r--ovn/controller-vtep/binding.c274
-rw-r--r--ovn/controller-vtep/binding.h27
-rw-r--r--ovn/controller-vtep/gateway.c230
-rw-r--r--ovn/controller-vtep/gateway.h26
-rw-r--r--ovn/controller-vtep/ovn-controller-vtep.8.xml80
-rw-r--r--ovn/controller-vtep/ovn-controller-vtep.c272
-rw-r--r--ovn/controller-vtep/ovn-controller-vtep.h51
-rw-r--r--ovn/controller-vtep/vtep.c600
-rw-r--r--ovn/controller-vtep/vtep.h27
-rw-r--r--ovn/controller/.gitignore2
-rw-r--r--ovn/controller/automake.mk32
-rw-r--r--ovn/controller/bfd.c268
-rw-r--r--ovn/controller/bfd.h41
-rw-r--r--ovn/controller/binding.c764
-rw-r--r--ovn/controller/binding.h57
-rw-r--r--ovn/controller/chassis.c671
-rw-r--r--ovn/controller/chassis.h46
-rw-r--r--ovn/controller/encaps.c409
-rw-r--r--ovn/controller/encaps.h48
-rw-r--r--ovn/controller/ha-chassis.c203
-rw-r--r--ovn/controller/ha-chassis.h50
-rw-r--r--ovn/controller/ip-mcast.c164
-rw-r--r--ovn/controller/ip-mcast.h52
-rw-r--r--ovn/controller/lflow.c898
-rw-r--r--ovn/controller/lflow.h184
-rw-r--r--ovn/controller/lport.c102
-rw-r--r--ovn/controller/lport.h52
-rw-r--r--ovn/controller/ofctrl.c1393
-rw-r--r--ovn/controller/ofctrl.h87
-rw-r--r--ovn/controller/ovn-controller.8.xml456
-rw-r--r--ovn/controller/ovn-controller.c2366
-rw-r--r--ovn/controller/ovn-controller.h85
-rw-r--r--ovn/controller/patch.c273
-rw-r--r--ovn/controller/patch.h42
-rw-r--r--ovn/controller/physical.c1459
-rw-r--r--ovn/controller/physical.h74
-rw-r--r--ovn/controller/pinctrl.c4342
-rw-r--r--ovn/controller/pinctrl.h51
-rw-r--r--ovn/lib/actions.c2902
-rw-r--r--ovn/lib/automake.mk15
-rw-r--r--ovn/lib/chassis-index.c67
-rw-r--r--ovn/lib/chassis-index.h30
-rw-r--r--ovn/lib/expr.c3450
-rw-r--r--ovn/lib/extend-table.c208
-rw-r--r--ovn/lib/extend-table.h82
-rw-r--r--ovn/lib/inc-proc-eng.c201
-rw-r--r--ovn/lib/inc-proc-eng.h234
-rw-r--r--ovn/lib/ip-mcast-index.c40
-rw-r--r--ovn/lib/ip-mcast-index.h36
-rw-r--r--ovn/lib/lex.c1023
-rw-r--r--ovn/lib/logical-fields.c261
-rw-r--r--ovn/lib/mcast-group-index.c43
-rw-r--r--ovn/lib/mcast-group-index.h32
-rw-r--r--ovn/lib/ovn-l7.h322
-rw-r--r--ovn/northd/.gitignore2
-rw-r--r--ovn/northd/automake.mk10
-rw-r--r--ovn/northd/ovn-northd.8.xml2544
-rw-r--r--ovn/northd/ovn-northd.c9446
-rw-r--r--ovn/ovn-architecture.7.xml2074
-rw-r--r--ovn/utilities/automake.mk42
-rw-r--r--ovn/utilities/bugtool/automake.mk9
-rw-r--r--ovn/utilities/bugtool/ovn-bugtool-nbctl-show19
-rw-r--r--ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list19
-rw-r--r--ovn/utilities/bugtool/ovn-bugtool-sbctl-show19
-rw-r--r--ovn/utilities/bugtool/plugins/network-status/ovn.xml23
-rwxr-xr-xovn/utilities/ovn-ctl822
-rw-r--r--ovn/utilities/ovn-ctl.8.xml215
-rw-r--r--ovn/utilities/ovn-detrace.1.in38
-rwxr-xr-xovn/utilities/ovn-detrace.in215
-rwxr-xr-xovn/utilities/ovn-docker-overlay-driver.in442
-rwxr-xr-xovn/utilities/ovn-docker-underlay-driver.in677
-rw-r--r--ovn/utilities/ovn-trace.8.xml485
-rw-r--r--ovn/utilities/ovn-trace.c2373
-rwxr-xr-xovn/utilities/ovndb-servers.ocf642
-rw-r--r--ovsdb/ovsdb-tool.1.in23
-rw-r--r--rhel/automake.mk16
-rw-r--r--rhel/ovn-fedora.spec.in432
-rw-r--r--rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml7
-rw-r--r--rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml6
-rw-r--r--rhel/usr_lib_systemd_system_ovn-controller-vtep.service50
-rw-r--r--rhel/usr_lib_systemd_system_ovn-controller.service34
-rw-r--r--rhel/usr_lib_systemd_system_ovn-northd.service35
-rw-r--r--tests/atlocal.in4
-rw-r--r--tests/automake.mk21
-rw-r--r--tests/ofproto-macros.at2
-rw-r--r--tests/oss-fuzz/automake.mk10
-rw-r--r--tests/oss-fuzz/config/expr.dict120
-rw-r--r--tests/oss-fuzz/config/expr_parse_target.options3
-rw-r--r--tests/oss-fuzz/expr_parse_target.c464
-rw-r--r--tests/ovn-controller-vtep.at467
-rw-r--r--tests/ovn-controller.at294
-rw-r--r--tests/ovn-macros.at180
-rw-r--r--tests/ovn-nbctl.at1660
-rw-r--r--tests/ovn-northd.at900
-rw-r--r--tests/ovn-performance.at424
-rw-r--r--tests/ovn-sbctl.at150
-rw-r--r--tests/ovn.at14702
-rw-r--r--tests/system-kmod-testsuite.at2
-rw-r--r--tests/system-ovn.at1667
-rw-r--r--tests/system-userspace-testsuite.at2
-rw-r--r--tests/test-ovn.c1584
-rw-r--r--tests/testsuite.at8
-rw-r--r--tutorial/automake.mk3
-rwxr-xr-xtutorial/ovn-setup.sh37
-rwxr-xr-xtutorial/ovs-sandbox261
-rw-r--r--utilities/bugtool/automake.mk9
-rwxr-xr-xutilities/ovs-sim.in237
-rw-r--r--xenserver/openvswitch-xen.spec.in7
169 files changed, 47 insertions, 75436 deletions
diff --git a/Documentation/automake.mk b/Documentation/automake.mk
index 2a3214a3c..cd68f3b15 100644
--- a/Documentation/automake.mk
+++ b/Documentation/automake.mk
@@ -18,7 +18,6 @@ DOC_SOURCE = \
Documentation/intro/install/fedora.rst \
Documentation/intro/install/general.rst \
Documentation/intro/install/netbsd.rst \
- Documentation/intro/install/ovn-upgrades.rst \
Documentation/intro/install/rhel.rst \
Documentation/intro/install/userspace.rst \
Documentation/intro/install/windows.rst \
@@ -26,12 +25,8 @@ DOC_SOURCE = \
Documentation/tutorials/index.rst \
Documentation/tutorials/faucet.rst \
Documentation/tutorials/ovs-advanced.rst \
- Documentation/tutorials/ovn-openstack.rst \
- Documentation/tutorials/ovn-sandbox.rst \
Documentation/tutorials/ovs-conntrack.rst \
Documentation/tutorials/ipsec.rst \
- Documentation/tutorials/ovn-ipsec.rst \
- Documentation/tutorials/ovn-rbac.rst \
Documentation/topics/index.rst \
Documentation/topics/bonding.rst \
Documentation/topics/idl-compound-indexes.rst \
@@ -54,28 +49,22 @@ DOC_SOURCE = \
Documentation/topics/fuzzing/ovs-fuzzers.rst \
Documentation/topics/fuzzing/security-analysis-of-ovs-fuzzers.rst \
Documentation/topics/testing.rst \
- Documentation/topics/high-availability.rst \
Documentation/topics/integration.rst \
Documentation/topics/language-bindings.rst \
Documentation/topics/networking-namespaces.rst \
Documentation/topics/openflow.rst \
- Documentation/topics/ovn-news-2.8.rst \
Documentation/topics/ovsdb-replication.rst \
Documentation/topics/porting.rst \
- Documentation/topics/role-based-access-control.rst \
Documentation/topics/tracing.rst \
Documentation/topics/windows.rst \
Documentation/howto/index.rst \
- Documentation/howto/docker.rst \
Documentation/howto/dpdk.rst \
- Documentation/howto/firewalld.rst \
Documentation/howto/ipsec.rst \
Documentation/howto/kvm.rst \
Documentation/howto/libvirt.rst \
Documentation/howto/selinux.rst \
Documentation/howto/ssl.rst \
Documentation/howto/lisp.rst \
- Documentation/howto/openstack-containers.rst \
Documentation/howto/qos.png \
Documentation/howto/qos.rst \
Documentation/howto/sflow.png \
@@ -94,7 +83,6 @@ DOC_SOURCE = \
Documentation/faq/general.rst \
Documentation/faq/issues.rst \
Documentation/faq/openflow.rst \
- Documentation/faq/ovn.rst \
Documentation/faq/qos.rst \
Documentation/faq/releases.rst \
Documentation/faq/terminology.rst \
diff --git a/Documentation/faq/index.rst b/Documentation/faq/index.rst
index ad3cc2b6f..334b828b2 100644
--- a/Documentation/faq/index.rst
+++ b/Documentation/faq/index.rst
@@ -41,4 +41,3 @@ Open vSwitch FAQ
terminology
vlan
vxlan
- ovn
diff --git a/Documentation/faq/ovn.rst b/Documentation/faq/ovn.rst
deleted file mode 100644
index 4d96b4aa5..000000000
--- a/Documentation/faq/ovn.rst
+++ /dev/null
@@ -1,90 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-===
-OVN
-===
-
-Q: Why does OVN use STT and Geneve instead of VLANs or VXLAN (or GRE)?
-
- A: OVN implements a fairly sophisticated packet processing pipeline in
- "logical datapaths" that can implement switching or routing functionality.
- A logical datapath has an ingress pipeline and an egress pipeline, and each
- of these pipelines can include logic based on packet fields as well as
- packet metadata such as the logical ingress and egress ports (the latter
- only in the egress pipeline).
-
- The processing for a logical datapath can be split across hypervisors. In
- particular, when a logical ingress pipeline executes an "output" action,
- OVN passes the packet to the egress pipeline on the hypervisor (or, in the
- case of output to a logical multicast group, hypervisors) on which the
- logical egress port is located. If this hypervisor is not the same as the
- ingress hypervisor, then the packet has to be transmitted across a physical
- network.
-
- This situation is where tunneling comes in. To send the packet to another
- hypervisor, OVN encapsulates it with a tunnel protocol and sends the
- encapsulated packet across the physical network. When the remote
- hypervisor receives the tunnel packet, it decapsulates it and passes it
- through the logical egress pipeline. To do so, it also needs the metadata,
- that is, the logical ingress and egress ports.
-
- Thus, to implement OVN logical packet processing, at least the following
- metadata must pass across the physical network:
-
- * Logical datapath ID, a 24-bit identifier. In Geneve, OVN uses the VNI to
- hold the logical datapath ID; in STT, OVN uses 24 bits of STT's 64-bit
- context ID.
-
- * Logical ingress port, a 15-bit identifier. In Geneve, OVN uses an option
- to hold the logical ingress port; in STT, 15 bits of the context ID.
-
- * Logical egress port, a 16-bit identifier. In Geneve, OVN uses an option
- to hold the logical egress port; in STT, 16 bits of the context ID.
-
- See ``ovn-architecture(7)``, under "Tunnel Encapsulations", for details.
-
- Together, these metadata require 24 + 15 + 16 = 55 bits. GRE provides 32
- bits, VXLAN provides 24, and VLAN only provides 12. Most notably, if
- logical egress pipelines do not match on the logical ingress port, thereby
- restricting the class of ACLs available to users, then this eliminates 15
- bits, bringing the requirement down to 40 bits. At this point, one can
- choose to limit the size of the OVN logical network in various ways, e.g.:
-
- * 16 bits of logical datapaths + 16 bits of logical egress ports. This
- combination fits within a 32-bit GRE tunnel key.
-
- * 12 bits of logical datapaths + 12 bits of logical egress ports. This
- combination fits within a 24-bit VXLAN VNI.
-
- * It's difficult to identify an acceptable compromise for a VLAN-based
- deployment.
-
- These compromises wouldn't suit every site, since some deployments
- may need to allocate more bits to the datapath or egress port
- identifiers.
-
- As a side note, OVN does support VXLAN for use with ASIC-based top of rack
- switches, using ``ovn-controller-vtep(8)`` and the OVSDB VTEP schema
- described in ``vtep(5)``, but this limits the features available from OVN
- to the subset available from the VTEP schema.
diff --git a/Documentation/howto/docker.rst b/Documentation/howto/docker.rst
deleted file mode 100644
index a68b02fdb..000000000
--- a/Documentation/howto/docker.rst
+++ /dev/null
@@ -1,326 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-===================================
-Open Virtual Networking With Docker
-===================================
-
-This document describes how to use Open Virtual Networking with Docker 1.9.0
-or later.
-
-.. important::
-
- Requires Docker version 1.9.0 or later. Only Docker 1.9.0+ comes with support
- for multi-host networking. Consult www.docker.com for instructions on how to
- install Docker.
-
-.. note::
-
- You must build and install Open vSwitch before proceeding with the below
- guide. Refer to :doc:`/intro/install/index` for more information.
-
-Setup
------
-
-For multi-host networking with OVN and Docker, Docker has to be started with a
-distributed key-value store. For example, if you decide to use consul as your
-distributed key-value store and your host IP address is ``$HOST_IP``, start
-your Docker daemon with::
-
- $ docker daemon --cluster-store=consul://127.0.0.1:8500 \
- --cluster-advertise=$HOST_IP:0
-
-OVN provides network virtualization to containers. OVN's integration with
-Docker currently works in two modes - the "underlay" mode or the "overlay"
-mode.
-
-In the "underlay" mode, OVN requires a OpenStack setup to provide container
-networking. In this mode, one can create logical networks and can have
-containers running inside VMs, standalone VMs (without having any containers
-running inside them) and physical machines connected to the same logical
-network. This is a multi-tenant, multi-host solution.
-
-In the "overlay" mode, OVN can create a logical network amongst containers
-running on multiple hosts. This is a single-tenant (extendable to multi-tenants
-depending on the security characteristics of the workloads), multi-host
-solution. In this mode, you do not need a pre-created OpenStack setup.
-
-For both the modes to work, a user has to install and start Open vSwitch in
-each VM/host that they plan to run their containers on.
-
-.. _docker-overlay:
-
-The "overlay" mode
-------------------
-
-.. note::
-
- OVN in "overlay" mode needs a minimum Open vSwitch version of 2.5.
-
-1. Start the central components.
-
- OVN architecture has a central component which stores your networking intent
- in a database. On one of your machines, with an IP Address of
- ``$CENTRAL_IP``, where you have installed and started Open vSwitch, you will
- need to start some central components.
-
- Start ovn-northd daemon. This daemon translates networking intent from Docker
- stored in the OVN\_Northbound database to logical flows in ``OVN_Southbound``
- database. For example::
-
- $ /usr/share/openvswitch/scripts/ovn-ctl start_northd
-
- With Open vSwitch version of 2.7 or greater, you need to run the following
- additional commands (Please read the manpages of ovn-nb for more control
- on the types of connection allowed.) ::
-
- $ ovn-nbctl set-connection ptcp:6641
- $ ovn-sbctl set-connection ptcp:6642
-
-2. One time setup
-
- On each host, where you plan to spawn your containers, you will need to run
- the below command once. You may need to run it again if your OVS database
- gets cleared. It is harmless to run it again in any case::
-
- $ ovs-vsctl set Open_vSwitch . \
- external_ids:ovn-remote="tcp:$CENTRAL_IP:6642" \
- external_ids:ovn-nb="tcp:$CENTRAL_IP:6641" \
- external_ids:ovn-encap-ip=$LOCAL_IP \
- external_ids:ovn-encap-type="$ENCAP_TYPE"
-
- where:
-
- ``$LOCAL_IP``
- is the IP address via which other hosts can reach this host. This acts as
- your local tunnel endpoint.
-
- ``$ENCAP_TYPE``
- is the type of tunnel that you would like to use for overlay networking.
- The options are ``geneve`` or ``stt``. Your kernel must have support for
- your chosen ``$ENCAP_TYPE``. Both ``geneve`` and ``stt`` are part of the
- Open vSwitch kernel module that is compiled from this repo. If you use the
- Open vSwitch kernel module from upstream Linux, you will need a minimum
- kernel version of 3.18 for ``geneve``. There is no ``stt`` support in
- upstream Linux. You can verify whether you have the support in your kernel
- as follows::
-
- $ lsmod | grep $ENCAP_TYPE
-
- In addition, each Open vSwitch instance in an OVN deployment needs a unique,
- persistent identifier, called the ``system-id``. If you install OVS from
- distribution packaging for Open vSwitch (e.g. .deb or .rpm packages), or if
- you use the ovs-ctl utility included with Open vSwitch, it automatically
- configures a system-id. If you start Open vSwitch manually, you should set
- one up yourself. For example::
-
- $ id_file=/etc/openvswitch/system-id.conf
- $ test -e $id_file || uuidgen > $id_file
- $ ovs-vsctl set Open_vSwitch . external_ids:system-id=$(cat $id_file)
-
-3. Start the ``ovn-controller``.
-
- You need to run the below command on every boot::
-
- $ /usr/share/openvswitch/scripts/ovn-ctl start_controller
-
-4. Start the Open vSwitch network driver.
-
- By default Docker uses Linux bridge for networking. But it has support for
- external drivers. To use Open vSwitch instead of the Linux bridge, you will
- need to start the Open vSwitch driver.
-
- The Open vSwitch driver uses the Python's flask module to listen to Docker's
- networking api calls. So, if your host does not have Python's flask module,
- install it::
-
- $ sudo pip install Flask
-
- Start the Open vSwitch driver on every host where you plan to create your
- containers. Refer to the note on ``$OVS_PYTHON_LIBS_PATH`` that is used below
- at the end of this document::
-
- $ PYTHONPATH=$OVS_PYTHON_LIBS_PATH ovn-docker-overlay-driver --detach
-
- .. note::
-
- The ``$OVS_PYTHON_LIBS_PATH`` variable should point to the directory where
- Open vSwitch Python modules are installed. If you installed Open vSwitch
- Python modules via the Debian package of ``python-openvswitch`` or via pip
- by running ``pip install ovs``, you do not need to specify the PATH. If
- you installed it by following the instructions in
- :doc:`/intro/install/general`, then you should specify the PATH. In this
- case, the PATH depends on the options passed to ``./configure``. It is
- usually either ``/usr/share/openvswitch/python`` or
- ``/usr/local/share/openvswitch/python``
-
-Docker has inbuilt primitives that closely match OVN's logical switches and
-logical port concepts. Consult Docker's documentation for all the possible
-commands. Here are some examples.
-
-Create a logical switch
-~~~~~~~~~~~~~~~~~~~~~~~
-
-To create a logical switch with name 'foo', on subnet '192.168.1.0/24', run::
-
- $ NID=`docker network create -d openvswitch --subnet=192.168.1.0/24 foo`
-
-List all logical switches
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- $ docker network ls
-
-You can also look at this logical switch in OVN's northbound database by
-running the following command::
-
- $ ovn-nbctl --db=tcp:$CENTRAL_IP:6640 ls-list
-
-Delete a logical switch
-~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- $ docker network rm bar
-
-
-Create a logical port
-~~~~~~~~~~~~~~~~~~~~~
-
-Docker creates your logical port and attaches it to the logical network in a
-single step. For example, to attach a logical port to network ``foo`` inside
-container busybox, run::
-
- $ docker run -itd --net=foo --name=busybox busybox
-
-List all logical ports
-~~~~~~~~~~~~~~~~~~~~~~
-
-Docker does not currently have a CLI command to list all logical ports but you
-can look at them in the OVN database by running::
-
- $ ovn-nbctl --db=tcp:$CENTRAL_IP:6640 lsp-list $NID
-
-Create and attach a logical port to a running container
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- $ docker network create -d openvswitch --subnet=192.168.2.0/24 bar
- $ docker network connect bar busybox
-
-Detach and delete a logical port from a running container
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can delete your logical port and detach it from a running container
-by running:
-
-::
-
- $ docker network disconnect bar busybox
-
-.. _docker-underlay:
-
-The "underlay" mode
--------------------
-
-.. note::
-
- This mode requires that you have a OpenStack setup pre-installed with
- OVN providing the underlay networking.
-
-1. One time setup
-
- A OpenStack tenant creates a VM with a single network interface (or multiple)
- that belongs to management logical networks. The tenant needs to fetch the
- port-id associated with the interface via which he plans to send the container
- traffic inside the spawned VM. This can be obtained by running the below
- command to fetch the 'id' associated with the VM::
-
- $ nova list
-
- and then by running::
-
- $ neutron port-list --device_id=$id
-
- Inside the VM, download the OpenStack RC file that contains the tenant
- information (henceforth referred to as ``openrc.sh``). Edit the file and add the
- previously obtained port-id information to the file by appending the following
- line::
-
- $ export OS_VIF_ID=$port_id
-
- After this edit, the file will look something like::
-
- #!/bin/bash
- export OS_AUTH_URL=http://10.33.75.122:5000/v2.0
- export OS_TENANT_ID=fab106b215d943c3bad519492278443d
- export OS_TENANT_NAME="demo"
- export OS_USERNAME="demo"
- export OS_VIF_ID=e798c371-85f4-4f2d-ad65-d09dd1d3c1c9
-
-2. Create the Open vSwitch bridge
-
- If your VM has one ethernet interface (e.g.: 'eth0'), you will need to add
- that device as a port to an Open vSwitch bridge 'breth0' and move its IP
- address and route related information to that bridge. (If it has multiple
- network interfaces, you will need to create and attach an Open vSwitch
- bridge for the interface via which you plan to send your container
- traffic.)
-
- If you use DHCP to obtain an IP address, then you should kill the DHCP
- client that was listening on the physical Ethernet interface (e.g. eth0) and
- start one listening on the Open vSwitch bridge (e.g. breth0).
-
- Depending on your VM, you can make the above step persistent across reboots.
- For example, if your VM is Debian/Ubuntu-based, read
- `openvswitch-switch.README.Debian` found in `debian` folder. If your VM is
- RHEL-based, refer to :doc:`/intro/install/rhel`.
-
-3. Start the Open vSwitch network driver
-
- The Open vSwitch driver uses the Python's flask module to listen to Docker's
- networking api calls. The driver also uses OpenStack's
- ``python-neutronclient`` libraries. If your host does not have Python's
- ``flask`` module or ``python-neutronclient`` you must install them. For
- example::
-
- $ pip install python-neutronclient
- $ pip install Flask
-
- Once installed, source the ``openrc`` file::
-
- $ . ./openrc.sh
-
- Start the network driver and provide your OpenStack tenant password when
- prompted::
-
- $ PYTHONPATH=$OVS_PYTHON_LIBS_PATH ovn-docker-underlay-driver \
- --bridge breth0 --detach
-
-From here-on you can use the same Docker commands as described in
-`docker-overlay`_.
-
-Refer to the ovs-architecture man pages (``man ovn-architecture``) to
-understand OVN's architecture in detail.
diff --git a/Documentation/howto/firewalld.rst b/Documentation/howto/firewalld.rst
deleted file mode 100644
index 0dc455ea8..000000000
--- a/Documentation/howto/firewalld.rst
+++ /dev/null
@@ -1,107 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-===================================
-Open Virtual Network With firewalld
-===================================
-
-firewalld is a service that allows for easy administration of firewalls. OVN
-ships with a set of service files that can be used with firewalld to allow
-for remote connections to the northbound and southbound databases.
-
-This guide will describe how you can use these files with your existing
-firewalld setup. Setup and administration of firewalld is outside the scope
-of this document.
-
-Installation
-------------
-
-If you have installed OVN from an RPM, then the service files for firewalld
-will automatically be installed in ``/usr/lib/firewalld/services``.
-Installation from RPM includes installation from the yum or dnf package
-managers.
-
-If you have installed OVN from source, then from the top level source
-directory, issue the following commands to copy the firewalld service files:
-
-::
-
- $ cp rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
- /etc/firewalld/services/
- $ cp rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
- /etc/firewalld/services/
-
-
-Activation
-----------
-
-Assuming you are already running firewalld, you can issue the following
-commands to enable the OVN services.
-
-On the central server (the one running ``ovn-northd``), issue the following::
-
-$ firewall-cmd --zone=public --add-service=ovn-central-firewall-service
-
-This will open TCP ports 6641 and 6642, allowing for remote connections to the
-northbound and southbound databases.
-
-On the OVN hosts (the ones running ``ovn-controller``), issue the following::
-
-$ firewall-cmd --zone=public --add-service=ovn-host-firewall-service
-
-This will open UDP port 6081, allowing for geneve traffic to flow between the
-controllers.
-
-Variations
-----------
-
-When installing the XML service files, you have the choice of copying them to
-``/etc/firewalld/services`` or ``/usr/lib/firewalld/services``. The former is
-recommend since the latter can be overwritten if firewalld is upgraded.
-
-The above commands assumed your underlay network interfaces are in the
-"public" firewalld zone. If your underlay network interfaces are in a separate
-zone, then adjust the above commands accordingly.
-
-The ``--permanent`` option may be passed to the above firewall-cmd invocations
-in order for the services to be permanently added to the firewalld
-configuration. This way it is not necessary to re-issue the commands each
-time the firewalld service restarts.
-
-The ovn-host-firewall-service only opens port 6081. This is because the
-default protocol for OVN tunnels is geneve. If you are using a different
-encapsulation protocol, you will need to modify the XML service file to open
-the appropriate port(s). For VXLAN, open port 4789. For STT, open port 7471.
-
-Recommendations
----------------
-
-The firewalld service files included with the OVS repo are meant as a
-convenience for firewalld users. All that the service files do is to open
-the common ports used by OVN. No additional security is provided. To ensure a
-more secure environment, it is a good idea to do the following
-
-* Use tools such as iptables or nftables to restrict access to known hosts.
-* Use SSL for all remote connections to OVN databases.
-* Use role-based access control for connections to the OVN southbound
- database.
diff --git a/Documentation/howto/index.rst b/Documentation/howto/index.rst
index 9a3487be3..60fb8a717 100644
--- a/Documentation/howto/index.rst
+++ b/Documentation/howto/index.rst
@@ -50,12 +50,3 @@ OVS
sflow
dpdk
-OVN
----
-
-.. toctree::
- :maxdepth: 1
-
- docker
- openstack-containers
- firewalld
diff --git a/Documentation/howto/openstack-containers.rst b/Documentation/howto/openstack-containers.rst
deleted file mode 100644
index 692fe25e5..000000000
--- a/Documentation/howto/openstack-containers.rst
+++ /dev/null
@@ -1,135 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-================================================
-Integration of Containers with OVN and OpenStack
-================================================
-
-Isolation between containers is weaker than isolation between VMs, so some
-environments deploy containers for different tenants in separate VMs as an
-additional security measure. This document describes creation of containers
-inside VMs and how they can be made part of the logical networks securely. The
-created logical network can include VMs, containers and physical machines as
-endpoints. To better understand the proposed integration of containers with
-OVN and OpenStack, this document describes the end to end workflow with an
-example.
-
-* A OpenStack tenant creates a VM (say VM-A) with a single network interface
- that belongs to a management logical network. The VM is meant to host
- containers. OpenStack Nova chooses the hypervisor on which VM-A is created.
-
-* A Neutron port may have been created in advance and passed in to Nova with
- the request to create a new VM. If not, Nova will issue a request to Neutron
- to create a new port. The ID of the logical port from Neutron will also be
- used as the vif-id for the virtual network interface (VIF) of VM-A.
-
-* When VM-A is created on a hypervisor, its VIF gets added to the Open vSwitch
- integration bridge. This creates a row in the Interface table of the
- ``Open_vSwitch`` database. As explained in the :doc:`integration guide
- </topics/integration>`, the vif-id associated with the VM network interface
- gets added in the ``external_ids:iface-id`` column of the newly created row
- in the Interface table.
-
-* Since VM-A belongs to a logical network, it gets an IP address. This IP
- address is used to spawn containers (either manually or through container
- orchestration systems) inside that VM and to monitor the health of the
- created containers.
-
-* The vif-id associated with the VM's network interface can be obtained by
- making a call to Neutron using tenant credentials.
-
-* This flow assumes a component called a "container network plugin". If you
- take Docker as an example for containers, you could envision the plugin to be
- either a wrapper around Docker or a feature of Docker itself that understands
- how to perform part of this workflow to get a container connected to a
- logical network managed by Neutron. The rest of the flow refers to this
- logical component that does not yet exist as the "container network plugin".
-
-* All the calls to Neutron will need tenant credentials. These calls can
- either be made from inside the tenant VM as part of a container network
- plugin or from outside the tenant VM (if the tenant is not comfortable using
- temporary Keystone tokens from inside the tenant VMs). For simplicity, this
- document explains the work flow using the former method.
-
-* The container hosting VM will need Open vSwitch installed in it. The only
- work for Open vSwitch inside the VM is to tag network traffic coming from
- containers.
-
-* When a container needs to be created inside the VM with a container network
- interface that is expected to be attached to a particular logical switch, the
- network plugin in that VM chooses any unused VLAN (This VLAN tag only needs
- to be unique inside that VM. This limits the number of container interfaces
- to 4096 inside a single VM). This VLAN tag is stripped out in the hypervisor
- by OVN and is only useful as a context (or metadata) for OVN.
-
-* The container network plugin then makes a call to Neutron to create a logical
- port. In addition to all the inputs that a call to create a port in Neutron
- that are currently needed, it sends the vif-id and the VLAN tag as inputs.
-
-* Neutron in turn will verify that the vif-id belongs to the tenant in question
- and then uses the OVN specific plugin to create a new row in the
- Logical_Switch_Port table of the OVN Northbound Database. Neutron responds
- back with an IP address and MAC address for that network interface. So
- Neutron becomes the IPAM system and provides unique IP and MAC addresses
- across VMs and containers in the same logical network.
-
-* The Neutron API call above to create a logical port for the container could
- add a relatively significant amount of time to container creation. However,
- an optimization is possible here. Logical ports could be created in advance
- and reused by the container system doing container orchestration. Additional
- Neutron API calls would only be needed if the port needs to be attached to a
- different logical network.
-
-* When a container is eventually deleted, the network plugin in that VM may
- make a call to Neutron to delete that port. Neutron in turn will delete the
- entry in the ``Logical_Switch_Port`` table of the OVN Northbound Database.
-
-As an example, consider Docker containers. Since Docker currently does not
-have a network plugin feature, this example uses a hypothetical wrapper around
-Docker to make calls to Neutron.
-
-* Create a Logical switch::
-
- $ ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f create network LS1
-
- The above command will make a call to Neutron with the credentials to create
- a logical switch. The above is optional if the logical switch has already
- been created from outside the VM.
-
-* List networks available to the tenant::
-
- $ ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f list networks
-
-* Create a container and attach a interface to the previously created switch as
- a logical port::
-
- $ ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f --vif-id=$VIF_ID \
- --network=LS1 run -d --net=none ubuntu:14.04 /bin/sh -c \
- "while true; do echo hello world; sleep 1; done"
-
- The above command will make a call to Neutron with all the inputs it
- currently needs to create a logical port. In addition, it passes the $VIF_ID
- and a unused VLAN. Neutron will add that information in OVN and return back
- a MAC address and IP address for that interface. ovn-docker will then create
- a veth pair, insert one end inside the container as 'eth0' and the other end
- as a port of a local OVS bridge as an access port of the chosen VLAN.
diff --git a/Documentation/index.rst b/Documentation/index.rst
index bace34dbf..f18f8df1c 100644
--- a/Documentation/index.rst
+++ b/Documentation/index.rst
@@ -33,22 +33,20 @@ How the Documentation is Organised
The Open vSwitch documentation is organised into multiple sections:
- :doc:`Installation guides </intro/install/index>` guide you through
- installing Open vSwitch (OVS) and Open Virtual Network (OVN) on a variety of
- different platforms
+ installing Open vSwitch (OVS) on a variety of different platforms
- :doc:`Tutorials </tutorials/index>` take you through a series of steps to
- configure OVS and OVN in sandboxed environments
-- :doc:`Topic guides </topics/index>` provide a high level overview of OVS and
- OVN internals and operation
-- :doc:`How-to guides </howto/index>` are recipes or use-cases for OVS and OVN.
+ configure OVS in sandboxed environments
+- :doc:`Topic guides </topics/index>` provide a high level overview of OVS
+ internals and operation
+- :doc:`How-to guides </howto/index>` are recipes or use-cases for OVS.
They are more advanced than the tutorials.
- :doc:`Frequently Asked Questions </faq/index>` provide general insight into
- a variety of topics related to configuration and operation of OVS and OVN.
+ a variety of topics related to configuration and operation of OVS.
First Steps
-----------
-Getting started with Open vSwitch (OVS) or Open Virtual Network (OVN) for Open
-vSwitch? Start here.
+Getting started with Open vSwitch (OVS)? Start here.
- **Overview:** :doc:`intro/what-is-ovs` |
:doc:`intro/why-ovs`
@@ -64,12 +62,8 @@ vSwitch? Start here.
- **Tutorials:** :doc:`tutorials/faucet` |
:doc:`tutorials/ovs-advanced` |
- :doc:`tutorials/ovn-sandbox` |
- :doc:`tutorials/ovn-openstack` |
:doc:`tutorials/ovs-conntrack` |
:doc:`tutorials/ipsec` |
- :doc:`tutorials/ovn-ipsec` |
- :doc:`tutorials/ovn-rbac`
Deeper Dive
-----------
diff --git a/Documentation/intro/install/fedora.rst b/Documentation/intro/install/fedora.rst
index 4e1a99766..f11d05a01 100644
--- a/Documentation/intro/install/fedora.rst
+++ b/Documentation/intro/install/fedora.rst
@@ -119,16 +119,6 @@ tests. This can take several minutes.
$ make rpm-fedora RPMBUILD_OPT="--with check"
-To build OVN RPMs, execute the following from the directory in which
-`./configure` was executed:
-
-::
-
- $ make rpm-fedora-ovn
-
-This will create the RPMs `ovn`, `ovn-common`, `ovn-central`, `ovn-host`,
-`ovn-docker` and `ovn-vtep`.
-
Kernel OVS Tree Datapath RPM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -165,8 +155,6 @@ In most cases only the `openvswitch` RPM will need to be installed. The
`openvswitch-debuginfo` RPMs are optional unless required for a specific
purpose.
-The `ovn-*` packages are only needed when using OVN.
-
Refer to the `RHEL README`__ for additional usage and configuration
information.
diff --git a/Documentation/intro/install/index.rst b/Documentation/intro/install/index.rst
index c27a9c9d1..586ced95f 100644
--- a/Documentation/intro/install/index.rst
+++ b/Documentation/intro/install/index.rst
@@ -62,14 +62,6 @@ provided below.
fedora
rhel
-Upgrades
---------
-
-.. toctree::
- :maxdepth: 2
-
- ovn-upgrades
-
Others
------
diff --git a/Documentation/intro/install/ovn-upgrades.rst b/Documentation/intro/install/ovn-upgrades.rst
deleted file mode 100644
index 3e6cd984e..000000000
--- a/Documentation/intro/install/ovn-upgrades.rst
+++ /dev/null
@@ -1,115 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-============
-OVN Upgrades
-============
-
-Since OVN is a distributed system, special consideration must be given to
-the process used to upgrade OVN across a deployment. This document discusses
-the recommended upgrade process.
-
-Release Notes
--------------
-
-You should always check the OVS and OVN release notes (NEWS file) for any
-release specific notes on upgrades.
-
-OVS
----
-
-OVN depends on and is included with OVS. It's expected that OVS and OVN are
-upgraded together, partly for convenience. OVN is included in OVS releases
-so it's easiest to upgrade them together. OVN may also make use of new
-features of OVS only available in that release.
-
-Upgrade ovn-controller
-----------------------
-
-You should start by upgrading ovn-controller on each host it's running on.
-First, you upgrade the OVS and OVN packages. Then, restart the
-ovn-controller service. You can restart with ovn-ctl::
-
- $ sudo /usr/share/openvswitch/scripts/ovn-ctl restart_controller
-
-or with systemd::
-
- $ sudo systemd restart ovn-controller
-
-Upgrade OVN Databases and ovn-northd
-------------------------------------
-
-The OVN databases and ovn-northd should be upgraded next. Since ovn-controller
-has already been upgraded, it will be ready to operate on any new functionality
-specified by the database or logical flows created by ovn-northd.
-
-Upgrading the OVN packages installs everything needed for an upgrade. The only
-step required after upgrading the packages is to restart ovn-northd, which
-automatically restarts the databases and upgrades the database schema, as well.
-
-You may perform this restart using the ovn-ctl script::
-
- $ sudo /usr/share/openvswitch/scripts/ovn-ctl restart_northd
-
-or if you're using a Linux distribution with systemd::
-
- $ sudo systemctl restart ovn-northd
-
-Schema Change
-^^^^^^^^^^^^^
-
-During database upgrading, if there is schema change, the DB file will be
-converted to the new schema automatically, if the schema change is backward
-compatible. OVN tries the best to keep the DB schemas backward compatible.
-
-However, there can be situations that an incompatible change is reasonble. An
-example of such case is to add constraints in the table to ensure correctness.
-If there were already data that violates the new constraints got added somehow,
-it will result in DB upgrade failures. In this case, user should manually
-correct data using ovn-nbctl (for north-bound DB) or ovn-sbctl (for south-
-bound DB), and then upgrade again following previous steps. Below is a list
-of known impactible schema changes and how to fix when error encountered.
-
-#. Release 2.11: index [type, ip] added for Encap table of south-bound DB to
- prevent duplicated IPs being used for same tunnel type. If there are
- duplicated data added already (e.g. due to improper chassis management),
- a convenient way to fix is to find the chassis that is using the IP
- with command::
-
- $ ovn-sbctl show
-
- Then delete the chassis with command::
-
- $ ovn-sbctl chassis-del <chassis>
-
-
-Upgrading OVN Integration
--------------------------
-
-Lastly, you may also want to upgrade integration with OVN that you may be
-using. For example, this could be the OpenStack Neutron driver or
-ovn-kubernetes.
-
-OVN's northbound database schema is a backwards compatible interface, so
-you should be able to safely complete an OVN upgrade before upgrading
-any integration in use.
diff --git a/Documentation/ref/ovs-sim.1.rst b/Documentation/ref/ovs-sim.1.rst
index 4382598e1..f59cd7af7 100644
--- a/Documentation/ref/ovs-sim.1.rst
+++ b/Documentation/ref/ovs-sim.1.rst
@@ -142,103 +142,3 @@ with ``main`` directly.
must already have been created by a previous invocation of
``net_add``. The default sandbox must not be ``main``.
-OVN Commands
-------------
-
-These commands interact with OVN, the Open Virtual Network.
-
-``ovn_start`` [*options*]
- Creates and initializes the central OVN databases (both
- ``ovn-sb(5)`` and ``ovn-nb(5)``) and starts an instance of
- ``ovsdb-server`` for each one. Also starts an instance of
- ``ovn-northd``.
-
- The following options are available:
-
- ``--nbdb-model`` *model*
- Uses the given database model for the northbound database.
- The *model* may be ``standalone`` (the default), ``backup``,
- or ``clustered``.
-
- ``--nbdb-servers`` *n*
- For a clustered northbound database, the number of servers in
- the cluster. The default is 3.
-
- ``--sbdb-model`` *model*
- Uses the given database model for the southbound database.
- The *model* may be ``standalone`` (the default), ``backup``,
- or ``clustered``.
-
- ``--sbdb-servers`` *n*
- For a clustered southbound database, the number of servers in
- the cluster. The default is 3.
-
-``ovn_attach`` *network* *bridge* *ip* [*masklen*]
- First, this command attaches bridge to interconnection network
- network, just like ``net_attach`` *network* *bridge*. Second, it
- configures (simulated) IP address *ip* (with network mask length
- *masklen*, which defaults to 24) on *bridge*. Finally, it
- configures the Open vSwitch database to work with OVN and starts
- ``ovn-controller``.
-
-Examples
-========
-
-The following creates a pair of Open vSwitch instances ``hv0`` and
-``hv1``, adds a port named ``vif0`` or ``vif1``, respectively, to each
-one, and then connects the two through an interconnection network
-``n1``::
-
- net_add n1
- for i in 0 1; do
- sim_add hv$i
- as hv$i ovs-vsctl add-br br0 -- add-port br0 vif$i
- as hv$i net_attach n1 br0
- done
-
-Here’s an extended version that also starts OVN::
-
- ovn_start
- ovn-nbctl ls-add lsw0
- net_add n1
- for i in 0 1; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.`expr $i + 1`
- ovs-vsctl add-port br-int vif$i -- set Interface vif$i external-ids:iface-id=lp$i
- ovn-nbctl lsp-add lsw0 lp$i
- ovn-nbctl lsp-set-addresses lp$i f0:00:00:00:00:0$i
- done
-
-Here’s a primitive OVN "scale test" (adjust the scale by changing
-``n`` in the first line::
-
- n=200; export n
- ovn_start --sbdb-model=clustered
- net_add n1
- ovn-nbctl ls-add br0
- for i in `seq $n`; do
- (sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- y=$(expr $i / 256)
- x=$(expr $i % 256)
- ovn_attach n1 br-phys 192.168.$y.$x
- ovs-vsctl add-port br-int vif$i -- set Interface vif$i external-ids:iface-id=lp$i) &
- case $i in
- *50|*00) echo $i; wait ;;
- esac
- done
- wait
- for i in `seq $n`; do
- yy=$(printf %02x $(expr $i / 256))
- xx=$(printf $02x $(expr $i % 256))
- ovn-nbctl lsp-add br0 lp$i
- ovn-nbctl lsp-set-addresses lp$i f0:00:00:00:$yy:$xx
- done
-
-When the scale test has finished initializing, you can watch the
-logical ports come up with a command like this::
-
- watch 'for i in `seq $n`; do if test `ovn-nbctl lsp-get-up lp$i` != up; then echo $i; fi; done'
diff --git a/Documentation/topics/high-availability.rst b/Documentation/topics/high-availability.rst
deleted file mode 100644
index a5cb76383..000000000
--- a/Documentation/topics/high-availability.rst
+++ /dev/null
@@ -1,440 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-==================================
-OVN Gateway High Availability Plan
-==================================
-
-::
-
- OVN Gateway
-
- +---------------------------+
- | |
- | External Network |
- | |
- +-------------^-------------+
- |
- |
- +-----------+
- | |
- | Gateway |
- | |
- +-----------+
- ^
- |
- |
- +-------------v-------------+
- | |
- | OVN Virtual Network |
- | |
- +---------------------------+
-
-The OVN gateway is responsible for shuffling traffic between the tunneled
-overlay network (governed by ovn-northd), and the legacy physical network. In
-a naive implementation, the gateway is a single x86 server, or hardware VTEP.
-For most deployments, a single system has enough forwarding capacity to service
-the entire virtualized network, however, it introduces a single point of
-failure. If this system dies, the entire OVN deployment becomes unavailable.
-To mitigate this risk, an HA solution is critical -- by spreading
-responsibility across multiple systems, no single server failure can take down
-the network.
-
-An HA solution is both critical to the manageability of the system, and
-extremely difficult to get right. The purpose of this document, is to propose
-a plan for OVN Gateway High Availability which takes into account our past
-experience building similar systems. It should be considered a fluid changing
-proposal, not a set-in-stone decree.
-
-.. note::
- This document describes a range of options OVN could take to provide
- high availability for gateways. The current implementation provides L3
- gateway high availability by the "Router Specific Active/Backup"
- approach described in this document.
-
-Basic Architecture
-------------------
-
-In an OVN deployment, the set of hypervisors and network elements operating
-under the guidance of ovn-northd are in what's called "logical space". These
-servers use VXLAN, STT, or Geneve to communicate, oblivious to the details of
-the underlying physical network. When these systems need to communicate with
-legacy networks, traffic must be routed through a Gateway which translates from
-OVN controlled tunnel traffic, to raw physical network traffic.
-
-Since the gateway is typically the only system with a connection to the
-physical network all traffic between logical space and the WAN must travel
-through it. This makes it a critical single point of failure -- if the gateway
-dies, communication with the WAN ceases for all systems in logical space.
-
-To mitigate this risk, multiple gateways should be run in a "High Availability
-Cluster" or "HA Cluster". The HA cluster will be responsible for performing
-the duties of a gateways, while being able to recover gracefully from
-individual member failures.
-
-::
-
- OVN Gateway HA Cluster
-
- +---------------------------+
- | |
- | External Network |
- | |
- +-------------^-------------+
- |
- |
- +----------------------v----------------------+
- | |
- | High Availability Cluster |
- | |
- | +-----------+ +-----------+ +-----------+ |
- | | | | | | | |
- | | Gateway | | Gateway | | Gateway | |
- | | | | | | | |
- | +-----------+ +-----------+ +-----------+ |
- +----------------------^----------------------+
- |
- |
- +-------------v-------------+
- | |
- | OVN Virtual Network |
- | |
- +---------------------------+
-
-L2 vs L3 High Availability
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In order to achieve this goal, there are two broad approaches one can take.
-The HA cluster can appear to the network like a giant Layer 2 Ethernet Switch,
-or like a giant IP Router. These approaches are called L2HA, and L3HA
-respectively. L2HA allows ethernet broadcast domains to extend into logical
-space, a significant advantage, but this comes at a cost. The need to avoid
-transient L2 loops during failover significantly complicates their design. On
-the other hand, L3HA works for most use cases, is simpler, and fails more
-gracefully. For these reasons, it is suggested that OVN supports an L3HA
-model, leaving L2HA for future work (or third party VTEP providers). Both
-models are discussed further below.
-
-L3HA
-----
-
-In this section, we'll work through a basic simple L3HA implementation, on top
-of which we'll gradually build more sophisticated features explaining their
-motivations and implementations as we go.
-
-Naive active-backup
-~~~~~~~~~~~~~~~~~~~
-
-Let's assume that there are a collection of logical routers which a tenant has
-asked for, our task is to schedule these logical routers on one of N gateways,
-and gracefully redistribute the routers on gateways which have failed. The
-absolute simplest way to achieve this is what we'll call "naive-active-backup".
-
-::
-
- Naive Active Backup HA Implementation
-
- +----------------+ +----------------+
- | Leader | | Backup |
- | | | |
- | A B C | | |
- | | | |
- +----+-+-+-+----++ +-+--------------+
- ^ ^ ^ ^ | |
- | | | | | |
- | | | | +-+------+---+
- + + + + | ovn-northd |
- Traffic +------------+
-
-In a naive active-backup, one of the Gateways is chosen (arbitrarily) as a
-leader. All logical routers (A, B, C in the figure), are scheduled on this
-leader gateway and all traffic flows through it. ovn-northd monitors this
-gateway via OpenFlow echo requests (or some equivalent), and if the gateway
-dies, it recreates the routers on one of the backups.
-
-This approach basically works in most cases and should likely be the starting
-point for OVN -- it's strictly better than no HA solution and is a good
-foundation for more sophisticated solutions. That said, it's not without it's
-limitations. Specifically, this approach doesn't coordinate with the physical
-network to minimize disruption during failures, and it tightly couples failover
-to ovn-northd (we'll discuss why this is bad in a bit), and wastes resources by
-leaving backup gateways completely unutilized.
-
-Router Failover
-+++++++++++++++
-
-When ovn-northd notices the leader has died and decides to migrate routers to a
-backup gateway, the physical network has to be notified to direct traffic to
-the new gateway. Otherwise, traffic could be blackholed for longer than
-necessary making failovers worse than they need to be.
-
-For now, let's assume that OVN requires all gateways to be on the same IP
-subnet on the physical network. If this isn't the case, gateways would need to
-participate in routing protocols to orchestrate failovers, something which is
-difficult and out of scope of this document.
-
-Since all gateways are on the same IP subnet, we simply need to worry about
-updating the MAC learning tables of the Ethernet switches on that subnet.
-Presumably, they all have entries for each logical router pointing to the old
-leader. If these entries aren't updated, all traffic will be sent to the (now
-defunct) old leader, instead of the new one.
-
-In order to mitigate this issue, it's recommended that the new gateway sends a
-Reverse ARP (RARP) onto the physical network for each logical router it now
-controls. A Reverse ARP is a benign protocol used by many hypervisors when
-virtual machines migrate to update L2 forwarding tables. In this case, the
-ethernet source address of the RARP is that of the logical router it
-corresponds to, and its destination is the broadcast address. This causes the
-RARP to travel to every L2 switch in the broadcast domain, updating forwarding
-tables accordingly. This strategy is recommended in all failover mechanisms
-discussed in this document -- when a router newly boots on a new leader, it
-should RARP its MAC address.
-
-Controller Independent Active-backup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- Controller Independent Active-Backup Implementation
-
- +----------------+ +----------------+
- | Leader | | Backup |
- | | | |
- | A B C | | |
- | | | |
- +----------------+ +----------------+
- ^ ^ ^ ^
- | | | |
- | | | |
- + + + +
- Traffic
-
-The fundamental problem with naive active-backup, is it tightly couples the
-failover solution to ovn-northd. This can significantly increase downtime in
-the event of a failover as the (often already busy) ovn-northd controller has
-to recompute state for the new leader. Worse, if ovn-northd goes down, we can't
-perform gateway failover at all. This violates the principle that control
-plane outages should have no impact on dataplane functionality.
-
-In a controller independent active-backup configuration, ovn-northd is
-responsible for initial configuration while the HA cluster is responsible for
-monitoring the leader, and failing over to a backup if necessary. ovn-northd
-sets HA policy, but doesn't actively participate when failovers occur.
-
-Of course, in this model, ovn-northd is not without some responsibility. Its
-role is to pre-plan what should happen in the event of a failure, leaving it to
-the individual switches to execute this plan. It does this by assigning each
-gateway a unique leadership priority. Once assigned, it communicates this
-priority to each node it controls. Nodes use the leadership priority to
-determine which gateway in the cluster is the active leader by using a simple
-metric: the leader is the gateway that is healthy, with the highest priority.
-If that gateway goes down, leadership falls to the next highest priority, and
-conversely, if a new gateway comes up with a higher priority, it takes over
-leadership.
-
-Thus, in this model, leadership of the HA cluster is determined simply by the
-status of its members. Therefore if we can communicate the status of each
-gateway to each transport node, they can individually figure out which is the
-leader, and direct traffic accordingly.
-
-Tunnel Monitoring
-+++++++++++++++++
-
-Since in this model leadership is determined exclusively by the health status
-of member gateways, a key problem is how do we communicate this information to
-the relevant transport nodes. Luckily, we can do this fairly cheaply using
-tunnel monitoring protocols like BFD.
-
-The basic idea is pretty straightforward. Each transport node maintains a
-tunnel to every gateway in the HA cluster (not just the leader). These tunnels
-are monitored using the BFD protocol to see which are alive. Given this
-information, hypervisors can trivially compute the highest priority live
-gateway, and thus the leader.
-
-In practice, this leadership computation can be performed trivially using the
-bundle or group action. Rather than using OpenFlow to simply output to the
-leader, all gateways could be listed in an active-backup bundle action ordered
-by their priority. The bundle action will automatically take into account the
-tunnel monitoring status to output the packet to the highest priority live
-gateway.
-
-Inter-Gateway Monitoring
-++++++++++++++++++++++++
-
-One somewhat subtle aspect of this model, is that failovers are not globally
-atomic. When a failover occurs, it will take some time for all hypervisors to
-notice and adjust accordingly. Similarly, if a new high priority Gateway comes
-up, it may take some time for all hypervisors to switch over to the new leader.
-In order to avoid confusing the physical network, under these circumstances
-it's important for the backup gateways to drop traffic they've received
-erroneously. In order to do this, each Gateway must know whether or not it is,
-in fact active. This can be achieved by creating a mesh of tunnels between
-gateways. Each gateway monitors the other gateways its cluster to determine
-which are alive, and therefore whether or not that gateway happens to be the
-leader. If leading, the gateway forwards traffic normally, otherwise it drops
-all traffic.
-
-We should note that this method works well under the assumption that there
-are no inter-gateway connectivity failures, in such case this method would fail
-to elect a single master. The simplest example is two gateways which stop seeing
-each other but can still reach the hypervisors. Protocols like VRRP or CARP
-have the same issue. A mitigation for this type of failure mode could be
-achieved by having all network elements (hypervisors and gateways) periodically
-share their link status to other endpoints.
-
-Gateway Leadership Resignation
-++++++++++++++++++++++++++++++
-
-Sometimes a gateway may be healthy, but still may not be suitable to lead the
-HA cluster. This could happen for several reasons including:
-
-* The physical network is unreachable
-
-* BFD (or ping) has detected the next hop router is unreachable
-
-* The Gateway recently booted and isn't fully configured
-
-In this case, the Gateway should resign leadership by holding its tunnels down
-using the ``other_config:cpath_down`` flag. This indicates to participating
-hypervisors and Gateways that this gateway should be treated as if it's down,
-even though its tunnels are still healthy.
-
-Router Specific Active-Backup
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- Router Specific Active-Backup
-
- +----------------+ +----------------+
- | | | |
- | A C | | B D E |
- | | | |
- +----------------+ +----------------+
- ^ ^ ^ ^
- | | | |
- | | | |
- + + + +
- Traffic
-
-Controller independent active-backup is a great advance over naive
-active-backup, but it still has one glaring problem -- it under-utilizes the
-backup gateways. In ideal scenario, all traffic would split evenly among the
-live set of gateways. Getting all the way there is somewhat tricky, but as a
-step in the direction, one could use the "Router Specific Active-Backup"
-algorithm. This algorithm looks a lot like active-backup on a per logical
-router basis, with one twist. It chooses a different active Gateway for each
-logical router. Thus, in situations where there are several logical routers,
-all with somewhat balanced load, this algorithm performs better.
-
-Implementation of this strategy is quite straightforward if built on top of
-basic controller independent active-backup. On a per logical router basis, the
-algorithm is the same, leadership is determined by the liveness of the
-gateways. The key difference here is that the gateways must have a different
-leadership priority for each logical router. These leadership priorities can
-be computed by ovn-northd just as they had been in the controller independent
-active-backup model.
-
-Once we have these per logical router priorities, they simply need be
-communicated to the members of the gateway cluster and the hypervisors. The
-hypervisors in particular, need simply have an active-backup bundle action (or
-group action) per logical router listing the gateways in priority order for
-*that router*, rather than having a single bundle action shared for all the
-routers.
-
-Additionally, the gateways need to be updated to take into account individual
-router priorities. Specifically, each gateway should drop traffic of backup
-routers it's running, and forward traffic of active gateways, instead of simply
-dropping or forwarding everything. This should likely be done by having
-ovn-controller recompute OpenFlow for the gateway, though other options exist.
-
-The final complication is that ovn-northd's logic must be updated to choose
-these per logical router leadership priorities in a more sophisticated manner.
-It doesn't matter much exactly what algorithm it chooses to do this, beyond
-that it should provide good balancing in the common case. I.E. each logical
-routers priorities should be different enough that routers balance to different
-gateways even when failures occur.
-
-Preemption
-++++++++++
-
-In an active-backup setup, one issue that users will run into is that of
-gateway leader preemption. If a new Gateway is added to a cluster, or for some
-reason an existing gateway is rebooted, we could end up in a situation where
-the newly activated gateway has higher priority than any other in the HA
-cluster. In this case, as soon as that gateway appears, it will preempt
-leadership from the currently active leader causing an unnecessary failover.
-Since failover can be quite expensive, this preemption may be undesirable.
-
-The controller can optionally avoid preemption by cleverly tweaking the
-leadership priorities. For each router, new gateways should be assigned
-priorities that put them second in line or later when they eventually come up.
-Furthermore, if a gateway goes down for a significant period of time, its old
-leadership priorities should be revoked and new ones should be assigned as if
-it's a brand new gateway. Note that this should only happen if a gateway has
-been down for a while (several minutes), otherwise a flapping gateway could
-have wide ranging, unpredictable, consequences.
-
-Note that preemption avoidance should be optional depending on the deployment.
-One necessarily sacrifices optimal load balancing to satisfy these requirements
-as new gateways will get no traffic on boot. Thus, this feature represents a
-trade-off which must be made on a per installation basis.
-
-Fully Active-Active HA
-~~~~~~~~~~~~~~~~~~~~~~
-
-::
-
- Fully Active-Active HA
-
- +----------------+ +----------------+
- | | | |
- | A B C D E | | A B C D E |
- | | | |
- +----------------+ +----------------+
- ^ ^ ^ ^
- | | | |
- | | | |
- + + + +
- Traffic
-
-The final step in L3HA is to have true active-active HA. In this scenario each
-router has an instance on each Gateway, and a mechanism similar to ECMP is used
-to distribute traffic evenly among all instances. This mechanism would require
-Gateways to participate in routing protocols with the physical network to
-attract traffic and alert of failures. It is out of scope of this document,
-but may eventually be necessary.
-
-L2HA
-----
-
-L2HA is very difficult to get right. Unlike L3HA, where the consequences of
-problems are minor, in L2HA if two gateways are both transiently active, an L2
-loop triggers and a broadcast storm results. In practice to get around this,
-gateways end up implementing an overly conservative "when in doubt drop all
-traffic" policy, or they implement something like MLAG.
-
-MLAG has multiple gateways work together to pretend to be a single L2 switch
-with a large LACP bond. In principle, it's the right solution to the problem
-as it solves the broadcast storm problem, and has been deployed successfully in
-other contexts. That said, it's difficult to get right and not recommended.
diff --git a/Documentation/topics/index.rst b/Documentation/topics/index.rst
index 057649dd7..fcb741637 100644
--- a/Documentation/topics/index.rst
+++ b/Documentation/topics/index.rst
@@ -27,7 +27,7 @@
Deep Dive
=========
-How Open vSwitch and OVN are implemented and, where necessary, why it was
+How Open vSwitch is implemented and, where necessary, why it was
implemented that way.
OVS
@@ -52,19 +52,3 @@ OVS
tracing
idl-compound-indexes
-OVN
----
-
-.. toctree::
- :maxdepth: 2
-
- high-availability
- role-based-access-control
- ovn-news-2.8
-
-.. list-table::
-
- * - ovn-architecture(7)
- - `(pdf) <http://openvswitch.org/support/dist-docs/ovn-architecture.7.pdf>`__
- - `(html) <http://openvswitch.org/support/dist-docs/ovn-architecture.7.html>`__
- - `(plain text) <http://openvswitch.org/support/dist-docs/ovn-architecture.7.txt>`__
diff --git a/Documentation/topics/ovn-news-2.8.rst b/Documentation/topics/ovn-news-2.8.rst
deleted file mode 100644
index fae0a4278..000000000
--- a/Documentation/topics/ovn-news-2.8.rst
+++ /dev/null
@@ -1,278 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-===============================
-What's New with OVS and OVN 2.8
-===============================
-
-This document is about what was added in Open vSwitch 2.8, which was released
-at the end of August 2017, concentrating on the new features in OVN. It also
-covers some of what is coming up in Open vSwitch and OVN 2.9, which is due to
-be released in February 2018. OVN has many features, and this document does
-not cover every new or enhanced feature (but contributions are welcome).
-
-This document assumes a basic familiarity with Open vSwitch, OVN, and their
-associated tools. For more information, please refer to the Open vSwitch and
-OVN documentation, such as the ``ovn-architecture``\(7) manpage.
-
-Debugging and Troubleshooting
------------------------------
-
-Before version 2.8, Open vSwitch command-line tools were far more painful to
-use than they needed to be. This section covers the improvements made to the
-CLI in the 2.8 release.
-
-User-Hostile UUIDs
-~~~~~~~~~~~~~~~~~~
-
-The OVN CLI, through ``ovn-nbctl``, ``ovn-nbctl``, and ``ovn-trace``, used
-full-length UUIDs almost everywhere. It didn't even provide any assistance
-with completion, etc., which in practice meant always cutting and pasting UUIDs
-from one command or window to another. This problem wasn't limited to the
-places where one would expect to have to see or use a UUID, either. In many
-places where one would expect to be able to use a network, router, or port
-name, a UUID was required instead. In many places where one would want to see
-a name, the UUID was displayed instead. More than anything else, these
-shortcomings made the CLI user-hostile.
-
-There was an underlying problem that the southbound database didn't actually
-contain all the information needed to provide a decent user interface. In some
-cases, for example, the human-friendly names that one would want to use for
-entities simply weren't part of the database. These names weren't necessary
-for correctness, only for usability.
-
-OVN 2.8 eased many of these problems. Most parts of the CLI now allow the user
-to abbreviate UUIDs, as long as the abbreviations are unique within the
-database. Some parts of the CLI where full-length UUIDs make output hard to
-read now abbreviate them themselves. Perhaps more importantly, in many places
-the OVN CLI now displays and accepts human-friendly names for networks,
-routers, ports, and other entities. In the places where the names were not
-previously available, OVN (through ``ovn-northd``) now copies the names into
-the southbound database.
-
-The CLIs for layers below OVN, at the OpenFlow and datapath layers with
-``ovs-ofctl`` and ``ovs-dpctl``, respectively, had some similar problems in
-which numbers were used for entities that had human-friendly names. Open
-vSwitch 2.8 also solves some of those problems. Other than that, the most
-notable enhancement in this area was the ``--no-stats`` option to ``ovs-ofctl
-dump-flows``, which made that command's output more readable for the cases
-where per-flow statistics were not interesting to the reader.
-
-Connections Between Levels
-~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-OVN and Open vSwitch work almost like a stack of compilers: the OVN Neutron
-plugin translates Neutron configuration into OVN northbound configuration,
-which ``ovn-northd`` translates into logical flows, which ``ovn-controller``
-translates into OpenFlow flows, which ``ovs-vswitchd`` translates into datapath
-flows. For debugging and troubleshooting it is often necessary to understand
-exactly how these translations work. The relationship from a logical flow to
-its OpenFlow flows, or in the other direction, from an OpenFlow flow back to
-the logical flow that produced it, was often of particular interest, but OVN
-didn't provide good tools for the job.
-
-OVN 2.8 added some new features that ease these jobs. ``ovn-sbctl lflow-list``
-has a new option ``--ovs`` that lists the OpenFlow flows on a particular
-chassis that were generated from the logical flows that it lists.
-``ovn-trace`` also added a similar ``--ovs`` option that applies to the logical
-flows it traces.
-
-In the other direction, OVN 2.8 added a new utility ``ovn-detrace`` that, given
-an Open vSwitch trace of OpenFlow flows, annotates it with the logical flows
-that yielded those OpenFlow flows.
-
-Distributed Firewall
-~~~~~~~~~~~~~~~~~~~~
-
-OVN supports a distributed firewall with stateful connection tracking to ensure
-that only packets for established connections, or those that the configuration
-explicitly allows, can ingress a given VM or container. Neutron uses this
-feature by default. Most packets in an OpenStack environment pass through it
-twice, once after egress from the packet's source VM and once before ingress
-into its destination VM. Before OVN 2.8, the ``ovn-trace`` program, which
-shows the path of a packet through an OVN logical network, did not support the
-logical firewall, which in practice made it almost useless for Neutron.
-
-In OVN 2.8, ``ovn-trace`` adds support for the logical firewall. By default it
-assumes that packets are part of an established connection, which is usually
-what the user wants as part of the trace. It also accepts command-line options
-to override that assumption, which allows the user to discover the treatment of
-packets that the firewall should drop.
-
-At the next level deeper, prior to Open vSwitch 2.8, the OpenFlow tracing
-command ``ofproto/trace`` also supported neither the connection tracking
-feature underlying the OVN distributed firewall nor the "recirculation" feature
-that accompanied it. This meant that, even if the user tried to look deeper
-into the distributed firewall mechanism, he or she would encounter a further
-roadblock. Open vSwitch 2.8 added support for both of these features as well.
-
-Summary Display
-~~~~~~~~~~~~~~~
-
-``ovn-nbctl show`` and ``ovn-sbctl show``, for showing an overview of the OVN
-configuration, didn't show a lot of important information. OVN adds some more
-useful information here.
-
-DNS, and IPAM
--------------
-
-OVN 2.8 adds a built-in DNS server designed for assigning names to VMs and
-containers within an OVN logical network. DNS names are assigned using records
-in the OVN northbound database and, like other OVN features, translated into
-logical flows at the OVN southbound layer. DNS requests directed to the OVN
-DNS server never leave the hypervisor from which the request is sent; instead,
-OVN processes and replies to the request from its ``ovn-controller`` local
-agent. The OVN DNS server is not a general-purpose DNS server and cannot be
-used for that purpose.
-
-OVN includes simple built-in support for IP address management (IPAM), in which
-OVN assigns IP addresses to VMs or containers from a pool or pools of IP
-addresses delegated to it by the administrator. Before OVN 2.8, OVN IPAM only
-supported IPv4 addresses; OVN 2.8 adds support for IPv6. OVN 2.8 also enhances
-the address pool support to allow specific addresses to be excluded. Neutron
-assigns IP addresses itself and does not use OVN IPAM.
-
-High Availability
------------------
-
-As a distributed system, in OVN a lot can go wrong. As OVN advances, it adds
-redundancy in places where currently a single failure could disrupt the
-functioning of the system as a whole. OVN 2.8 adds two new kinds of high
-availability.
-
-ovn-northd HA
-~~~~~~~~~~~~~
-
-The ``ovn-northd`` program sits between the OVN northbound and southbound
-databases and translates from a logical network configuration into logical
-flows. If ``ovn-northd`` itself or the host on which it runs fails, then
-updates to the OVN northbound configuration will not propagate to the
-hypervisors and the OVN configuration freezes in place until ``ovn-northd``
-restarts.
-
-OVN 2.8 adds support for active-backup HA to ``ovn-northd``. When more than
-one ``ovn-northd`` instance runs, it uses an OVSDB locking feature to
-automatically choose a single active instance. When that instance dies or
-becomes nonresponsive, the OVSDB server automatically choose one of the
-remaining instance(s) to take over.
-
-L3 Gateway HA
-~~~~~~~~~~~~~
-
-In OVN 2.8, multiple chassis may now be specified for L3 gateways. When more
-than one chassis is specified, OVN manages high availability for that gateway.
-Each hypervisor uses the BFD protocol to keep track of the gateway nodes that
-are currently up. At any given time, a hypervisor uses the highest-priority
-gateway node that is currently up.
-
-OVSDB
------
-
-The OVN architecture relies heavily on OVSDB, the Open vSwitch database, for
-hosting the northbound and southbound databases. OVSDB was originally selected
-for this purpose because it was already used in Open vSwitch for configuring
-OVS itself and, thus, it was well integrated with OVS and well supported in C
-and Python, the two languages that are used in Open vSwitch.
-
-OVSDB was well designed for its original purpose of configuring Open vSwitch.
-It supports ACID transactions, has a small, efficient server, a flexible schema
-system, and good support for troubleshooting and debugging. However, it lacked
-several features that are important for OVN but not for Open vSwitch. As OVN
-advances, these missing features have become more and more of a problem. One
-option would be to switch to a different database that already has many of
-these features, but despite a careful search, no ideal existing database was
-identified, so the project chose instead to improve OVSDB where necessary to
-bring it up to speed. The following sections talk more about recent and future
-improvements.
-
-High Availability
-~~~~~~~~~~~~~~~~~
-
-When ``ovsdb-server`` was only used for OVS configuration, high availability
-was not important. ``ovsdb-server`` was capable of restarting itself
-automatically if it crashed, and if the whole system went down then Open
-vSwitch itself was dead too, so the database server's failure was not
-important.
-
-In contrast, the northbound and southbound databases are centralized components
-of a distributed system, so it is important that they not be a single point of
-failure for the system as a whole. In released versions of OVN,
-``ovsdb-server`` supports only "active-backup replication" across a pair of
-servers. This means that if one server goes down, the other can pick it back
-up approximately where the other one left off. The servers do not have
-built-in support for deciding at any given time which is the active and which
-the backup, so the administrator must configure an external agent to do this
-management.
-
-Active-backup replication is not entirely satisfactory, for multiple reasons.
-Replication is only approximate. Configuring the external agent requires extra
-work. There is no benefit from the backup server except when the active server
-fails. At most two servers can be used.
-
-A new form of high availability for OVSDB is under development for the OVN 2.9
-release, based on the Raft algorithm for distributed consensus. Whereas
-replication uses two servers, clustering using Raft requires three or more
-(typically an odd number) and continues functioning as long as more than half
-of the servers are up. The clustering implementation is built into
-``ovsdb-server`` and does not require an external agent. Clustering preserves
-the ACID properties of the database, so that a transaction that commits is
-guaranteed to persist. Finally, reads (which are the bulk of the OVN workload)
-scale with the size of the cluster, so that adding more servers should improve
-performance as the number of hypervisors in an OVN deployment increases. As of
-this writing, OVSDB support for clustering is undergoing development and early
-deployment testing.
-
-RBAC security
-~~~~~~~~~~~~~
-
-Until Open vSwitch 2.8, ``ovsdb-server`` had little support for access control
-within a database. If an OVSDB client could modify the database at all, it
-could make arbitrary changes. This was sufficient for most uses case to that
-point.
-
-Hypervisors in an OVN deployment need access to the OVN southbound database.
-Most of their access is reads, to find out about the OVN configuration.
-Hypervisors do need some write access to the southbound database, primarily to
-let the other hypervisors know what VMs and containers they are running and how
-to reach them. Thus, OVN gives all of the hypervisors in the OVN deployment
-write access to the OVN southbound database. This is fine when all is well,
-but if any of the hypervisors were compromised then they could disrupt the
-entire OVN deployment by corrupting the database.
-
-The OVN developers considered a few ways to solve this problem. One way would
-be to introduce a new central service (perhaps in ``ovn-northd``) that provided
-only the kinds of writes that the hypervisors legitimately need, and then grant
-hypervisors direct access to the southbound database only for reads. But
-ultimately the developers decided to introduce a new form of more access
-control for OVSDB, called the OVSDB RBAC (role-based access control) feature.
-OVSDB RBAC allows for granular enough control over access that hypervisors can
-be granted only the ability to add, modify, and delete the records that relate
-to themselves, preventing them from corrupting the database as a whole.
-
-Further Directions
-------------------
-
-For more information about new features in OVN and Open vSwitch, please refer
-to the NEWS file distributed with the source tree. If you have questions about
-Open vSwitch or OVN features, please feel free to write to the Open vSwitch
-discussion mailing list at ovs-discuss@openvswitch.org.
diff --git a/Documentation/topics/role-based-access-control.rst b/Documentation/topics/role-based-access-control.rst
deleted file mode 100644
index 8f2a3a998..000000000
--- a/Documentation/topics/role-based-access-control.rst
+++ /dev/null
@@ -1,101 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-=========================
-Role Based Access Control
-=========================
-
-Where SSL provides authentication when connecting to an OVS database, role
-based access control (RBAC) provides authorization to operations performed
-by clients connecting to an OVS database. RBAC allows for administrators to
-restrict the database operations a client may perform and thus enhance the
-security already provided by SSL.
-
-In theory, any OVS database could define RBAC roles and permissions, but at
-present only the OVN southbound database has the appropriate tables defined to
-facilitate RBAC.
-
-Mechanics
----------
-RBAC is intended to supplement SSL. In order to enable RBAC, the connection to
-the database must use SSL. Some permissions in RBAC are granted based on the
-certificate common name (CN) of the connecting client.
-
-RBAC is controlled with two database tables, RBAC_Role and RBAC_Permission.
-The RBAC_Permission table contains records that describe a set of permissions
-for a given table in the database.
-
-The RBAC_Permission table contains the following columns:
-
-table
- The table in the database for which permissions are being described.
-insert_delete
- Describes whether insertion and deletion of records is allowed.
-update
- A list of columns that are allowed to be updated.
-authorization
- A list of column names. One of the listed columns must match the SSL
- certificate CN in order for the attempted operation on the table to
- succeed. If a key-value pair is provided, then the key is the column name,
- and the value is the name of a key in that column. An empty string gives
- permission to all clients to perform operations.
-
-The RBAC_Role table contains the following columns:
-
-name
- The name of the role being defined
-permissions
- A list of key-value pairs. The key is the name of a table in the database,
- and the value is a UUID of a record in the RBAC_Permission table that
- describes the permissions the role has for that table.
-
-.. note::
-
- All tables not explicitly referenced in an RBAC_Role record are read-only
-
-In order to enable RBAC, specify the role name as an argument to the
-set-connection command for the database. As an example, to enable the
-"ovn-controller" role on the OVN southbound database, use the following
-command:
-
-::
-
- $ ovn-sbctl set-connection role=ovn-controller ssl:192.168.0.1:6642
-
-Pre-defined Roles
------------------
-This section describes roles that have been defined internally by OVS/OVN.
-
-ovn-controller
-~~~~~~~~~~~~~~
-The ovn-controller role is specified in the OVN southbound database and is
-intended for use by hypervisors running the ovn-controller daemon.
-ovn-controller connects to the OVN southbound database mostly to read
-information, but there are a few cases where ovn-controller also needs to
-write. The ovn-controller role was designed to allow for ovn-controllers
-to write to the southbound database only in places where it makes sense to do
-so. This way, if an intruder were to take over a hypervisor running
-ovn-controller, it is more difficult to compromise the entire overlay network.
-
-It is strongly recommended to set the ovn-controller role for the OVN
-southbound database to enhance security.
diff --git a/Documentation/tutorials/index.rst b/Documentation/tutorials/index.rst
index 35340ee56..5ec62beab 100644
--- a/Documentation/tutorials/index.rst
+++ b/Documentation/tutorials/index.rst
@@ -27,8 +27,7 @@
Tutorials
=========
-Getting started with Open vSwitch (OVS) and Open Virtual Network (OVN) for Open
-vSwitch.
+Getting started with Open vSwitch (OVS).
.. TODO(stephenfin): We could really do with a few basic tutorials, along with
some more specialized ones (DPDK, XenServer, Windows). The latter could
@@ -42,8 +41,4 @@ vSwitch.
faucet
ipsec
ovs-advanced
- ovn-sandbox
- ovn-openstack
- ovn-rbac
- ovn-ipsec
ovs-conntrack
diff --git a/Documentation/tutorials/ovn-ipsec.rst b/Documentation/tutorials/ovn-ipsec.rst
deleted file mode 100644
index feb695ea3..000000000
--- a/Documentation/tutorials/ovn-ipsec.rst
+++ /dev/null
@@ -1,146 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-==================
-OVN IPsec Tutorial
-==================
-
-This document provides a step-by-step guide for encrypting tunnel traffic with
-IPsec in Open Virtual Network (OVN). OVN tunnel traffic is transported by
-physical routers and switches. These physical devices could be untrusted
-(devices in public network) or might be compromised. Enabling IPsec encryption
-for the tunnel traffic can prevent the traffic data from being monitored and
-manipulated. More details about the OVN IPsec design can be found in
-``ovn-architecture``\(7) manpage.
-
-This document assumes OVN is installed in your system and runs normally. Also,
-you need to install OVS IPsec packages in each chassis (refer to
-:ref:`install-ovs-ipsec`).
-
-Generating Certificates and Keys
---------------------------------
-
-OVN chassis uses CA-signed certificate to authenticate peer chassis for
-building IPsec tunnel. If you have enabled Role-Based Access Control (RBAC) in
-OVN, you can use the RBAC SSL certificates and keys to set up OVN IPsec. Or you
-can generate separate certificates and keys with ``ovs-pki`` (refer to
-:ref:`gen-certs-keys`).
-
-.. note::
-
- OVN IPsec requires x.509 version 3 certificate with the subjectAltName DNS
- field setting the same string as the common name (CN) field. CN should be
- set as the chassis name. ``ovs-pki`` in Open vSwitch 2.10.90 and later
- generates such certificates. Please generate compatible certificates if you
- use another PKI tool, or an older version of ``ovs-pki``, to manage
- certificates.
-
-Configuring OVN IPsec
----------------------
-
-You need to install the CA certificate, chassis certificate and private key in
-each chassis. Use the following command::
-
- $ ovs-vsctl set Open_vSwitch . \
- other_config:certificate=/path/to/chassis-cert.pem \
- other_config:private_key=/path/to/chassis-privkey.pem \
- other_config:ca_cert=/path/to/cacert.pem
-
-Enabling OVN IPsec
-------------------
-
-To enable OVN IPsec, set ``ipsec`` column in ``NB_Global`` table of the
-northbound database to true::
-
- $ ovn-nbctl set nb_global . ipsec=true
-
-With OVN IPsec enabled, all tunnel traffic in OVN will be encrypted with IPsec.
-To disable it, set ``ipsec`` column in ``NB_Global`` table of the northbound
-database to false::
-
- $ ovn-nbctl set nb_global . ipsec=false
-
-Troubleshooting
----------------
-
-The ``ovs-monitor-ipsec`` daemon in each chassis manages and monitors the IPsec
-tunnel state. Use the following ``ovs-appctl`` command to view
-``ovs-monitor-ipsec`` internal representation of tunnel configuration::
-
- $ ovs-appctl -t ovs-monitor-ipsec tunnels/show
-
-If there is a misconfiguration, then ``ovs-appctl`` should indicate why.
-For example::
-
- Interface name: ovn-host_2-0 v1 (CONFIGURED) <--- Should be set
- to CONFIGURED. Otherwise,
- error message will be
- provided
- Tunnel Type: geneve
- Remote IP: 2.2.2.2
- SKB mark: None
- Local cert: /path/to/chassis-cert.pem
- Local name: host_1
- Local key: /path/to/chassis-privkey.pem
- Remote cert: None
- Remote name: host_2
- CA cert: /path/to/cacert.pem
- PSK: None
- Ofport: 2 <--- Whether ovs-vswitchd has assigned Ofport
- number to this Tunnel Port
- CFM state: Disabled <--- Whether CFM declared this tunnel healthy
- Kernel policies installed:
- ... <--- IPsec policies for this OVS tunnel in
- Linux Kernel installed by strongSwan
- Kernel security associations installed:
- ... <--- IPsec security associations for this OVS
- tunnel in Linux Kernel installed by
- strongswan
- IPsec connections that are active:
- ... <--- IPsec "connections" for this OVS
- tunnel
-
-If you don't see any active connections, try to run the following command to
-refresh the ``ovs-monitor-ipsec`` daemon::
-
- $ ovs-appctl -t ovs-monitor-ipsec refresh
-
-You can also check the logs of the ``ovs-monitor-ipsec`` daemon and the IKE
-daemon to locate issues. ``ovs-monitor-ipsec`` outputs log messages to
-``/var/log/openvswitch/ovs-monitor-ipsec.log``.
-
-Bug Reporting
--------------
-
-If you think you may have found a bug with security implications, like
-
-1. IPsec protected tunnel accepted packets that came unencrypted; OR
-2. IPsec protected tunnel allowed packets to leave unencrypted;
-
-Then report such bugs according to :doc:`/internals/security`.
-
-If bug does not have security implications, then report it according to
-instructions in :doc:`/internals/bugs`.
-
-If you have suggestions to improve this tutorial, please send a email to
-ovs-discuss@openvswitch.org.
diff --git a/Documentation/tutorials/ovn-openstack.rst b/Documentation/tutorials/ovn-openstack.rst
deleted file mode 100644
index c6dff5e55..000000000
--- a/Documentation/tutorials/ovn-openstack.rst
+++ /dev/null
@@ -1,1922 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-======================
-OVN OpenStack Tutorial
-======================
-
-This tutorial demonstrates how OVN works in an OpenStack "DevStack"
-environment. It was tested with the "master" branches of DevStack and
-Open vSwitch near the beginning of May 2017. Anyone using an earlier
-version is likely to encounter some differences. In particular, we
-noticed some shortcomings in OVN utilities while writing the tutorial
-and pushed out some improvements, so it's best to use recent Open
-vSwitch at least from that point of view.
-
-The goal of this tutorial is to demonstrate OVN in an end-to-end way,
-that is, to show how it works from the cloud management system at the
-top (in this case, OpenStack and specifically its Neutron networking
-subsystem), through the OVN northbound and southbound databases, to
-the bottom at the OVN local controller and Open vSwitch data plane.
-We hope that this demonstration makes it easier for users and
-potential users to understand how OVN works and how to debug and
-troubleshoot it.
-
-In addition to new material, this tutorial incorporates content from
-``testing.rst`` in OpenStack networking-ovn, by Russell Bryant and
-others. Without that example, this tutorial could not have been
-written.
-
-We provide enough details in the tutorial that you should be able to
-fully follow along, by creating a DevStack VM and cloning DevStack and
-so on. If you want to do this, start out from `Setting Up DevStack`_
-below.
-
-Setting Up DevStack
--------------------
-
-This section explains how to install DevStack, a kind of OpenStack
-packaging for developers, in a way that allows you to follow along
-with the tutorial in full.
-
-Unless you have a spare computer laying about, it's easiest to install
-DevStacck in a virtual machine. This tutorial was built using a VM
-implemented by KVM and managed by virt-manager. I recommend
-configuring the VM configured for the x86-64 architecture, 4 GB RAM, 2
-VCPUs, and a 20 GB virtual disk.
-
-.. note::
-
- If you happen to run your Linux-based host with 32-bit userspace,
- then you will have some special issues, even if you use a 64-bit
- kernel:
-
- * You may find that you can get 32-bit DevStack VMs to work to some
- extent, but I personally got tired of finding workarounds. I
- recommend running your VMs in 64-bit mode. To get this to work,
- I had to go to the CPUs tab for the VM configuration in
- virt-manager and change the CPU model from the one originally
- listed to "Hypervisor Default' (it is curious that this is not
- the default!).
-
- * On a host with 32-bit userspace, KVM supports VMs with at most
- 2047 MB RAM. This is adequate, barely, to start DevStack, but it
- is not enough to run multiple (nested) VMs. To prevent
- out-of-memory failures, set up extra swap space in the guest.
- For example, to add 2 GB swap::
-
- $ sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
- $ sudo mkswap /swapfile
- $ sudo swapon /swapfile
-
- and then add a line like this to ``/etc/fstab`` to add the new
- swap automatically upon reboot::
-
- /swapfile swap swap defaults 0 0
-
-Here are step-by-step instructions to get started:
-
-1. Install a VM.
-
- I tested these instructions with Centos 7.3. Download the "minimal
- install" ISO and booted it. The install is straightforward. Be
- sure to enable networking, and set a host name, such as
- "ovn-devstack-1". Add a regular (non-root) user, and check the box
- "Make this user administrator". Also, set your time zone.
-
-2. You can SSH into the DevStack VM, instead of running from a
- console. I recommend it because it's easier to cut and paste
- commands into a terminal than a VM console. You might also
- consider using a very wide terminal, perhaps 160 columns, to keep
- tables from wrapping.
-
- To improve convenience further, you can make it easier to log in
- with the following steps, which are optional:
-
- a. On your host, edit your ``~/.ssh/config``, adding lines like
- the following::
-
- Host ovn-devstack-1
- Hostname VMIP
- User VMUSER
-
- where VMIP is the VM's IP address and VMUSER is your username
- inside the VM. (You can omit the ``User`` line if your
- username is the same in the host and the VM.) After you do
- this, you can SSH to the VM by name, e.g. ``ssh
- ovn-devstack-1``, and if command-line completion is set up in
- your host shell, you can shorten that to something like ``ssh
- ovn`` followed by hitting the Tab key.
-
- b. If you have SSH public key authentication set up, with an SSH
- agent, run on your host::
-
- $ ssh-copy-id ovn-devstack-1
-
- and type your password once. Afterward, you can log in without
- typing your password again.
-
- (If you don't already use SSH public key authentication and an
- agent, consider looking into it--it will save you time in the
- long run.)
-
- c. Optionally, inside the VM, append the following to your
- ``~/.bash_profile``::
-
- . $HOME/devstack/openrc admin
-
- It will save you running it by hand each time you log in. But
- it also prints garbage to the console, which can screw up
- services like ``ssh-copy-id``, so be careful.
-
-2. Boot into the installed system and log in as the regular user, then
- install Git::
-
- $ sudo yum install git
-
- .. note::
-
- If you installed a 32-bit i386 guest (against the advice above),
- install a non-PAE kernel and reboot into it at this point::
-
- $ sudo yum install kernel-core kernel-devel
- $ sudo reboot
-
- Be sure to select the non-PAE kernel from the list at boot.
- Without this step, DevStack will fail to install properly later.
-
-3. Get copies of DevStack and OVN and set them up::
-
- $ git clone http://git.openstack.org/openstack-dev/devstack.git
- $ git clone http://git.openstack.org/openstack/networking-ovn.git
- $ cd devstack
- $ cp ../networking-ovn/devstack/local.conf.sample local.conf
-
- .. note::
-
- If you installed a 32-bit i386 guest (against the advice above),
- at this point edit ``local.conf`` to add the following line::
-
- CIRROS_ARCH=i386
-
-4. Initialize DevStack::
-
- $ ./stack.sh
-
- This will spew many screenfuls of text, and the first time you run
- it, it will download lots of software from the Internet. The
- output should eventually end with something like this::
-
- This is your host IP address: 172.16.189.6
- This is your host IPv6 address: ::1
- Horizon is now available at http://172.16.189.6/dashboard
- Keystone is serving at http://172.16.189.6/identity/
- The default users are: admin and demo
- The password: password
- 2017-03-09 15:10:54.117 | stack.sh completed in 2110 seconds.
-
- If there's some kind of failure, you can restart by running
- ``./stack.sh`` again. It won't restart exactly where it left off,
- but steps up to the one where it failed will skip the download
- steps. (Sometimes blindly restarting after a failure will allow it
- to succeed.) If you reboot your VM, you need to rerun this
- command. (If you run into trouble with ``stack.sh`` after
- rebooting your VM, try running ``./unstack.sh``.)
-
- At this point you can navigate a web browser on your host to the
- Horizon dashboard URL. Many OpenStack operations can be initiated
- from this UI. Feel free to explore, but this tutorial focuses on
- the alternative command-line interfaces because they are easier to
- explain and to cut and paste.
-
-5. As of this writing, you need to run the following to fix a problem
- with using VM consoles from the OpenStack web instance::
-
- $ (cd /opt/stack/noVNC && git checkout v0.6.0)
-
- See
- https://serenity-networks.com/how-to-fix-setkeycodes-00-and-unknown-key-pressed-console-errors-on-openstack/
- for more details.
-
-6. The firewall in the VM by default allows SSH access but not HTTP.
- You will probably want HTTP access to use the OpenStack web
- interface. The following command enables that. (It also enables
- every other kind of network access, so if you're concerned about
- security then you might want to find a more targeted approach.)
-
- ::
-
- $ sudo iptables -F
-
- (You need to re-run this if you reboot the VM.)
-
-7. To use OpenStack command line utilities in the tutorial, run::
-
- $ . ~/devstack/openrc admin
-
- This needs to be re-run each time you log in (but see the following
- section).
-
-DevStack preliminaries
-----------------------
-
-Before we really jump in, let's set up a couple of things in DevStack.
-This is the first real test that DevStack is working, so if you get
-errors from any of these commands, it's a sign that ``stack.sh``
-didn't finish properly, or perhaps that you didn't run the ``openrc
-admin`` command at the end of the previous instructions.
-
-If you stop and restart DevStack via ``unstack.sh`` followed by
-``stack.sh``, you have to rerun these steps.
-
-1. For SSH access to the VMs we're going to create, we'll need a SSH
- keypair. Later on, we'll get OpenStack to install this keypair
- into VMs. Create one with::
-
- $ openstack keypair create demo > ~/id_rsa_demo
- $ chmod 600 ~/id_rsa_demo
-
-2. By default, DevStack security groups drop incoming traffic, but to
- test networking in a reasonable way we need to enable it. You only
- need to actually edit one particular security group, but DevStack
- creates multiple and it's somewhat difficult to figure out which
- one is important because all of them are named "default". So, the
- following adds rules to allow SSH and ICMP traffic into **every**
- security group::
-
- $ for group in $(openstack security group list -f value -c ID); do \
- openstack security group rule create --ingress --ethertype IPv4 --dst-port 22 --protocol tcp $group; \
- openstack security group rule create --ingress --ethertype IPv4 --protocol ICMP $group; \
- done
-
-3. Later on, we're going to create some VMs and we'll need an
- operating system image to install. DevStack comes with a very
- simple image built-in, called "cirros", which works fine. We need
- to get the UUID for this image. Our later commands assume shell
- variable ``IMAGE_ID`` holds this UUID. You can set this by hand,
- e.g.::
-
- $ openstack image list
- +--------------------------------------+--------------------------+--------+
- | ID | Name | Status |
- +--------------------------------------+--------------------------+--------+
- | 77f37d2c-3d6b-4e99-a01b-1fa5d78d1fa1 | cirros-0.3.5-x86_64-disk | active |
- +--------------------------------------+--------------------------+--------+
- $ IMAGE_ID=73ca34f3-63c4-4c10-a62f-4540afc24eaa
-
- or by parsing CLI output::
-
- $ IMAGE_ID=$(openstack image list -f value -c ID)
-
- .. note::
-
- Your image ID will differ from the one above, as will every UUID
- in this tutorial. They will also change every time you run
- ``stack.sh``. The UUIDs are generated randomly.
-
-Shortening UUIDs
-----------------
-
-OpenStack, OVN, and Open vSwitch all really like UUIDs. These are
-great for uniqueness, but 36-character strings are terrible for
-readability. Statistically, just the first few characters are enough
-for uniqueness in small environments, so let's define a helper to make
-things more readable::
-
- $ abbrev() { a='[0-9a-fA-F]' b=$a$a c=$b$b; sed "s/$b-$c-$c-$c-$c$c$c//g"; }
-
-You can use this as a filter to abbreviate UUIDs. For example, use it
-to abbreviate the above image list::
-
- $ openstack image list -f yaml | abbrev
- - ID: 77f37d
- Name: cirros-0.3.5-x86_64-disk
- Status: active
-
-The command above also adds ``-f yaml`` to switch to YAML output
-format, because abbreviating UUIDs screws up the default table-based
-formatting and because YAML output doesn't produce wrap columns across
-lines and therefore is easier to cut and paste.
-
-Overview
---------
-
-Now that DevStack is ready, with OVN set up as the networking
-back-end, here's an overview of what we're going to do in the
-remainder of the demo, all via OpenStack:
-
-1. Switching: Create an OpenStack network ``n1`` and VMs ``a`` and
- ``b`` attached to it.
-
- An OpenStack network is a virtual switch; it corresponds to an OVN
- logical switch.
-
-2. Routing: Create a second OpenStack network ``n2`` and VM ``c``
- attached to it, then connect it to network ``n1`` by creating an
- OpenStack router and attaching ``n1`` and ``n2`` to it.
-
-3. Gateways: Make VMs ``a`` and ``b`` available via an external network.
-
-4. IPv6: Add IPv6 addresses to our VMs to demonstrate OVN support for
- IPv6 routing.
-
-5. ACLs: Add and modify OpenStack stateless and stateful rules in
- security groups.
-
-6. DHCP: How it works in OVN.
-
-7. Further directions: Adding more compute nodes.
-
-At each step, we will take a look at how the features in question work
-from OpenStack's Neutron networking layer at the top to the data plane
-layer at the bottom. From the highest to lowest level, these layers
-and the software components that connect them are:
-
-* OpenStack Neutron, which as the top level in the system is the
- authoritative source of the virtual network configuration.
-
- We will use OpenStack's ``openstack`` utility to observe and modify
- Neutron and other OpenStack configuration.
-
-* networking-ovn, the Neutron driver that interfaces with OVN and
- translates the internal Neutron representation of the virtual
- network into OVN's representation and pushes that representation
- down the OVN northbound database.
-
- In this tutorial it's rarely worth distinguishing Neutron from
- networking-ovn, so we usually don't break out this layer separately.
-
-* The OVN Northbound database, aka NB DB. This is an instance of
- OVSDB, a simple general-purpose database that is used for multiple
- purposes in Open vSwitch and OVN. The NB DB's schema is in terms of
- networking concepts such as switches and routers. The NB DB serves
- the purpose that in other systems might be filled by some kind of
- API; for example, in place of calling an API to create or delete a
- logical switch, networking-ovn performs these operations by
- inserting or deleting a row in the NB DB's Logical_Switch table.
-
- We will use OVN's ``ovn-nbctl`` utility to observe the NB DB. (We
- won't directly modify data at this layer or below. Because
- configuration trickles down from Neutron through the stack, the
- right way to make changes is to use the ``openstack`` utility or
- another OpenStack interface and then wait for them to percolate
- through to lower layers.)
-
-* The ovn-northd daemon, a program that runs centrally and translates
- the NB DB's network representation into the lower-level
- representation used by the OVN Southbound database in the next
- layer. The details of this daemon are usually not of interest,
- although without it OVN will not work, so this tutorial does not
- often mention it.
-
-* The OVN Southbound database, aka SB DB, which is also an OVSDB
- database. Its schema is very different from the NB DB. Instead of
- familiar networking concepts, the SB DB defines the network in terms
- of collections of match-action rules called "logical flows", which
- while similar in concept to OpenFlow flows use logical concepts, such
- as virtual machine instances, in place of physical concepts like
- physical Ethernet ports.
-
- We will use OVN's ``ovn-sbctl`` utility to observe the SB DB.
-
-* The ovn-controller daemon. A copy of ovn-controller runs on each
- hypervisor. It reads logical flows from the SB DB, translates them
- into OpenFlow flows, and sends them to Open vSwitch's ovs-vswitchd
- daemon. Like ovn-northd, usually the details of what this daemon
- are not of interest, even though it's important to the operation of
- the system.
-
-* ovs-vswitchd. This program runs on each hypervisor. It is the core
- of Open vSwitch, which processes packets according to the OpenFlow
- flows set up by ovn-controller.
-
-* Open vSwitch datapath. This is essentially a cache designed to
- accelerate packet processing. Open vSwitch includes a few different
- datapaths but OVN installations typically use one based on the Open
- vSwitch Linux kernel module.
-
-Switching
----------
-
-Switching is the basis of networking in the real world and in virtual
-networking as well. OpenStack calls its concept of a virtual switch a
-"network", and OVN calls its corresponding concept a "logical switch".
-
-In this step, we'll create an OpenStack network ``n1``, then create
-VMs ``a`` and ``b`` and attach them to ``n1``.
-
-Creating network ``n1``
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Let's start by creating the network::
-
- $ openstack network create --project admin --provider-network-type geneve n1
-
-OpenStack needs to know the subnets that a network serves. We inform
-it by creating subnet objects. To keep it simple, let's give our
-network a single subnet for the 10.1.1.0/24 network. We have to give
-it a name, in this case ``n1subnet``::
-
- $ openstack subnet create --subnet-range 10.1.1.0/24 --network n1 n1subnet
-
-If you ask Neutron to show us the available networks, we see ``n1`` as
-well as the two networks that DevStack creates by default::
-
- $ openstack network list -f yaml | abbrev
- - ID: 5b6baf
- Name: n1
- Subnets: 5e67e7
- - ID: c02c4d
- Name: private
- Subnets: d88a34, fd87f9
- - ID: d1ac28
- Name: public
- Subnets: 0b1e79, c87dc1
-
-Neutron pushes this network setup down to the OVN northbound
-database. We can use ``ovn-nbctl show`` to see an overview of what's
-in the NB DB::
-
- $ ovn-nbctl show | abbrev
- switch 5b3d5f (neutron-c02c4d) (aka private)
- port b256dd
- type: router
- router-port: lrp-b256dd
- port f264e7
- type: router
- router-port: lrp-f264e7
- switch 2579f4 (neutron-d1ac28) (aka public)
- port provnet-d1ac28
- type: localnet
- addresses: ["unknown"]
- port ae9b52
- type: router
- router-port: lrp-ae9b52
- switch 3eb263 (neutron-5b6baf) (aka n1)
- router c59ad2 (neutron-9b057f) (aka router1)
- port lrp-ae9b52
- mac: "fa:16:3e:b2:d2:67"
- networks: ["172.24.4.9/24", "2001:db8::b/64"]
- port lrp-b256dd
- mac: "fa:16:3e:35:33:db"
- networks: ["fdb0:5860:4ba8::1/64"]
- port lrp-f264e7
- mac: "fa:16:3e:fc:c8:da"
- networks: ["10.0.0.1/26"]
- nat 80914c
- external ip: "172.24.4.9"
- logical ip: "10.0.0.0/26"
- type: "snat"
-
-This output shows that OVN has three logical switches, each of which
-corresponds to a Neutron network, and a logical router that
-corresponds to the Neutron router that DevStack creates by default.
-The logical switch that corresponds to our new network ``n1`` has no
-ports yet, because we haven't added any. The ``public`` and
-``private`` networks that DevStack creates by default have router
-ports that connect to the logical router.
-
-Using ovn-northd, OVN translates the NB DB's high-level switch and
-router concepts into lower-level concepts of "logical datapaths" and
-logical flows. There's one logical datapath for each logical switch
-or router::
-
- $ ovn-sbctl list datapath_binding | abbrev
- _uuid : 0ad69d
- external_ids : {logical-switch="5b3d5f", name="neutron-c02c4d", "name2"=private}
- tunnel_key : 1
-
- _uuid : a8a758
- external_ids : {logical-switch="3eb263", name="neutron-5b6baf", "name2"="n1"}
- tunnel_key : 4
-
- _uuid : 191256
- external_ids : {logical-switch="2579f4", name="neutron-d1ac28", "name2"=public}
- tunnel_key : 3
-
- _uuid : b87bec
- external_ids : {logical-router="c59ad2", name="neutron-9b057f", "name2"="router1"}
- tunnel_key : 2
-
-This output lists the NB DB UUIDs in external_ids:logical-switch and
-Neutron UUIDs in externals_ids:uuid. We can dive in deeper by viewing
-the OVN logical flows that implement a logical switch. Our new
-logical switch is a simple and almost pathological example given that
-it doesn't yet have any ports attached to it. We'll look at the
-details a bit later::
-
- $ ovn-sbctl lflow-list n1 | abbrev
- Datapath: "neutron-5b6baf" aka "n1" (a8a758) Pipeline: ingress
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;)
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;)
- ...
- Datapath: "neutron-5b6baf" aka "n1" (a8a758) Pipeline: egress
- table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;)
- table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;)
- ...
-
-We have one hypervisor (aka "compute node", in OpenStack parlance),
-which is the one where we're running all these commands. On this
-hypervisor, ovn-controller is translating OVN logical flows into
-OpenFlow flows ("physical flows"). It makes sense to go deeper, to
-see the OpenFlow flows that get generated from this datapath. By
-adding ``--ovs`` to the ``ovn-sbctl`` command, we can see OpenFlow
-flows listed just below their logical flows. We also need to use
-``sudo`` because connecting to Open vSwitch is privileged. Go ahead
-and try it::
-
- $ sudo ovn-sbctl --ovs lflow-list n1 | abbrev
- Datapath: "neutron-5b6baf" aka "n1" (a8a758) Pipeline: ingress
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;)
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;)
- ...
- Datapath: "neutron-5b6baf" aka "n1" (a8a758) Pipeline: egress
- table=0 (ls_out_pre_lb ), priority=0 , match=(1), action=(next;)
- table=1 (ls_out_pre_acl ), priority=0 , match=(1), action=(next;)
- ...
-
-You were probably disappointed: the output didn't change, and no
-OpenFlow flows were printed. That's because no OpenFlow flows are
-installed for this logical datapath, which in turn is because there
-are no VIFs for this logical datapath on the local hypervisor. For a
-better example, you can try ``ovn-sbctl --ovs`` on one of the other
-logical datapaths.
-
-Attaching VMs
-~~~~~~~~~~~~~
-
-A switch without any ports is not very interesting. Let's create a
-couple of VMs and attach them to the switch. Run the following
-commands, which create VMs named ``a`` and ``b`` and attaches them to
-our network ``n1`` with IP addresses 10.1.1.5 and 10.1.1.6,
-respectively. It is not actually necessary to manually assign IP
-address assignments, since OpenStack is perfectly happy to assign them
-itself from the subnet's IP address range, but predictable addresses
-are useful for our discussion::
-
- $ openstack server create --nic net-id=n1,v4-fixed-ip=10.1.1.5 --flavor m1.nano --image $IMAGE_ID --key-name demo a
- $ openstack server create --nic net-id=n1,v4-fixed-ip=10.1.1.6 --flavor m1.nano --image $IMAGE_ID --key-name demo b
-
-These commands return before the VMs are really finished being built.
-You can run ``openstack server list`` a few times until each of them
-is shown in the state ACTIVE, which means that they're not just built
-but already running on the local hypervisor.
-
-These operations had the side effect of creating separate "port"
-objects, but without giving those ports any easy-to-read names. It'll
-be easier to deal with them later if we can refer to them by name, so
-let's name ``a``'s port ``ap`` and ``b``'s port ``bp``::
-
- $ openstack port set --name ap $(openstack port list --server a -f value -c ID)
- $ openstack port set --name bp $(openstack port list --server b -f value -c ID)
-
-We'll need to refer to these ports' MAC addresses a few times, so
-let's put them in variables::
-
- $ AP_MAC=$(openstack port show -f value -c mac_address ap)
- $ BP_MAC=$(openstack port show -f value -c mac_address bp)
-
-At this point you can log into the consoles of the VMs if you like.
-You can do that from the OpenStack web interface or get a direct URL
-to paste into a web browser using a command like::
-
- $ openstack console url show -f yaml a
-
-(The option ``-f yaml`` keeps the URL in the output from being broken
-into noncontiguous pieces on a 80-column console.)
-
-The VMs don't have many tools in them but ``ping`` and ``ssh`` from
-one to the other should work fine. The VMs do not have any external
-network access or DNS configuration.
-
-Let's chase down what's changed in OVN. Start with the NB DB at the
-top of the system. It's clear that our logical switch now has the two
-logical ports attached to it::
-
- $ ovn-nbctl show | abbrev
- ...
- switch 3eb263 (neutron-5b6baf) (aka n1)
- port c29d41 (aka bp)
- addresses: ["fa:16:3e:99:7a:17 10.1.1.6"]
- port 820c08 (aka ap)
- addresses: ["fa:16:3e:a9:4c:c7 10.1.1.5"]
- ...
-
-We can get some more details on each of these by looking at their NB
-DB records in the Logical_Switch_Port table. Each port has addressing
-information, port security enabled, and a pointer to DHCP
-configuration (which we'll look at much later in `DHCP`_)::
-
- $ ovn-nbctl list logical_switch_port ap bp | abbrev
- _uuid : ef17e5
- addresses : ["fa:16:3e:a9:4c:c7 10.1.1.5"]
- dhcpv4_options : 165974
- dhcpv6_options : []
- dynamic_addresses : []
- enabled : true
- external_ids : {"neutron:port_name"=ap}
- name : "820c08"
- options : {}
- parent_name : []
- port_security : ["fa:16:3e:a9:4c:c7 10.1.1.5"]
- tag : []
- tag_request : []
- type : ""
- up : true
-
- _uuid : e8af12
- addresses : ["fa:16:3e:99:7a:17 10.1.1.6"]
- dhcpv4_options : 165974
- dhcpv6_options : []
- dynamic_addresses : []
- enabled : true
- external_ids : {"neutron:port_name"=bp}
- name : "c29d41"
- options : {}
- parent_name : []
- port_security : ["fa:16:3e:99:7a:17 10.1.1.6"]
- tag : []
- tag_request : []
- type : ""
- up : true
-
-Now that the logical switch is less pathological, it's worth taking
-another look at the SB DB logical flow table. Try a command like
-this::
-
- $ ovn-sbctl lflow-list n1 | abbrev | less -S
-
-and then glance through the flows. Packets that egress a VM into the
-logical switch travel through the flow table's ingress pipeline
-starting from table 0. At each table, the switch finds the
-highest-priority logical flow that matches and executes its actions,
-or if there's no matching flow then the packet is dropped. The
-``ovn-sb``\(5) manpage gives all the details, but with a little
-thought it's possible to guess a lot without reading the manpage. For
-example, consider the flows in ingress pipeline table 0, which are the
-first flows encountered by a packet traversing the switch::
-
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;)
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;)
- table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "820c08" && eth.src == {fa:16:3e:a9:4c:c7}), action=(next;)
- table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "c29d41" && eth.src == {fa:16:3e:99:7a:17}), action=(next;)
-
-The first two flows, with priority 100, immediately drop two kinds of
-invalid packets: those with a multicast or broadcast Ethernet source
-address (since multicast is only for packet destinations) and those
-with a VLAN tag (because OVN doesn't yet support VLAN tags inside
-logical networks). The next two flows implement L2 port security:
-they advance to the next table for packets with the correct Ethernet
-source addresses for their ingress ports. A packet that does not
-match any flow is implicitly dropped, so there's no need for flows to
-deal with mismatches.
-
-The logical flow table includes many other flows, some of which we
-will look at later. For now, it's most worth looking at ingress table
-13::
-
- table=13(ls_in_l2_lkup ), priority=100 , match=(eth.mcast), action=(outport = "_MC_flood"; output;)
- table=13(ls_in_l2_lkup ), priority=50 , match=(eth.dst == fa:16:3e:99:7a:17), action=(outport = "c29d41"; output;)
- table=13(ls_in_l2_lkup ), priority=50 , match=(eth.dst == fa:16:3e:a9:4c:c7), action=(outport = "820c08"; output;)
-
-The first flow in table 13 checks whether the packet is an Ethernet
-multicast or broadcast and, if so, outputs it to a special port that
-egresses to every logical port (other than the ingress port).
-Otherwise the packet is output to the port corresponding to its
-Ethernet destination address. Packets addressed to any other Ethernet
-destination are implicitly dropped.
-
-(It's common for an OVN logical switch to know all the MAC addresses
-supported by its logical ports, like this one. That's why there's no
-logic here for MAC learning or flooding packets to unknown MAC
-addresses. OVN does support unknown MAC handling but that's not in
-play in our example.)
-
-.. note::
-
- If you're interested in the details for the multicast group, you can
- run a command like the following and then look at the row for the
- correct datapath::
-
- $ ovn-sbctl find multicast_group name=_MC_flood | abbrev
-
-Now if you want to look at the OpenFlow flows, you can actually see
-them. For example, here's the beginning of the output that lists the
-first four logical flows, which we already looked at above, and their
-corresponding OpenFlow flows. If you want to know more about the
-syntax, the ``ovs-fields``\(7) manpage explains OpenFlow matches and
-``ovs-ofctl``\(8) explains OpenFlow actions::
-
- $ sudo ovn-sbctl --ovs lflow-list n1 | abbrev
- Datapath: "neutron-5b6baf" aka "n1" (a8a758) Pipeline: ingress
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(eth.src[40]), action=(drop;)
- table=8 metadata=0x4,dl_src=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop
- table=0 (ls_in_port_sec_l2 ), priority=100 , match=(vlan.present), action=(drop;)
- table=8 metadata=0x4,vlan_tci=0x1000/0x1000 actions=drop
- table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "820c08" && eth.src == {fa:16:3e:a9:4c:c7}), action=(next;)
- table=8 reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7 actions=resubmit(,9)
- table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "c29d41" && eth.src == {fa:16:3e:99:7a:17}), action=(next;)
- table=8 reg14=0x2,metadata=0x4,dl_src=fa:16:3e:99:7a:17 actions=resubmit(,9)
- ...
-
-Logical Tracing
-+++++++++++++++
-
-Let's go a level deeper. So far, everything we've done has been
-fairly general. We can also look at something more specific: the path
-that a particular packet would take through OVN, logically, and Open
-vSwitch, physically.
-
-Let's use OVN's ovn-trace utility to see what happens to packets from
-a logical point of view. The ``ovn-trace``\(8) manpage has a lot of
-detail on how to do that, but let's just start by building up from a
-simple example. You can start with a command that just specifies the
-logical datapath, an input port, and nothing else; unspecified fields
-default to all-zeros. This doesn't do much::
-
- $ ovn-trace n1 'inport == "ap"'
- ...
- ingress(dp="n1", inport="ap")
- -----------------------------
- 0. ls_in_port_sec_l2: no match (implicit drop)
-
-We see that the packet was dropped in logical table 0,
-"ls_in_port_sec_l2", the L2 port security stage (as we discussed
-earlier). That's because we didn't use the right Ethernet source
-address for ``a``. Let's see what happens if we do::
-
- $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC
- ...
- ingress(dp="n1", inport="ap")
- -----------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src == {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
- next;
- 13. ls_in_l2_lkup: no match (implicit drop)
-
-Now the packet passes through L2 port security and skips through
-several other tables until it gets dropped in the L2 lookup stage
-(because the destination is unknown). Let's add the Ethernet
-destination for ``b``::
-
- $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$BP_MAC
- ...
- ingress(dp="n1", inport="ap")
- -----------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src == {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:99:7a:17, priority 50, uuid 57a4c46f
- outport = "bp";
- output;
-
- egress(dp="n1", inport="ap", outport="bp")
- ------------------------------------------
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "bp" && eth.dst == {fa:16:3e:99:7a:17}, priority 50, uuid 8aa6426d
- output;
- /* output to "bp", type "" */
-
-You can see that in this case the packet gets properly switched from
-``a`` to ``b``.
-
-Physical Tracing for Hypothetical Packets
-+++++++++++++++++++++++++++++++++++++++++
-
-ovn-trace showed us how a hypothetical packet would travel through the
-system in a logical fashion, that is, without regard to how VMs are
-distributed across the physical network. This is a convenient
-representation for understanding how OVN is **supposed** to work
-abstractly, but sometimes we might want to know more about how it
-actually works in the real systems where it is running. For this, we
-can use the tracing tool that Open vSwitch provides, which traces
-a hypothetical packet through the OpenFlow tables.
-
-We can actually get two levels of detail. Let's start with the
-version that's easier to interpret, by physically tracing a packet
-that looks like the one we logically traced before. One obstacle is
-that we need to know the OpenFlow port number of the input port. One
-way to do that is to look for a port whose "attached-mac" is the one
-we expect and print its ofport number::
-
- $ AP_PORT=$(ovs-vsctl --bare --columns=ofport find interface external-ids:attached-mac=\"$AP_MAC\")
- $ echo $AP_PORT
- 3
-
-(You could also just do a plain ``ovs-vsctl list interface`` and then
-look through for the right row and pick its ``ofport`` value.)
-
-Now we can feed this input port number into ``ovs-appctl
-ofproto/trace`` along with the correct Ethernet source and
-destination addresses and get a physical trace::
-
- $ sudo ovs-appctl ofproto/trace br-int in_port=$AP_PORT,dl_src=$AP_MAC,dl_dst=$BP_MAC
- Flow: in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=fa:16:3e:99:7a:17,dl_type=0x0000
-
- bridge("br-int")
- ----------------
- 0. in_port=3, priority 100
- set_field:0x8->reg13
- set_field:0x9->reg11
- set_field:0xa->reg12
- set_field:0x4->metadata
- set_field:0x1->reg14
- resubmit(,8)
- 8. reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7, priority 50, cookie 0x6dcc418a
- resubmit(,9)
- 9. metadata=0x4, priority 0, cookie 0x8fe8689e
- resubmit(,10)
- 10. metadata=0x4, priority 0, cookie 0x719549d1
- resubmit(,11)
- 11. metadata=0x4, priority 0, cookie 0x39c99e6f
- resubmit(,12)
- 12. metadata=0x4, priority 0, cookie 0x838152a3
- resubmit(,13)
- 13. metadata=0x4, priority 0, cookie 0x918259e3
- resubmit(,14)
- 14. metadata=0x4, priority 0, cookie 0xcad14db2
- resubmit(,15)
- 15. metadata=0x4, priority 0, cookie 0x7834d912
- resubmit(,16)
- 16. metadata=0x4, priority 0, cookie 0x87745210
- resubmit(,17)
- 17. metadata=0x4, priority 0, cookie 0x34951929
- resubmit(,18)
- 18. metadata=0x4, priority 0, cookie 0xd7a8c9fb
- resubmit(,19)
- 19. metadata=0x4, priority 0, cookie 0xd02e9578
- resubmit(,20)
- 20. metadata=0x4, priority 0, cookie 0x42d35507
- resubmit(,21)
- 21. metadata=0x4,dl_dst=fa:16:3e:99:7a:17, priority 50, cookie 0x57a4c46f
- set_field:0x2->reg15
- resubmit(,32)
- 32. priority 0
- resubmit(,33)
- 33. reg15=0x2,metadata=0x4, priority 100
- set_field:0xb->reg13
- set_field:0x9->reg11
- set_field:0xa->reg12
- resubmit(,34)
- 34. priority 0
- set_field:0->reg0
- set_field:0->reg1
- set_field:0->reg2
- set_field:0->reg3
- set_field:0->reg4
- set_field:0->reg5
- set_field:0->reg6
- set_field:0->reg7
- set_field:0->reg8
- set_field:0->reg9
- resubmit(,40)
- 40. metadata=0x4, priority 0, cookie 0xde9f3899
- resubmit(,41)
- 41. metadata=0x4, priority 0, cookie 0x74074eff
- resubmit(,42)
- 42. metadata=0x4, priority 0, cookie 0x7789c8b1
- resubmit(,43)
- 43. metadata=0x4, priority 0, cookie 0xa6b002c0
- resubmit(,44)
- 44. metadata=0x4, priority 0, cookie 0xaeab2b45
- resubmit(,45)
- 45. metadata=0x4, priority 0, cookie 0x290cc4d4
- resubmit(,46)
- 46. metadata=0x4, priority 0, cookie 0xa3223b88
- resubmit(,47)
- 47. metadata=0x4, priority 0, cookie 0x7ac2132e
- resubmit(,48)
- 48. reg15=0x2,metadata=0x4,dl_dst=fa:16:3e:99:7a:17, priority 50, cookie 0x8aa6426d
- resubmit(,64)
- 64. priority 0
- resubmit(,65)
- 65. reg15=0x2,metadata=0x4, priority 100
- output:4
-
- Final flow: reg11=0x9,reg12=0xa,reg13=0xb,reg14=0x1,reg15=0x2,metadata=0x4,in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=fa:16:3e:99:7a:17,dl_type=0x0000
- Megaflow: recirc_id=0,ct_state=-new-est-rel-rpl-inv-trk,ct_label=0/0x1,in_port=3,vlan_tci=0x0000/0x1000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=fa:16:3e:99:7a:17,dl_type=0x0000
- Datapath actions: 4
-
-There's a lot there, which you can read through if you like, but the
-important part is::
-
- 65. reg15=0x2,metadata=0x4, priority 100
- output:4
-
-which means that the packet is ultimately being output to OpenFlow
-port 4. That's port ``b``, which you can confirm with::
-
- $ sudo ovs-vsctl find interface ofport=4
- _uuid : 840a5aca-ea8d-4c16-a11b-a94e0f408091
- admin_state : up
- bfd : {}
- bfd_status : {}
- cfm_fault : []
- cfm_fault_status : []
- cfm_flap_count : []
- cfm_health : []
- cfm_mpid : []
- cfm_remote_mpids : []
- cfm_remote_opstate : []
- duplex : full
- error : []
- external_ids : {attached-mac="fa:16:3e:99:7a:17", iface-id="c29d4120-20a4-4c44-bd83-8d91f5f447fd", iface-status=active, vm-id="2db969ca-ca2a-4d9a-b49e-f287d39c5645"}
- ifindex : 9
- ingress_policing_burst: 0
- ingress_policing_rate: 0
- lacp_current : []
- link_resets : 1
- link_speed : 10000000
- link_state : up
- lldp : {}
- mac : []
- mac_in_use : "fe:16:3e:99:7a:17"
- mtu : 1500
- mtu_request : []
- name : "tapc29d4120-20"
- ofport : 4
- ofport_request : []
- options : {}
- other_config : {}
- statistics : {collisions=0, rx_bytes=4254, rx_crc_err=0, rx_dropped=0, rx_errors=0, rx_frame_err=0, rx_over_err=0, rx_packets=39, tx_bytes=4188, tx_dropped=0, tx_errors=0, tx_packets=39}
- status : {driver_name=tun, driver_version="1.6", firmware_version=""}
- type : ""
-
-or::
-
- $ BP_PORT=$(ovs-vsctl --bare --columns=ofport find interface external-ids:attached-mac=\"$BP_MAC\")
- $ echo $BP_PORT
- 4
-
-Physical Tracing for Real Packets
-+++++++++++++++++++++++++++++++++
-
-In the previous sections we traced a hypothetical L2 packet, one
-that's honestly not very realistic: we didn't even supply an Ethernet
-type, so it defaulted to zero, which isn't anything one would see on a
-real network. We could refine our packet so that it becomes a more
-realistic TCP or UDP or ICMP, etc. packet, but let's try a different
-approach: working from a real packet.
-
-Pull up a console for VM ``a`` and start ``ping 10.1.1.6``, then leave
-it running for the rest of our experiment.
-
-Now go back to your DevStack session and run::
-
- $ sudo watch ovs-dpctl dump-flows
-
-We're working with a new program. ovn-dpctl is an interface to Open
-vSwitch datapaths, in this case to the Linux kernel datapath. Its
-``dump-flows`` command displays the contents of the in-kernel flow
-cache, and by running it under the ``watch`` program we see a new
-snapshot of the flow table every 2 seconds.
-
-Look through the output for a flow that begins with ``recirc_id(0)``
-and matches the Ethernet source address for ``a``. There is one flow
-per line, but the lines are very long, so it's easier to read if you
-make the window very wide. This flow's packet counter should be
-increasing at a rate of 1 packet per second. It looks something like
-this::
-
- recirc_id(0),in_port(3),eth(src=fa:16:3e:f5:2a:90),eth_type(0x0800),ipv4(src=10.1.1.5,frag=no), packets:388, bytes:38024, used:0.977s, actions:ct(zone=8),recirc(0x18)
-
-We can hand the first part of this (everything up to the first space)
-to ``ofproto/trace``, and it will tell us what happens::
-
- $ sudo ovs-appctl ofproto/trace 'recirc_id(0),in_port(3),eth(src=fa:16:3e:a9:4c:c7),eth_type(0x0800),ipv4(src=10.1.1.5,dst=10.1.0.0/255.255.0.0,frag=no)'
- Flow: ip,in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=00:00:00:00:00:00,nw_src=10.1.1.5,nw_dst=10.1.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
-
- bridge("br-int")
- ----------------
- 0. in_port=3, priority 100
- set_field:0x8->reg13
- set_field:0x9->reg11
- set_field:0xa->reg12
- set_field:0x4->metadata
- set_field:0x1->reg14
- resubmit(,8)
- 8. reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7, priority 50, cookie 0x6dcc418a
- resubmit(,9)
- 9. ip,reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7,nw_src=10.1.1.5, priority 90, cookie 0x343af48c
- resubmit(,10)
- 10. metadata=0x4, priority 0, cookie 0x719549d1
- resubmit(,11)
- 11. ip,metadata=0x4, priority 100, cookie 0x46c089e6
- load:0x1->NXM_NX_XXREG0[96]
- resubmit(,12)
- 12. metadata=0x4, priority 0, cookie 0x838152a3
- resubmit(,13)
- 13. ip,reg0=0x1/0x1,metadata=0x4, priority 100, cookie 0xd1941634
- ct(table=22,zone=NXM_NX_REG13[0..15])
- drop
-
- Final flow: ip,reg0=0x1,reg11=0x9,reg12=0xa,reg13=0x8,reg14=0x1,metadata=0x4,in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=00:00:00:00:00:00,nw_src=10.1.1.5,nw_dst=10.1.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
- Megaflow: recirc_id=0,ip,in_port=3,vlan_tci=0x0000/0x1000,dl_src=fa:16:3e:a9:4c:c7,nw_src=10.1.1.5,nw_dst=10.1.0.0/16,nw_frag=no
- Datapath actions: ct(zone=8),recirc(0xb)
-
-.. note::
- Be careful cutting and pasting ``ovs-dpctl dump-flows`` output into
- ``ofproto/trace`` because the latter has terrible error reporting.
- If you add an extra line break, etc., it will likely give you a
- useless error message.
-
-There's no ``output`` action in the output, but there are ``ct`` and
-``recirc`` actions (which you can see in the ``Datapath actions`` at
-the end). The ``ct`` action tells the kernel to pass the packet
-through the kernel connection tracking for firewalling purposes and
-the ``recirc`` says to go back to the flow cache for another pass
-based on the firewall results. The ``0xb`` value inside the
-``recirc`` gives us a hint to look at the kernel flows for a cached
-flow with ``recirc_id(0xb)``. Indeed, there is one::
-
- recirc_id(0xb),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=10.1.1.4/255.255.255.252,frag=no), packets:171, bytes:16758, used:0.271s, actions:ct(zone=11),recirc(0xc)
-
-We can then repeat our command with the match part of this kernel
-flow::
-
- $ sudo ovs-appctl ofproto/trace 'recirc_id(0xb),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=10.1.1.4/255.255.255.252,frag=no)'
- ...
- Datapath actions: ct(zone=11),recirc(0xc)
-
-In other words, the flow passes through the connection tracker a
-second time. The first time was for ``a``'s outgoing firewall; this
-second time is for ``b``'s incoming firewall. Again, we continue
-tracing with ``recirc_id(0xc)``::
-
- $ sudo ovs-appctl ofproto/trace 'recirc_id(0xc),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=10.1.1.6,proto=1,frag=no)'
- ...
- Datapath actions: 4
-
-It was took multiple hops, but we finally came to the end of the line
-where the packet was output to ``b`` after passing through both
-firewalls. The port number here is a datapath port number, which is
-usually different from an OpenFlow port number. To check that it is
-``b``'s port, we first list the datapath ports to get the name
-corresponding to the port number::
-
- $ sudo ovs-dpctl show
- system@ovs-system:
- lookups: hit:1994 missed:56 lost:0
- flows: 6
- masks: hit:2340 total:4 hit/pkt:1.14
- port 0: ovs-system (internal)
- port 1: br-int (internal)
- port 2: br-ex (internal)
- port 3: tap820c0888-13
- port 4: tapc29d4120-20
-
-and then confirm that this is the port we think it is with a command
-like this::
-
- $ ovs-vsctl --columns=external-ids list interface tapc29d4120-20
- external_ids : {attached-mac="fa:16:3e:99:7a:17", iface-id="c29d4120-20a4-4c44-bd83-8d91f5f447fd", iface-status=active, vm-id="2db969ca-ca2a-4d9a-b49e-f287d39c5645"}
-
-Finally, we can relate the OpenFlow flows from our traces back to OVN
-logical flows. For individual flows, cut and paste a "cookie" value
-from ``ofproto/trace`` output into ``ovn-sbctl lflow-list``, e.g.::
-
- $ ovn-sbctl lflow-list 0x6dcc418a|abbrev
- Datapath: "neutron-5b6baf" aka "n1" (a8a758) Pipeline: ingress
- table=0 (ls_in_port_sec_l2 ), priority=50 , match=(inport == "820c08" && eth.src == {fa:16:3e:a9:4c:c7}), action=(next;)
-
-Or, you can pipe ``ofproto/trace`` output through ``ovn-detrace`` to
-annotate every flow::
-
- $ sudo ovs-appctl ofproto/trace 'recirc_id(0xc),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=10.1.1.6,proto=1,frag=no)' | ovn-detrace
- ...
-
-Routing
--------
-
-Previously we set up a pair of VMs ``a`` and ``b`` on a network ``n1``
-and demonstrated how packets make their way between them. In this
-step, we'll set up a second network ``n2`` with a new VM ``c``,
-connect a router ``r`` to both networks, and demonstrate how routing
-works in OVN.
-
-There's nothing really new for the network and the VM so let's just go
-ahead and create them::
-
- $ openstack network create --project admin --provider-network-type geneve n2
- $ openstack subnet create --subnet-range 10.1.2.0/24 --network n2 n2subnet
- $ openstack server create --nic net-id=n2,v4-fixed-ip=10.1.2.7 --flavor m1.nano --image $IMAGE_ID --key-name demo c
- $ openstack port set --name cp $(openstack port list --server c -f value -c ID)
- $ CP_MAC=$(openstack port show -f value -c mac_address cp)
-
-The new network ``n2`` is not yet connected to ``n1`` in any way. You
-can try tracing a broadcast packet from ``a`` to see, for example,
-that it doesn't make it to ``c``::
-
- $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$CP_MAC
- ...
-
-Now create an OpenStack router and connect it to ``n1`` and ``n2``::
-
- $ openstack router create r
- $ openstack router add subnet r n1subnet
- $ openstack router add subnet r n2subnet
-
-Now ``a``, ``b``, and ``c`` should all be able to reach other. You
-can get some verification that routing is taking place by running you
-``ping`` between ``c`` and one of the other VMs: the reported TTL
-should be one less than between ``a`` and ``b`` (63 instead of 64).
-
-Observe via ``ovn-nbctl`` the new OVN logical switch and router and
-then ports that connect them together::
-
- $ ovn-nbctl show|abbrev
- ...
- switch f51234 (neutron-332346) (aka n2)
- port 82b983
- type: router
- router-port: lrp-82b983
- port 2e585f (aka cp)
- addresses: ["fa:16:3e:89:f2:36 10.1.2.7"]
- switch 3eb263 (neutron-5b6baf) (aka n1)
- port c29d41 (aka bp)
- addresses: ["fa:16:3e:99:7a:17 10.1.1.6"]
- port 820c08 (aka ap)
- addresses: ["fa:16:3e:a9:4c:c7 10.1.1.5"]
- port 17d870
- type: router
- router-port: lrp-17d870
- ...
- router dde06c (neutron-f88ebc) (aka r)
- port lrp-82b983
- mac: "fa:16:3e:19:9f:46"
- networks: ["10.1.2.1/24"]
- port lrp-17d870
- mac: "fa:16:3e:f6:e2:8f"
- networks: ["10.1.1.1/24"]
-
-We have not yet looked at the logical flows for an OVN logical router.
-You might find it of interest to look at them on your own::
-
- $ ovn-sbctl lflow-list r | abbrev | less -S
- ...
-
-Let's grab the ``n1subnet`` router porter MAC address to simplify
-later commands::
-
- $ N1SUBNET_MAC=$(ovn-nbctl --bare --columns=mac find logical_router_port networks=10.1.1.1/24)
-
-Let's see what happens at the logical flow level for an ICMP packet
-from ``a`` to ``c``. This generates a long trace but an interesting
-one, so we'll look at it bit by bit. The first three stanzas in the
-output show the packet's ingress into ``n1`` and processing through
-the firewall on that side (via the "ct_next" connection-tracking
-action), and then the selection of the port that leads to router ``r``
-as the output port::
-
- $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$N1SUBNET_MAC' && ip4.src == 10.1.1.5 && ip4.dst == 10.1.2.7 && ip.ttl == 64 && icmp4.type == 8'
- ...
- ingress(dp="n1", inport="ap")
- -----------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src == {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
- next;
- 1. ls_in_port_sec_ip (ovn-northd.c:2364): inport == "ap" && eth.src == fa:16:3e:a9:4c:c7 && ip4.src == {10.1.1.5}, priority 90, uuid 343af48c
- next;
- 3. ls_in_pre_acl (ovn-northd.c:2646): ip, priority 100, uuid 46c089e6
- reg0[0] = 1;
- next;
- 5. ls_in_pre_stateful (ovn-northd.c:2764): reg0[0] == 1, priority 100, uuid d1941634
- ct_next;
-
- ct_next(ct_state=est|trk /* default (use --ct to customize) */)
- ---------------------------------------------------------------
- 6. ls_in_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (inport == "ap" && ip4), priority 2002, uuid a12b39f0
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:f6:e2:8f, priority 50, uuid c43ead31
- outport = "17d870";
- output;
-
- egress(dp="n1", inport="ap", outport="17d870")
- ----------------------------------------------
- 1. ls_out_pre_acl (ovn-northd.c:2626): ip && outport == "17d870", priority 110, uuid 60395450
- next;
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "17d870", priority 50, uuid 91b5cab0
- output;
- /* output to "17d870", type "patch" */
-
-The next two stanzas represent processing through logical router
-``r``. The processing in table 5 is the core of the routing
-implementation: it recognizes that the packet is destined for an
-attached subnet, decrements the TTL and updates the Ethernet source
-address. Table 6 then selects the Ethernet destination address based
-on the IP destination. The packet then passes to switch ``n2`` via an
-OVN "logical patch port"::
-
- ingress(dp="r", inport="lrp-17d870")
- ------------------------------------
- 0. lr_in_admission (ovn-northd.c:4071): eth.dst == fa:16:3e:f6:e2:8f && inport == "lrp-17d870", priority 50, uuid fa5270b0
- next;
- 5. lr_in_ip_routing (ovn-northd.c:3782): ip4.dst == 10.1.2.0/24, priority 49, uuid 5f9d469f
- ip.ttl--;
- reg0 = ip4.dst;
- reg1 = 10.1.2.1;
- eth.src = fa:16:3e:19:9f:46;
- outport = "lrp-82b983";
- flags.loopback = 1;
- next;
- 6. lr_in_arp_resolve (ovn-northd.c:5088): outport == "lrp-82b983" && reg0 == 10.1.2.7, priority 100, uuid 03d506d3
- eth.dst = fa:16:3e:89:f2:36;
- next;
- 8. lr_in_arp_request (ovn-northd.c:5260): 1, priority 0, uuid 6dacdd82
- output;
-
- egress(dp="r", inport="lrp-17d870", outport="lrp-82b983")
- ---------------------------------------------------------
- 3. lr_out_delivery (ovn-northd.c:5288): outport == "lrp-82b983", priority 100, uuid 00bea4f2
- output;
- /* output to "lrp-82b983", type "patch" */
-
-Finally the logical switch for ``n2`` runs through the same logic as
-``n1`` and the packet is delivered to VM ``c``::
-
- ingress(dp="n2", inport="82b983")
- ---------------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "82b983", priority 50, uuid 9a789e06
- next;
- 3. ls_in_pre_acl (ovn-northd.c:2624): ip && inport == "82b983", priority 110, uuid ab52f21a
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:89:f2:36, priority 50, uuid dcafb3e9
- outport = "cp";
- output;
-
- egress(dp="n2", inport="82b983", outport="cp")
- ----------------------------------------------
- 1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid cd9cfa74
- reg0[0] = 1;
- next;
- 2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority 100, uuid 9e8e22c5
- ct_next;
-
- ct_next(ct_state=est|trk /* default (use --ct to customize) */)
- ---------------------------------------------------------------
- 4. ls_out_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (outport == "cp" && ip4 && ip4.src == $as_ip4_0fc1b6cf_f925_49e6_8f00_6dd13beca9dc), priority 2002, uuid a746fa0d
- next;
- 7. ls_out_port_sec_ip (ovn-northd.c:2364): outport == "cp" && eth.dst == fa:16:3e:89:f2:36 && ip4.dst == {255.255.255.255, 224.0.0.0/4, 10.1.2.7}, priority 90, uuid 4d9862b5
- next;
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "cp" && eth.dst == {fa:16:3e:89:f2:36}, priority 50, uuid 0242cdc3
- output;
- /* output to "cp", type "" */
-
-Physical Tracing
-~~~~~~~~~~~~~~~~
-
-It's possible to use ``ofproto/trace``, just as before, to trace a
-packet through OpenFlow tables, either for a hypothetical packet or
-one that you get from a real test case using ``ovs-dpctl``. The
-process is just the same as before and the output is almost the same,
-too. Using a router doesn't actually introduce any interesting new
-wrinkles, so we'll skip over this for this case and for the remainder
-of the tutorial, but you can follow the steps on your own if you like.
-
-Adding a Gateway
-----------------
-
-The VMs that we've created can access each other but they are isolated
-from the physical world. In OpenStack, the dominant way to connect a
-VM to external networks is by creating what is called a "floating IP
-address", which uses network address translation to connect an
-external address to an internal one.
-
-DevStack created a pair of networks named "private" and "public". To
-use a floating IP address from a VM, we first add a port to the VM
-with an IP address from the "private" network, then we create a
-floating IP address on the "public" network, then we associate the
-port with the floating IP address.
-
-Let's add a new VM ``d`` with a floating IP::
-
- $ openstack server create --nic net-id=private --flavor m1.nano --image $IMAGE_ID --key-name demo d
- $ openstack port set --name dp $(openstack port list --server d -f value -c ID)
- $ DP_MAC=$(openstack port show -f value -c mac_address dp)
- $ openstack floating ip create --floating-ip-address 172.24.4.8 public
- $ openstack server add floating ip d 172.24.4.8
-
-(We specified a particular floating IP address to make the examples
-easier to follow, but without that OpenStack will automatically
-allocate one.)
-
-It's also necessary to configure the "public" network because DevStack
-does not do it automatically::
-
- $ sudo ip link set br-ex up
- $ sudo ip route add 172.24.4.0/24 dev br-ex
- $ sudo ip addr add 172.24.4.1/24 dev br-ex
-
-Now you should be able to "ping" VM ``d`` from the OpenStack host::
-
- $ ping 172.24.4.8
- PING 172.24.4.8 (172.24.4.8) 56(84) bytes of data.
- 64 bytes from 172.24.4.8: icmp_seq=1 ttl=63 time=56.0 ms
- 64 bytes from 172.24.4.8: icmp_seq=2 ttl=63 time=1.44 ms
- 64 bytes from 172.24.4.8: icmp_seq=3 ttl=63 time=1.04 ms
- 64 bytes from 172.24.4.8: icmp_seq=4 ttl=63 time=0.403 ms
- ^C
- --- 172.24.4.8 ping statistics ---
- 4 packets transmitted, 4 received, 0% packet loss, time 3003ms
- rtt min/avg/max/mdev = 0.403/14.731/56.028/23.845 ms
-
-You can also SSH in with the key that we created during setup::
-
- $ ssh -i ~/id_rsa_demo cirros@172.24.4.8
-
-Let's dive in and see how this gets implemented in OVN. First, the
-relevant parts of the NB DB for the "public" and "private" networks
-and the router between them::
-
- $ ovn-nbctl show | abbrev
- switch 2579f4 (neutron-d1ac28) (aka public)
- port provnet-d1ac28
- type: localnet
- addresses: ["unknown"]
- port ae9b52
- type: router
- router-port: lrp-ae9b52
- switch 5b3d5f (neutron-c02c4d) (aka private)
- port b256dd
- type: router
- router-port: lrp-b256dd
- port f264e7
- type: router
- router-port: lrp-f264e7
- port cae25b (aka dp)
- addresses: ["fa:16:3e:c1:f5:a2 10.0.0.6 fdb0:5860:4ba8:0:f816:3eff:fec1:f5a2"]
- ...
- router c59ad2 (neutron-9b057f) (aka router1)
- port lrp-ae9b52
- mac: "fa:16:3e:b2:d2:67"
- networks: ["172.24.4.9/24", "2001:db8::b/64"]
- port lrp-b256dd
- mac: "fa:16:3e:35:33:db"
- networks: ["fdb0:5860:4ba8::1/64"]
- port lrp-f264e7
- mac: "fa:16:3e:fc:c8:da"
- networks: ["10.0.0.1/26"]
- nat 788c6d
- external ip: "172.24.4.8"
- logical ip: "10.0.0.6"
- type: "dnat_and_snat"
- nat 80914c
- external ip: "172.24.4.9"
- logical ip: "10.0.0.0/26"
- type: "snat"
- ...
-
-What we see is:
-
-* VM ``d`` is on the "private" switch under its private IP address
- 10.0.0.8. The "private" switch is connected to "router1" via two
- router ports (one for IPv4, one for IPv6).
-
-* The "public" switch is connected to "router1" and to the physical
- network via a "localnet" port.
-
-* "router1" is in the middle between "private" and "public". In
- addition to the router ports that connect to these switches, it has
- "nat" entries that direct network address translation. The
- translation between floating IP address 172.24.4.8 and private
- address 10.0.0.8 makes perfect sense.
-
-When the NB DB gets translated into logical flows at the southbound
-layer, the "nat" entries get translated into IP matches that then
-invoke "ct_snat" and "ct_dnat" actions. The details are intricate,
-but you can get some of the idea by just looking for relevant flows::
-
- $ ovn-sbctl lflow-list router1 | abbrev | grep nat | grep -E '172.24.4.8|10.0.0.8'
- table=3 (lr_in_unsnat ), priority=100 , match=(ip && ip4.dst == 172.24.4.8 && inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52")), action=(ct_snat;)
- table=3 (lr_in_unsnat ), priority=50 , match=(ip && ip4.dst == 172.24.4.8), action=(reg9[0] = 1; next;)
- table=4 (lr_in_dnat ), priority=100 , match=(ip && ip4.dst == 172.24.4.8 && inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52")), action=(ct_dnat(10.0.0.6);)
- table=4 (lr_in_dnat ), priority=50 , match=(ip && ip4.dst == 172.24.4.8), action=(reg9[0] = 1; next;)
- table=1 (lr_out_snat ), priority=33 , match=(ip && ip4.src == 10.0.0.6 && outport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52")), action=(ct_snat(172.24.4.8);)
-
-Let's take a look at how a packet passes through this whole gauntlet.
-The first two stanzas just show the packet traveling through the
-"public" network and being forwarded to the "router1" network::
-
- $ ovn-trace public 'inport == "provnet-d1ac2896-18a7-4bca-8f46-b21e2370e5b1" && eth.src == 00:01:02:03:04:05 && eth.dst == fa:16:3e:b2:d2:67 && ip4.src == 172.24.4.1 && ip4.dst == 172.24.4.8 && ip.ttl == 64 && icmp4.type==8'
- ...
- ingress(dp="public", inport="provnet-d1ac28")
- ---------------------------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "provnet-d1ac28", priority 50, uuid 8d86fb06
- next;
- 10. ls_in_arp_rsp (ovn-northd.c:3266): inport == "provnet-d1ac28", priority 100, uuid 21313eff
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3571): eth.dst == fa:16:3e:b2:d2:67 && is_chassis_resident("cr-lrp-ae9b52"), priority 50, uuid 7f28f51f
- outport = "ae9b52";
- output;
-
- egress(dp="public", inport="provnet-d1ac28", outport="ae9b52")
- --------------------------------------------------------------
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "ae9b52", priority 50, uuid 72fea396
- output;
- /* output to "ae9b52", type "patch" */
-
-In "router1", first the ``ct_snat`` action without an argument
-attempts to "un-SNAT" the packet. ovn-trace treats this as a no-op,
-because it doesn't have any state for tracking connections. As an
-alternative, it invokes ``ct_dnat(10.0.0.8)`` to NAT the destination
-IP::
-
- ingress(dp="router1", inport="lrp-ae9b52")
- ------------------------------------------
- 0. lr_in_admission (ovn-northd.c:4071): eth.dst == fa:16:3e:b2:d2:67 && inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52"), priority 50, uuid 8c6945c2
- next;
- 3. lr_in_unsnat (ovn-northd.c:4591): ip && ip4.dst == 172.24.4.8 && inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52"), priority 100, uuid e922f541
- ct_snat;
-
- ct_snat /* assuming no un-snat entry, so no change */
- -----------------------------------------------------
- 4. lr_in_dnat (ovn-northd.c:4649): ip && ip4.dst == 172.24.4.8 && inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52"), priority 100, uuid 02f41b79
- ct_dnat(10.0.0.6);
-
-Still in "router1", the routing and output steps transmit the packet
-to the "private" network::
-
- ct_dnat(ip4.dst=10.0.0.6)
- -------------------------
- 5. lr_in_ip_routing (ovn-northd.c:3782): ip4.dst == 10.0.0.0/26, priority 53, uuid 86e005b0
- ip.ttl--;
- reg0 = ip4.dst;
- reg1 = 10.0.0.1;
- eth.src = fa:16:3e:fc:c8:da;
- outport = "lrp-f264e7";
- flags.loopback = 1;
- next;
- 6. lr_in_arp_resolve (ovn-northd.c:5088): outport == "lrp-f264e7" && reg0 == 10.0.0.6, priority 100, uuid 2963d67c
- eth.dst = fa:16:3e:c1:f5:a2;
- next;
- 8. lr_in_arp_request (ovn-northd.c:5260): 1, priority 0, uuid eea419b7
- output;
-
- egress(dp="router1", inport="lrp-ae9b52", outport="lrp-f264e7")
- ---------------------------------------------------------------
- 3. lr_out_delivery (ovn-northd.c:5288): outport == "lrp-f264e7", priority 100, uuid 42dadc23
- output;
- /* output to "lrp-f264e7", type "patch" */
-
-In the "private" network, the packet passes through VM ``d``'s
-firewall and is output to ``d``::
-
- ingress(dp="private", inport="f264e7")
- --------------------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "f264e7", priority 50, uuid 5b721214
- next;
- 3. ls_in_pre_acl (ovn-northd.c:2624): ip && inport == "f264e7", priority 110, uuid 5bdc3209
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:c1:f5:a2, priority 50, uuid 7957f80f
- outport = "dp";
- output;
-
- egress(dp="private", inport="f264e7", outport="dp")
- ---------------------------------------------------
- 1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid 4981c79d
- reg0[0] = 1;
- next;
- 2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority 100, uuid 247e02eb
- ct_next;
-
- ct_next(ct_state=est|trk /* default (use --ct to customize) */)
- ---------------------------------------------------------------
- 4. ls_out_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (outport == "dp" && ip4 && ip4.src == 0.0.0.0/0 && icmp4), priority 2002, uuid b860fc9f
- next;
- 7. ls_out_port_sec_ip (ovn-northd.c:2364): outport == "dp" && eth.dst == fa:16:3e:c1:f5:a2 && ip4.dst == {255.255.255.255, 224.0.0.0/4, 10.0.0.6}, priority 90, uuid 15655a98
- next;
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "dp" && eth.dst == {fa:16:3e:c1:f5:a2}, priority 50, uuid 5916f94b
- output;
- /* output to "dp", type "" */
-
-IPv6
-----
-
-OVN supports IPv6 logical routing. Let's try it out.
-
-The first step is to add an IPv6 subnet to networks ``n1`` and ``n2``,
-then attach those subnets to our router ``r``. As usual, though
-OpenStack can assign addresses itself, we use fixed ones to make the
-discussion easier::
-
- $ openstack subnet create --ip-version 6 --subnet-range fc11::/64 --network n1 n1subnet6
- $ openstack subnet create --ip-version 6 --subnet-range fc22::/64 --network n2 n2subnet6
- $ openstack router add subnet r n1subnet6
- $ openstack router add subnet r n2subnet6
-
-Then we add an IPv6 address to each of our VMs::
-
- $ A_PORT_ID=$(openstack port list --server a -f value -c ID)
- $ openstack port set --fixed-ip subnet=n1subnet6,ip-address=fc11::5 $A_PORT_ID
- $ B_PORT_ID=$(openstack port list --server b -f value -c ID)
- $ openstack port set --fixed-ip subnet=n1subnet6,ip-address=fc11::6 $B_PORT_ID
- $ C_PORT_ID=$(openstack port list --server c -f value -c ID)
- $ openstack port set --fixed-ip subnet=n2subnet6,ip-address=fc22::7 $C_PORT_ID
-
-At least for me, the new IPv6 addresses didn't automatically get
-propagated into the VMs. To do it by hand, pull up the console for
-``a`` and run::
-
- $ sudo ip addr add fc11::5/64 dev eth0
- $ sudo ip route add via fc11::1
-
-Then in ``b``::
-
- $ sudo ip addr add fc11::6/64 dev eth0
- $ sudo ip route add via fc11::1
-
-Finally in ``c``::
-
- $ sudo ip addr add fc22::7/64 dev eth0
- $ sudo ip route add via fc22::1
-
-Now you should have working IPv6 routing through router ``r``. The
-relevant parts of the NB DB look like the following. The interesting
-parts are the new ``fc11::`` and ``fc22::`` addresses on the ports in
-``n1`` and ``n2`` and the new IPv6 router ports in ``r``::
-
- $ ovn-nbctl show | abbrev
- ...
- switch f51234 (neutron-332346) (aka n2)
- port 1a8162
- type: router
- router-port: lrp-1a8162
- port 82b983
- type: router
- router-port: lrp-82b983
- port 2e585f (aka cp)
- addresses: ["fa:16:3e:89:f2:36 10.1.2.7 fc22::7"]
- switch 3eb263 (neutron-5b6baf) (aka n1)
- port ad952e
- type: router
- router-port: lrp-ad952e
- port c29d41 (aka bp)
- addresses: ["fa:16:3e:99:7a:17 10.1.1.6 fc11::6"]
- port 820c08 (aka ap)
- addresses: ["fa:16:3e:a9:4c:c7 10.1.1.5 fc11::5"]
- port 17d870
- type: router
- router-port: lrp-17d870
- ...
- router dde06c (neutron-f88ebc) (aka r)
- port lrp-1a8162
- mac: "fa:16:3e:06:de:ad"
- networks: ["fc22::1/64"]
- port lrp-82b983
- mac: "fa:16:3e:19:9f:46"
- networks: ["10.1.2.1/24"]
- port lrp-ad952e
- mac: "fa:16:3e:ef:2f:8b"
- networks: ["fc11::1/64"]
- port lrp-17d870
- mac: "fa:16:3e:f6:e2:8f"
- networks: ["10.1.1.1/24"]
-
-Try tracing a packet from ``a`` to ``c``. The results correspond
-closely to those for IPv4 which we already discussed back under
-`Routing`_::
-
- $ N1SUBNET6_MAC=$(ovn-nbctl --bare --columns=mac find logical_router_port networks=\"fc11::1/64\")
- $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip6.src == fc11::5 && ip6.dst == fc22::7 && ip.ttl == 64 && icmp6.type == 8'
- ...
- ingress(dp="n1", inport="ap")
- -----------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src == {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
- next;
- 1. ls_in_port_sec_ip (ovn-northd.c:2390): inport == "ap" && eth.src == fa:16:3e:a9:4c:c7 && ip6.src == {fe80::f816:3eff:fea9:4cc7, fc11::5}, priority 90, uuid 604810ea
- next;
- 3. ls_in_pre_acl (ovn-northd.c:2646): ip, priority 100, uuid 46c089e6
- reg0[0] = 1;
- next;
- 5. ls_in_pre_stateful (ovn-northd.c:2764): reg0[0] == 1, priority 100, uuid d1941634
- ct_next;
-
- ct_next(ct_state=est|trk /* default (use --ct to customize) */)
- ---------------------------------------------------------------
- 6. ls_in_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (inport == "ap" && ip6), priority 2002, uuid 7fdd607e
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:ef:2f:8b, priority 50, uuid e1d87fc5
- outport = "ad952e";
- output;
-
- egress(dp="n1", inport="ap", outport="ad952e")
- ----------------------------------------------
- 1. ls_out_pre_acl (ovn-northd.c:2626): ip && outport == "ad952e", priority 110, uuid 88f68988
- next;
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "ad952e", priority 50, uuid 5935755e
- output;
- /* output to "ad952e", type "patch" */
-
- ingress(dp="r", inport="lrp-ad952e")
- ------------------------------------
- 0. lr_in_admission (ovn-northd.c:4071): eth.dst == fa:16:3e:ef:2f:8b && inport == "lrp-ad952e", priority 50, uuid ddfeb712
- next;
- 5. lr_in_ip_routing (ovn-northd.c:3782): ip6.dst == fc22::/64, priority 129, uuid cc2130ec
- ip.ttl--;
- xxreg0 = ip6.dst;
- xxreg1 = fc22::1;
- eth.src = fa:16:3e:06:de:ad;
- outport = "lrp-1a8162";
- flags.loopback = 1;
- next;
- 6. lr_in_arp_resolve (ovn-northd.c:5122): outport == "lrp-1a8162" && xxreg0 == fc22::7, priority 100, uuid bcf75288
- eth.dst = fa:16:3e:89:f2:36;
- next;
- 8. lr_in_arp_request (ovn-northd.c:5260): 1, priority 0, uuid 6dacdd82
- output;
-
- egress(dp="r", inport="lrp-ad952e", outport="lrp-1a8162")
- ---------------------------------------------------------
- 3. lr_out_delivery (ovn-northd.c:5288): outport == "lrp-1a8162", priority 100, uuid 5260dfc5
- output;
- /* output to "lrp-1a8162", type "patch" */
-
- ingress(dp="n2", inport="1a8162")
- ---------------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "1a8162", priority 50, uuid 10957d1b
- next;
- 3. ls_in_pre_acl (ovn-northd.c:2624): ip && inport == "1a8162", priority 110, uuid a27ebd00
- next;
- 13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:89:f2:36, priority 50, uuid dcafb3e9
- outport = "cp";
- output;
-
- egress(dp="n2", inport="1a8162", outport="cp")
- ----------------------------------------------
- 1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid cd9cfa74
- reg0[0] = 1;
- next;
- 2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority 100, uuid 9e8e22c5
- ct_next;
-
- ct_next(ct_state=est|trk /* default (use --ct to customize) */)
- ---------------------------------------------------------------
- 4. ls_out_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (outport == "cp" && ip6 && ip6.src == $as_ip6_0fc1b6cf_f925_49e6_8f00_6dd13beca9dc), priority 2002, uuid 12fc96f9
- next;
- 7. ls_out_port_sec_ip (ovn-northd.c:2390): outport == "cp" && eth.dst == fa:16:3e:89:f2:36 && ip6.dst == {fe80::f816:3eff:fe89:f236, ff00::/8, fc22::7}, priority 90, uuid c622596a
- next;
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "cp" && eth.dst == {fa:16:3e:89:f2:36}, priority 50, uuid 0242cdc3
- output;
- /* output to "cp", type "" */
-
-ACLs
-----
-
-Let's explore how ACLs work in OpenStack and OVN. In OpenStack, ACL
-rules are part of "security groups", which are "default deny", that
-is, packets are not allowed by default and the rules added to security
-groups serve to allow different classes of packets. The default group
-(named "default") that is assigned to each of our VMs so far allows
-all traffic from our other VMs, which isn't very interesting for
-testing. So, let's create a new security group, which we'll name
-"custom", add rules to it that allow incoming SSH and ICMP traffic,
-and apply this security group to VM ``c``::
-
- $ openstack security group create custom
- $ openstack security group rule create --dst-port 22 custom
- $ openstack security group rule create --protocol icmp custom
- $ openstack server remove security group c default
- $ openstack server add security group c custom
-
-Now we can do some experiments to test security groups. From the
-console on ``a`` or ``b``, it should now be possible to "ping" ``c``
-or to SSH to it, but attempts to initiate connections on other ports
-should be blocked. (You can try to connect on another port with
-``ssh -p PORT IP`` or ``nc PORT IP``.) Connection attempts should
-time out rather than receive the "connection refused" or "connection
-reset" error that you would see between ``a`` and ``b``.
-
-It's also possible to test ACLs via ovn-trace, with one new wrinkle.
-ovn-trace can't simulate connection tracking state in the network, so
-by default it assumes that every packet represents an established
-connection. That's good enough for what we've been doing so far, but
-for checking properties of security groups we want to look at more
-detail.
-
-If you look back at the VM-to-VM traces we've done until now, you can
-see that they execute two ``ct_next`` actions:
-
-* The first of these is for the packet passing outward through the
- source VM's firewall. We can tell ovn-trace to treat the packet as
- starting a new connection or adding to an established connection by
- adding a ``--ct`` option: ``--ct new`` or ``--ct est``,
- respectively. The latter is the default and therefore what we've
- been using so far. We can also use ``--ct est,rpl``, which in
- addition to ``--ct est`` means that the connection was initiated by
- the destination VM rather than by the VM sending this packet.
-
-* The second is for the packet passing inward through the destination
- VM's firewall. For this one, it makes sense to tell ovn-trace that
- the packet is starting a new connection, with ``--ct new``, or that
- it is a packet sent in reply to a connection established by the
- destination VM, with ``--ct est,rpl``.
-
-ovn-trace uses the ``--ct`` options in order, so if we want to
-override the second ``ct_next`` behavior we have to specify two
-options.
-
-Another useful ovn-trace option for this testing is ``--minimal``,
-which reduces the amount of output. In this case we're really just
-interested in finding out whether the packet reaches the destination
-VM, that is, whether there's an eventual ``output`` action to ``c``,
-so ``--minimal`` works fine and the output is easier to read.
-
-Try a few traces. For example:
-
-* VM ``a`` initiates a new SSH connection to ``c``::
-
- $ ovn-trace --ct new --ct new --minimal n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip4.src == 10.1.1.5 && ip4.dst == 10.1.2.7 && ip.ttl == 64 && tcp.dst == 22'
- ...
- ct_next(ct_state=new|trk) {
- ip.ttl--;
- eth.src = fa:16:3e:19:9f:46;
- eth.dst = fa:16:3e:89:f2:36;
- ct_next(ct_state=new|trk) {
- output("cp");
- };
- };
-
- This succeeds, as you can see since there is an ``output`` action.
-
-* VM ``a`` initiates a new Telnet connection to ``c``::
-
- $ ovn-trace --ct new --ct new --minimal n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip4.src == 10.1.1.5 && ip4.dst == 10.1.2.7 && ip.ttl == 64 && tcp.dst == 23'
- ct_next(ct_state=new|trk) {
- ip.ttl--;
- eth.src = fa:16:3e:19:9f:46;
- eth.dst = fa:16:3e:89:f2:36;
- ct_next(ct_state=new|trk);
- };
-
- This fails, as you can see from the lack of an ``output`` action.
-
-* VM ``a`` replies to a packet that is part of a Telnet connection
- originally initiated by ``c``::
-
- $ ovn-trace --ct est,rpl --ct est,rpl --minimal n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip4.src == 10.1.1.5 && ip4.dst == 10.1.2.7 && ip.ttl == 64 && tcp.dst == 23'
- ...
- ct_next(ct_state=est|rpl|trk) {
- ip.ttl--;
- eth.src = fa:16:3e:19:9f:46;
- eth.dst = fa:16:3e:89:f2:36;
- ct_next(ct_state=est|rpl|trk) {
- output("cp");
- };
- };
-
- This succeeds, as you can see from the ``output`` action, since
- traffic received in reply to an outgoing connection is always
- allowed.
-
-DHCP
-----
-
-As a final demonstration of the OVN architecture, let's examine the
-DHCP implementation. Like switching, routing, and NAT, the OVN
-implementation of DHCP involves configuration in the NB DB and logical
-flows in the SB DB.
-
-Let's look at the DHCP support for ``a``'s port ``ap``. The port's
-Logical_Switch_Port record shows that ``ap`` has DHCPv4 options::
-
- $ ovn-nbctl list logical_switch_port ap | abbrev
- _uuid : ef17e5
- addresses : ["fa:16:3e:a9:4c:c7 10.1.1.5 fc11::5"]
- dhcpv4_options : 165974
- dhcpv6_options : 26f7cd
- dynamic_addresses : []
- enabled : true
- external_ids : {"neutron:port_name"=ap}
- name : "820c08"
- options : {}
- parent_name : []
- port_security : ["fa:16:3e:a9:4c:c7 10.1.1.5 fc11::5"]
- tag : []
- tag_request : []
- type : ""
- up : true
-
-We can then list them either by UUID or, more easily, by port name::
-
- $ ovn-nbctl list dhcp_options ap | abbrev
- _uuid : 165974
- cidr : "10.1.1.0/24"
- external_ids : {subnet_id="5e67e7"}
- options : {lease_time="43200", mtu="1442", router="10.1.1.1", server_id="10.1.1.1", server_mac="fa:16:3e:bb:94:72"}
-
-These options show the basic DHCP configuration for the subnet. They
-do not include the IP address itself, which comes from the
-Logical_Switch_Port record. This allows a whole Neutron subnet to
-share a single DHCP_Options record. You can see this sharing in
-action, if you like, by listing the record for port ``bp``, which is
-on the same subnet as ``ap``, and see that it is the same record as before::
-
- $ ovn-nbctl list dhcp_options bp | abbrev
- _uuid : 165974
- cidr : "10.1.1.0/24"
- external_ids : {subnet_id="5e67e7"}
- options : {lease_time="43200", mtu="1442", router="10.1.1.1", server_id="10.1.1.1", server_mac="fa:16:3e:bb:94:72"}
-
-You can take another look at the southbound flow table if you like,
-but the best demonstration is to trace a DHCP packet. The following
-is a trace of a DHCP request inbound from ``ap``. The first part is
-just the usual travel through the firewall::
-
- $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst == ff:ff:ff:ff:ff:ff && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67 && ip.ttl == 1'
- ...
- ingress(dp="n1", inport="ap")
- -----------------------------
- 0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src == {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
- next;
- 1. ls_in_port_sec_ip (ovn-northd.c:2325): inport == "ap" && eth.src == fa:16:3e:a9:4c:c7 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67, priority 90, uuid e46bed6f
- next;
- 3. ls_in_pre_acl (ovn-northd.c:2646): ip, priority 100, uuid 46c089e6
- reg0[0] = 1;
- next;
- 5. ls_in_pre_stateful (ovn-northd.c:2764): reg0[0] == 1, priority 100, uuid d1941634
- ct_next;
-
-The next part is the new part. First, an ACL in table 6 allows a DHCP
-request to pass through. In table 11, the special ``put_dhcp_opts``
-action replaces a DHCPDISCOVER or DHCPREQUEST packet by a
-reply. Table 12 flips the packet's source and destination and sends
-it back the way it came in::
-
- 6. ls_in_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl && ct_label.blocked == 0 && (inport == "ap" && ip4 && ip4.dst == {255.255.255.255, 10.1.1.0/24} && udp && udp.src == 68 && udp.dst == 67), priority 2002, uuid 9c90245d
- next;
- 11. ls_in_dhcp_options (ovn-northd.c:3409): inport == "ap" && eth.src == fa:16:3e:a9:4c:c7 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67, priority 100, uuid 8d63f29c
- reg0[3] = put_dhcp_opts(offerip = 10.1.1.5, lease_time = 43200, mtu = 1442, netmask = 255.255.255.0, router = 10.1.1.1, server_id = 10.1.1.1);
- /* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */
- next;
- 12. ls_in_dhcp_response (ovn-northd.c:3438): inport == "ap" && eth.src == fa:16:3e:a9:4c:c7 && ip4 && udp.src == 68 && udp.dst == 67 && reg0[3], priority 100, uuid 995eeaa9
- eth.dst = eth.src;
- eth.src = fa:16:3e:bb:94:72;
- ip4.dst = 10.1.1.5;
- ip4.src = 10.1.1.1;
- udp.src = 67;
- udp.dst = 68;
- outport = inport;
- flags.loopback = 1;
- output;
-
-Then the last part is just traveling back through the firewall to VM
-``a``::
-
- egress(dp="n1", inport="ap", outport="ap")
- ------------------------------------------
- 1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid 3752b746
- reg0[0] = 1;
- next;
- 2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority 100, uuid 0c066ea1
- ct_next;
-
- ct_next(ct_state=est|trk /* default (use --ct to customize) */)
- ---------------------------------------------------------------
- 4. ls_out_acl (ovn-northd.c:3008): outport == "ap" && eth.src == fa:16:3e:bb:94:72 && ip4.src == 10.1.1.1 && udp && udp.src == 67 && udp.dst == 68, priority 34000, uuid 0b383e77
- ct_commit;
- next;
- 7. ls_out_port_sec_ip (ovn-northd.c:2364): outport == "ap" && eth.dst == fa:16:3e:a9:4c:c7 && ip4.dst == {255.255.255.255, 224.0.0.0/4, 10.1.1.5}, priority 90, uuid 7b8cbcd5
- next;
- 8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "ap" && eth.dst == {fa:16:3e:a9:4c:c7}, priority 50, uuid b874ece8
- output;
- /* output to "ap", type "" */
-
-Further Directions
-------------------
-
-We've looked at a fair bit of how OVN works and how it interacts with
-OpenStack. If you still have some interest, then you might want to
-explore some of these directions:
-
-* Adding more than one hypervisor ("compute node", in OpenStack
- parlance). OVN connects compute nodes by tunneling packets with the
- STT or Geneve protocols. OVN scales to 1000 compute nodes or more,
- but two compute nodes demonstrate the principle. All of the tools
- and techniques we demonstrated also work with multiple compute
- nodes.
-
-* Container support. OVN supports seamlessly connecting VMs to
- containers, whether the containers are hosted on "bare metal" or
- nested inside VMs. OpenStack support for containers, however, is
- still evolving, and too difficult to incorporate into the tutorial
- at this point.
-
-* Other kinds of gateways. In addition to floating IPs with NAT, OVN
- supports directly attaching VMs to a physical network and connecting
- logical switches to VTEP hardware.
diff --git a/Documentation/tutorials/ovn-rbac.rst b/Documentation/tutorials/ovn-rbac.rst
deleted file mode 100644
index ec163e2df..000000000
--- a/Documentation/tutorials/ovn-rbac.rst
+++ /dev/null
@@ -1,134 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-=============================================
-OVN Role-Based Access Control (RBAC) Tutorial
-=============================================
-
-This document provides a step-by-step guide for setting up role-based access
-control (RBAC) in OVN. In OVN, hypervisors (chassis) read and write the
-southbound database to do configuration. Without restricting hypervisor's
-access to the southbound database, a compromised hypervisor might disrupt the
-entire OVN deployment by corrupting the database. RBAC ensures that each
-hypervisor can only modify its own data and thus improves the security of OVN.
-More details about the RBAC design can be found in ``ovn-architecture``\(7)
-manpage.
-
-This document assumes OVN is installed in your system and runs normally.
-
-.. _gen-certs-keys:
-
-Generating Certificates and Keys
---------------------------------
-
-In the OVN RBAC deployment, ovn-controller connects to the southbound database
-via SSL connection. The southbound database uses CA-signed certificate to
-authenticate ovn-controller.
-
-Suppose there are three machines in your deployment. `machine_1` runs
-`chassis_1` and has IP address `machine_1-ip`. `machine_2` runs `chassis_2` and
-has IP address `machine_2-ip`. `machine_3` hosts southbound database and has IP
-address `machine_3-ip`. `machine_3` also hosts public key infrastructure (PKI).
-
-1. Initiate PKI.
-
- In `machine_3`::
-
- $ ovs-pki init
-
-2. Generate southbound database's certificate request. Sign the certificate
- request with the CA key.
-
- In `machine_3`::
-
- $ ovs-pki req -u sbdb
- $ ovs-pki sign sbdb switch
-
-3. Generate chassis certificate requests. Copy the certificate requests to
- `machine_3`.
-
- In `machine_1`::
-
- $ ovs-pki req -u chassis_1
- $ scp chassis_1-req.pem \
- machine_3@machine_3-ip:/path/to/chassis_1-req.pem
-
- In `machine_2`::
-
- $ ovs-pki req -u chassis_2
- $ scp chassis_2-req.pem \
- machine_3@machine_3-ip:/path/to/chassis_2-req.pem
-
- .. note::
-
- chassis_1 must be the same string as ``external_ids:system-id`` in the
- Open_vSwitch table (the chassis name) of machine_1. Same applies for
- chassis_2.
-
-4. Sign the chassis certificate requests with the CA key. Copy `chassis_1`'s
- signed certificate and the CA certificate to `machine_1`. Copy `chassis_2`'s
- signed certificate and the CA certificate to `machine_2`.
-
- In `machine_3`::
-
- $ ovs-pki sign chassis_1 switch
- $ ovs-pki sign chassis_2 switch
- $ scp chassis_1-cert.pem \
- machine_1@machine_1-ip:/path/to/chassis_1-cert.pem
- $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \
- machine_1@machine_1-ip:/path/to/cacert.pem
- $ scp chassis_2-cert.pem \
- machine_2@machine_2-ip:/path/to/chassis_2-cert.pem
- $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \
- machine_2@machine_2-ip:/path/to/cacert.pem
-
-Configuring RBAC
-----------------
-
-1. Set certificate, private key, and CA certificate for the southbound
- database. Configure the southbound database to listen on SSL connection and
- enforce role-based access control.
-
- In `machine_3`::
-
- $ ovn-sbctl set-ssl /path/to/sbdb-privkey.pem \
- /path/to/sbdb-cert.pem /path/to/cacert.pem
- $ ovn-sbctl set-connection role=ovn-controller pssl:6642
-
-2. Set certificate, private key, and CA certificate for `chassis_1` and
- `chassis_2`. Configure `chassis_1` and `chassis_2` to connect southbound
- database via SSL.
-
- In `machine_1`::
-
- $ ovs-vsctl set-ssl /path/to/chassis_1-privkey.pem \
- /path/to/chassis_1-cert.pem /path/to/cacert.pem
- $ ovs-vsctl set open_vswitch . \
- external_ids:ovn-remote=ssl:machine_3-ip:6642
-
- In `machine_2`::
-
- $ ovs-vsctl set-ssl /path/to/chassis_2-privkey.pem \
- /path/to/chassis_2-cert.pem /path/to/cacert.pem
- $ ovs-vsctl set open_vswitch . \
- external_ids:ovn-remote=ssl:machine_3-ip:6642
diff --git a/Documentation/tutorials/ovn-sandbox.rst b/Documentation/tutorials/ovn-sandbox.rst
deleted file mode 100644
index b906b799d..000000000
--- a/Documentation/tutorials/ovn-sandbox.rst
+++ /dev/null
@@ -1,177 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-===========
-OVN Sandbox
-===========
-
-This tutorial shows you how to explore features using ``ovs-sandbox`` as a
-simulated test environment. It's assumed that you have an understanding of OVS
-before going through this tutorial. Detail about OVN is covered in
-ovn-architecture_, but this tutorial lets you quickly see it in action.
-
-Getting Started
----------------
-
-For some general information about ``ovs-sandbox``, see the "Getting Started"
-section of :doc:`ovs-advanced`.
-
-``ovs-sandbox`` does not include OVN support by default. To enable OVN, you
-must pass the ``--ovn`` flag. For example, if running it straight from the OVS
-git tree you would run::
-
- $ make sandbox SANDBOXFLAGS="--ovn"
-
-Running the sandbox with OVN enabled does the following additional steps to the
-environment:
-
-1. Creates the ``OVN_Northbound`` and ``OVN_Southbound`` databases as described in
- `ovn-nb(5)`_ and `ovn-sb(5)`_.
-
-2. Creates a backup server for ``OVN_Southbond`` database. Sandbox launch
- screen provides the instructions on accessing the backup database. However
- access to the backup server is not required to go through the tutorial.
-
-3. Creates the ``hardware_vtep`` database as described in `vtep(5)`_.
-
-4. Runs the `ovn-northd(8)`_, `ovn-controller(8)`_, and
- `ovn-controller-vtep(8)`_ daemons.
-
-5. Makes OVN and VTEP utilities available for use in the environment, including
- `vtep-ctl(8)`_, `ovn-nbctl(8)`_, and `ovn-sbctl(8)`_.
-
-Using GDB
----------
-
-GDB support is not required to go through the tutorial. See the "Using GDB"
-section of :doc:`ovs-advanced` for more info. Additional flags exist for
-launching the debugger for the OVN programs::
-
- --gdb-ovn-northd
- --gdb-ovn-controller
- --gdb-ovn-controller-vtep
-
-Creating OVN Resources
-----------------------
-
-Once you have ``ovs-sandbox`` running with OVN enabled, you can start using OVN
-utilities to create resources in OVN. As an example, we will create an
-environment that has two logical switches connected by a logical router.
-
-Create the first logical switch with one port::
-
- $ ovn-nbctl ls-add sw0
- $ ovn-nbctl lsp-add sw0 sw0-port1
- $ ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 192.168.0.2"
-
-Create the second logical switch with one port::
-
- $ ovn-nbctl ls-add sw1
- $ ovn-nbctl lsp-add sw1 sw1-port1
- $ ovn-nbctl lsp-set-addresses sw1-port1 "50:54:00:00:00:03 11.0.0.2"
-
-Create the logical router and attach both logical switches::
-
- $ ovn-nbctl lr-add lr0
- $ ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:ff:01 192.168.0.1/24
- $ ovn-nbctl lsp-add sw0 lrp0-attachment
- $ ovn-nbctl lsp-set-type lrp0-attachment router
- $ ovn-nbctl lsp-set-addresses lrp0-attachment 00:00:00:00:ff:01
- $ ovn-nbctl lsp-set-options lrp0-attachment router-port=lrp0
- $ ovn-nbctl lrp-add lr0 lrp1 00:00:00:00:ff:02 11.0.0.1/24
- $ ovn-nbctl lsp-add sw1 lrp1-attachment
- $ ovn-nbctl lsp-set-type lrp1-attachment router
- $ ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02
- $ ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1
-
-View a summary of OVN's current logical configuration::
-
- $ ovn-nbctl show
- switch 1396cf55-d176-4082-9a55-1c06cef626e4 (sw1)
- port lrp1-attachment
- addresses: ["00:00:00:00:ff:02"]
- port sw1-port1
- addresses: ["50:54:00:00:00:03 11.0.0.2"]
- switch 2c9d6d03-09fc-4e32-8da6-305f129b0d53 (sw0)
- port lrp0-attachment
- addresses: ["00:00:00:00:ff:01"]
- port sw0-port1
- addresses: ["50:54:00:00:00:01 192.168.0.2"]
- router f8377e8c-f75e-4fc8-8751-f3ea03c6dd98 (lr0)
- port lrp0
- mac: "00:00:00:00:ff:01"
- networks: ["192.168.0.1/24"]
- port lrp1
- mac: "00:00:00:00:ff:02"
- networks: ["11.0.0.1/24"]
-
-The ``tutorial`` directory of the OVS source tree includes a script
-that runs all of the commands for you::
-
- $ ./ovn-setup.sh
-
-Using ovn-trace
----------------
-
-Once you have configured resources in OVN, try using ``ovn-trace`` to see
-how OVN would process a sample packet through its logical pipeline.
-
-For example, we can trace an IP packet from ``sw0-port1`` to ``sw1-port1``.
-The ``--minimal`` output shows each visible action performed on the packet,
-which includes:
-
-#. The logical router will decrement the IP TTL field.
-#. The logical router will change the source and destination
- MAC addresses to reflect the next hop.
-#. The packet will be output to ``sw1-port1``.
-
-::
-
- $ ovn-trace --minimal sw0 'inport == "sw0-port1" \
- > && eth.src == 50:54:00:00:00:01 && ip4.src == 192.168.0.2 \
- > && eth.dst == 00:00:00:00:ff:01 && ip4.dst == 11.0.0.2 \
- > && ip.ttl == 64'
-
- # ip,reg14=0x1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=00:00:00:00:ff:01,nw_src=192.168.0.2,nw_dst=11.0.0.2,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=64
- ip.ttl--;
- eth.src = 00:00:00:00:ff:02;
- eth.dst = 50:54:00:00:00:03;
- output("sw1-port1");
-
-The ``ovn-trace`` utility can also provide much more detail on how the packet
-would be processed through OVN's logical pipeline, as well as correlate that
-to OpenFlow flows programmed by ``ovn-controller``. See the `ovn-trace(8)`_
-man page for more detail.
-
-
-.. _ovn-architecture: http://openvswitch.org/support/dist-docs/ovn-architecture.7.html
-.. _ovn-nb(5): http://openvswitch.org/support/dist-docs/ovn-nb.5.html
-.. _ovn-sb(5): http://openvswitch.org/support/dist-docs/ovn-sb.5.html
-.. _vtep(5): http://openvswitch.org/support/dist-docs/vtep.5.html
-.. _ovn-northd(8): http://openvswitch.org/support/dist-docs/ovn-northd.8.html
-.. _ovn-controller(8): http://openvswitch.org/support/dist-docs/ovn-controller.8.html
-.. _ovn-controller-vtep(8): http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html
-.. _vtep-ctl(8): http://openvswitch.org/support/dist-docs/vtep-ctl.8.html
-.. _ovn-nbctl(8): http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html
-.. _ovn-sbctl(8): http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html
-.. _ovn-trace(8): http://openvswitch.org/support/dist-docs/ovn-trace.8.html
diff --git a/NEWS b/NEWS
index d58a67141..f5a0b8faf 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
Post-v2.12.0
---------------------
-
+ - OVN:
+ * OVN has been removed from this repository. It now exists as a
+ separate project. You can find it at
+ https://github.com/ovn-org/ovn.git
v2.12.0 - 03 Sep 2019
---------------------
diff --git a/configure.ac b/configure.ac
index e3603926b..1d45c4fdd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -210,8 +210,6 @@ dnl This makes sure that include/openflow gets created in the build directory.
AC_CONFIG_COMMANDS([include/openflow/openflow.h.stamp])
AC_CONFIG_COMMANDS([utilities/bugtool/dummy], [:])
-AC_CONFIG_COMMANDS([ovn/dummy], [:])
-AC_CONFIG_COMMANDS([ovn/utilities/dummy], [:])
AC_CONFIG_COMMANDS([ipsec/dummy], [:])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
diff --git a/debian/.gitignore b/debian/.gitignore
index 9ec70eb9c..441a8ffb7 100644
--- a/debian/.gitignore
+++ b/debian/.gitignore
@@ -22,10 +22,5 @@
/openvswitch-test
/openvswitch-testcontroller
/openvswitch-vtep
-/ovn-common
-/ovn-controller-vtep
-/ovn-host
-/ovn-central
-/ovn-docker
/python-openvswitch
/tmp
diff --git a/debian/automake.mk b/debian/automake.mk
index 8a8d43c9f..03a1d68c2 100644
--- a/debian/automake.mk
+++ b/debian/automake.mk
@@ -52,28 +52,6 @@ EXTRA_DIST += \
debian/openvswitch-vtep.init \
debian/openvswitch-vtep.install \
debian/openvswitch-vtep.manpages \
- debian/ovn-central.dirs \
- debian/ovn-central.init \
- debian/ovn-central.install \
- debian/ovn-central.manpages \
- debian/ovn-central.postinst \
- debian/ovn-central.postrm \
- debian/ovn-central.template \
- debian/ovn-controller-vtep.init \
- debian/ovn-controller-vtep.install \
- debian/ovn-controller-vtep.manpages \
- debian/ovn-common.install \
- debian/ovn-common.manpages \
- debian/ovn-common.postinst \
- debian/ovn-common.postrm \
- debian/ovn-docker.install \
- debian/ovn-host.dirs \
- debian/ovn-host.init \
- debian/ovn-host.install \
- debian/ovn-host.manpages \
- debian/ovn-host.postinst \
- debian/ovn-host.postrm \
- debian/ovn-host.template \
debian/python-openvswitch.dirs \
debian/python-openvswitch.install \
debian/rules \
diff --git a/debian/control b/debian/control
index b97e99b92..2ad35f2ce 100644
--- a/debian/control
+++ b/debian/control
@@ -119,84 +119,6 @@ Description: Open vSwitch switch implementations
openvswitch-switch provides the userspace components and utilities for
the Open vSwitch kernel-based switch.
-Package: ovn-common
-Architecture: linux-any
-Depends: openvswitch-common (= ${binary:Version}),
- ${misc:Depends},
- ${shlibs:Depends}
-Description: OVN common components
- OVN, the Open Virtual Network, is a system to support virtual network
- abstraction. OVN complements the existing capabilities of OVS to add
- native support for virtual network abstractions, such as virtual L2 and L3
- overlays and security groups.
- .
- ovn-common provides components required by other OVN packages.
-
-Package: ovn-controller-vtep
-Architecture: linux-any
-Depends: ovn-common (= ${binary:Version}),
- ${misc:Depends},
- ${shlibs:Depends}
-Description: OVN vtep controller
- ovn-controller-vtep is the local controller daemon in
- OVN, the Open Virtual Network, for VTEP enabled physical switches.
- It connects up to the OVN Southbound database over the OVSDB protocol,
- and down to the VTEP database over the OVSDB protocol.
- .
- ovn-controller-vtep provides the ovn-controller-vtep binary for controlling
- vtep gateways.
-
-
-Package: ovn-host
-Architecture: linux-any
-Depends: openvswitch-switch (= ${binary:Version}),
- openvswitch-common (= ${binary:Version}),
- ovn-common (= ${binary:Version}),
- ${misc:Depends},
- ${shlibs:Depends}
-Description: OVN host components
- OVN, the Open Virtual Network, is a system to support virtual network
- abstraction. OVN complements the existing capabilities of OVS to add
- native support for virtual network abstractions, such as virtual L2 and L3
- overlays and security groups.
- .
- ovn-host provides the userspace components and utilities for
- OVN that can be run on every host/hypervisor.
-
-Package: ovn-central
-Architecture: linux-any
-Depends: openvswitch-switch (= ${binary:Version}),
- openvswitch-common (= ${binary:Version}),
- ovn-common (= ${binary:Version}),
- ${misc:Depends},
- ${shlibs:Depends}
-Description: OVN central components
- OVN, the Open Virtual Network, is a system to support virtual network
- abstraction. OVN complements the existing capabilities of OVS to add
- native support for virtual network abstractions, such as virtual L2 and L3
- overlays and security groups.
- .
- ovn-central provides the userspace daemons, utilities and
- databases for OVN that is run at a central location.
-
-Package: ovn-docker
-Architecture: linux-any
-Depends: openvswitch-switch (= ${binary:Version}),
- openvswitch-common (= ${binary:Version}),
- python (>= 2.7),
- python-openvswitch (= ${source:Version}),
- ovn-common (= ${binary:Version}),
- ${misc:Depends},
- ${python:Depends},
- ${shlibs:Depends}
-Description: OVN Docker drivers
- OVN, the Open Virtual Network, is a system to support virtual network
- abstraction. OVN complements the existing capabilities of OVS to add
- native support for virtual network abstractions, such as virtual L2 and L3
- overlays and security groups.
- .
- ovn-docker provides the docker drivers for OVN.
-
Package: openvswitch-pki
Architecture: all
Depends: openvswitch-common (<< ${source:Version}.1~),
diff --git a/debian/ovn-central.dirs b/debian/ovn-central.dirs
deleted file mode 100644
index 6394883ce..000000000
--- a/debian/ovn-central.dirs
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/ovn/central
diff --git a/debian/ovn-central.init b/debian/ovn-central.init
deleted file mode 100755
index 60cee95a3..000000000
--- a/debian/ovn-central.init
+++ /dev/null
@@ -1,60 +0,0 @@
-#! /bin/sh
-#
-### BEGIN INIT INFO
-# Provides: ovn-central
-# Required-Start: openvswitch-switch $remote_fs $syslog
-# Required-Stop: $remote_fs
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: OVN central components
-# Description: ovn-central provides the userspace daemons,
-# utilities and databases for OVN that is run at a central
-# location.
-### END INIT INFO
-
-test -x /usr/bin/ovn-northd || exit 0
-test -x /usr/share/openvswitch/scripts/ovn-ctl || exit 0
-
-_SYSTEMCTL_SKIP_REDIRECT=yes
-SYSTEMCTL_SKIP_REDIRECT=yes
-
-. /usr/share/openvswitch/scripts/ovs-lib
-if [ -e /etc/default/ovn-central ]; then
- . /etc/default/ovn-central
-fi
-
-start () {
- set /usr/share/openvswitch/scripts/ovn-ctl ${1-start_northd}
- set "$@" $OVN_CTL_OPTS
- "$@" || exit $?
-}
-
-stop_northd () {
- set /usr/share/openvswitch/scripts/ovn-ctl ${1-stop_northd}
- set "$@" $OVN_CTL_OPTS
- "$@" || exit $?
-}
-
-case $1 in
- start)
- start
- ;;
- stop)
- stop_northd
- ;;
- restart)
- start restart_northd
- ;;
- reload | force-reload)
- ;;
- status)
- /usr/share/openvswitch/scripts/ovn-ctl status_northd
- exit $?
- ;;
- *)
- echo "Usage: $0 {start|stop|reload|force-reload|restart|status}" >&2
- exit 1
- ;;
-esac
-
-exit 0
diff --git a/debian/ovn-central.install b/debian/ovn-central.install
deleted file mode 100644
index 733d3fc5e..000000000
--- a/debian/ovn-central.install
+++ /dev/null
@@ -1,3 +0,0 @@
-usr/bin/ovn-northd
-usr/share/openvswitch/ovn-nb.ovsschema
-usr/share/openvswitch/ovn-sb.ovsschema
diff --git a/debian/ovn-central.manpages b/debian/ovn-central.manpages
deleted file mode 100644
index 2ddb43784..000000000
--- a/debian/ovn-central.manpages
+++ /dev/null
@@ -1 +0,0 @@
-ovn/northd/ovn-northd.8
diff --git a/debian/ovn-central.postinst b/debian/ovn-central.postinst
deleted file mode 100755
index 10e07ece4..000000000
--- a/debian/ovn-central.postinst
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/sh
-# postinst script for ovn-central
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * <postinst> `configure' <most-recently-configured-version>
-# * <old-postinst> `abort-upgrade' <new version>
-# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
-# <new-version>
-# * <postinst> `abort-remove'
-# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
-# <failed-install-package> <version> `removing'
-# <conflicting-package> <version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- configure)
- DEFAULT=/etc/default/ovn-central
- TEMPLATE=/usr/share/ovn/central/default.template
- if ! test -e $DEFAULT; then
- cp $TEMPLATE $DEFAULT
- else
- for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE)
- do
- if ! grep $var $DEFAULT >/dev/null 2>&1; then
- echo >> $DEFAULT
- sed -n "/$var:/,/$var=/p" $TEMPLATE >> $DEFAULT
- fi
- done
- fi
- ;;
-
- abort-upgrade|abort-remove|abort-deconfigure)
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/ovn-central.postrm b/debian/ovn-central.postrm
deleted file mode 100755
index 0e654a37a..000000000
--- a/debian/ovn-central.postrm
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/bin/sh
-# postrm script for ovn-central
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * <postrm> `remove'
-# * <postrm> `purge'
-# * <old-postrm> `upgrade' <new-version>
-# * <new-postrm> `failed-upgrade' <old-version>
-# * <new-postrm> `abort-install'
-# * <new-postrm> `abort-install' <old-version>
-# * <new-postrm> `abort-upgrade' <old-version>
-# * <disappearer's-postrm> `disappear' <overwriter>
-# <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- purge)
- rm -f /etc/default/ovn-central
- rm -f /etc/openvswitch/ovnnb.db*
- rm -f /etc/openvswitch/.ovnnb.db.~lock~
- rm -f /etc/openvswitch/ovnsb.db*
- rm -f /etc/openvswitch/.ovnsb.db.~lock~
- rm -f /var/log/openvswitch/ovn-northd.log* || true
- ;;
-
- remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
- ;;
-
- *)
- echo "postrm called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/ovn-central.template b/debian/ovn-central.template
deleted file mode 100644
index 7cea13e50..000000000
--- a/debian/ovn-central.template
+++ /dev/null
@@ -1,5 +0,0 @@
-# This is a POSIX shell fragment -*- sh -*-
-
-# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example,
-# a suitable place to specify --ovn-northd-wrapper=valgrind.
-# OVN_CTL_OPTS=
diff --git a/debian/ovn-common.install b/debian/ovn-common.install
deleted file mode 100644
index 90484d2fe..000000000
--- a/debian/ovn-common.install
+++ /dev/null
@@ -1,7 +0,0 @@
-usr/bin/ovn-nbctl
-usr/bin/ovn-sbctl
-usr/bin/ovn-trace
-usr/bin/ovn-detrace
-usr/share/openvswitch/scripts/ovn-ctl
-usr/share/openvswitch/scripts/ovndb-servers.ocf
-usr/lib/*/libovn*.so.*
diff --git a/debian/ovn-common.manpages b/debian/ovn-common.manpages
deleted file mode 100644
index 249349ed8..000000000
--- a/debian/ovn-common.manpages
+++ /dev/null
@@ -1,8 +0,0 @@
-ovn/ovn-architecture.7
-ovn/ovn-nb.5
-ovn/ovn-sb.5
-ovn/utilities/ovn-ctl.8
-ovn/utilities/ovn-nbctl.8
-ovn/utilities/ovn-sbctl.8
-ovn/utilities/ovn-trace.8
-ovn/utilities/ovn-detrace.1
diff --git a/debian/ovn-common.postinst b/debian/ovn-common.postinst
deleted file mode 100644
index 588044fbc..000000000
--- a/debian/ovn-common.postinst
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-# postinst script for ovn-common
-#
-# see: dh_installdeb(1)
-
-set -e
-
-case "$1" in
- configure)
- mkdir -p /usr/lib/ocf/resource.d/ovn
- ln -sf /usr/share/openvswitch/scripts/ovndb-servers.ocf /usr/lib/ocf/resource.d/ovn/ovndb-servers
- ;;
- abort-upgrade|abort-remove|abort-deconfigure)
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/ovn-common.postrm b/debian/ovn-common.postrm
deleted file mode 100644
index 9face726b..000000000
--- a/debian/ovn-common.postrm
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-# postrm script for openvswitch-testcontroller
-#
-# see: dh_installdeb(1)
-
-set -e
-
-case "$1" in
- purge|remove)
- rm -rf /usr/lib/ocf/resource.d/ovn
- ;;
- upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
- ;;
-
- *)
- echo "postrm called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/ovn-controller-vtep.init b/debian/ovn-controller-vtep.init
deleted file mode 100755
index be0a24358..000000000
--- a/debian/ovn-controller-vtep.init
+++ /dev/null
@@ -1,54 +0,0 @@
-#! /bin/sh
-#
-### BEGIN INIT INFO
-# Provides: ovn-controller-vtep
-# Required-Start: openvswitch-switch $remote_fs $syslog
-# Required-Stop: $remote_fs
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: OVN Controller for VTEP enabled devices
-# Description: ovn-controller-vtep provides the userspace
-# components and utilities for OVN that can be run on
-# hosts taht connect to VTEP enabled devices.
-### END INIT INFO
-
-test -x /usr/bin/ovn-controller-vtep || exit 0
-test -x /usr/share/openvswitch/scripts/ovn-ctl || exit 0
-
-_SYSTEMCTL_SKIP_REDIRECT=yes
-SYSTEMCTL_SKIP_REDIRECT=yes
-
-. /usr/share/openvswitch/scripts/ovs-lib
-if [ -e /etc/default/ovn-controller-vtep ]; then
- . /etc/default/ovn-controller-vtep
-fi
-
-start () {
- set /usr/share/openvswitch/scripts/ovn-ctl ${1-start_controller_vtep}
- set "$@" $OVN_CTL_OPTS
- "$@" || exit $?
-}
-
-case $1 in
- start)
- start
- ;;
- stop | force-stop)
- /usr/share/openvswitch/scripts/ovn-ctl stop_controller_vtep
- ;;
- restart)
- start restart_controller_vtep
- ;;
- status)
- /usr/share/openvswitch/scripts/ovn-ctl status_controller_vtep
- exit $?
- ;;
- reload | force-reload)
- ;;
- *)
- echo "Usage: $0 {start|stop|reload|force-reload|restart|status}" >&2
- exit 1
- ;;
-esac
-
-exit 0
diff --git a/debian/ovn-controller-vtep.install b/debian/ovn-controller-vtep.install
deleted file mode 100644
index 1d208f37e..000000000
--- a/debian/ovn-controller-vtep.install
+++ /dev/null
@@ -1 +0,0 @@
-usr/bin/ovn-controller-vtep
diff --git a/debian/ovn-controller-vtep.manpages b/debian/ovn-controller-vtep.manpages
deleted file mode 100644
index 787301704..000000000
--- a/debian/ovn-controller-vtep.manpages
+++ /dev/null
@@ -1 +0,0 @@
-ovn/controller-vtep/ovn-controller-vtep.8
diff --git a/debian/ovn-docker.install b/debian/ovn-docker.install
deleted file mode 100644
index 583306732..000000000
--- a/debian/ovn-docker.install
+++ /dev/null
@@ -1,2 +0,0 @@
-usr/bin/ovn-docker-overlay-driver
-usr/bin/ovn-docker-underlay-driver
diff --git a/debian/ovn-host.dirs b/debian/ovn-host.dirs
deleted file mode 100644
index 7d3c761e1..000000000
--- a/debian/ovn-host.dirs
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/ovn/host
diff --git a/debian/ovn-host.init b/debian/ovn-host.init
deleted file mode 100755
index 39c3bcf16..000000000
--- a/debian/ovn-host.init
+++ /dev/null
@@ -1,54 +0,0 @@
-#! /bin/sh
-#
-### BEGIN INIT INFO
-# Provides: ovn-host
-# Required-Start: openvswitch-switch $remote_fs $syslog
-# Required-Stop: $remote_fs
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: OVN host components
-# Description: ovn-host provides the userspace
-# components and utilities for OVN that can be run on
-# every host/hypervisor.
-### END INIT INFO
-
-test -x /usr/bin/ovn-controller || exit 0
-test -x /usr/share/openvswitch/scripts/ovn-ctl || exit 0
-
-_SYSTEMCTL_SKIP_REDIRECT=yes
-SYSTEMCTL_SKIP_REDIRECT=yes
-
-. /usr/share/openvswitch/scripts/ovs-lib
-if [ -e /etc/default/ovn-host ]; then
- . /etc/default/ovn-host
-fi
-
-start () {
- set /usr/share/openvswitch/scripts/ovn-ctl ${1-start_controller}
- set "$@" $OVN_CTL_OPTS
- "$@" || exit $?
-}
-
-case $1 in
- start)
- start
- ;;
- stop | force-stop)
- /usr/share/openvswitch/scripts/ovn-ctl stop_controller
- ;;
- restart)
- start restart_controller
- ;;
- status)
- /usr/share/openvswitch/scripts/ovn-ctl status_controller
- exit $?
- ;;
- reload | force-reload)
- ;;
- *)
- echo "Usage: $0 {start|stop|reload|force-reload|restart|status}" >&2
- exit 1
- ;;
-esac
-
-exit 0
diff --git a/debian/ovn-host.install b/debian/ovn-host.install
deleted file mode 100644
index d2de82fd9..000000000
--- a/debian/ovn-host.install
+++ /dev/null
@@ -1 +0,0 @@
-usr/bin/ovn-controller
diff --git a/debian/ovn-host.manpages b/debian/ovn-host.manpages
deleted file mode 100644
index 4f9e7bc90..000000000
--- a/debian/ovn-host.manpages
+++ /dev/null
@@ -1 +0,0 @@
-ovn/controller/ovn-controller.8
diff --git a/debian/ovn-host.postinst b/debian/ovn-host.postinst
deleted file mode 100755
index 4b3edeb75..000000000
--- a/debian/ovn-host.postinst
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/bin/sh
-# postinst script for ovn-host
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * <postinst> `configure' <most-recently-configured-version>
-# * <old-postinst> `abort-upgrade' <new version>
-# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
-# <new-version>
-# * <postinst> `abort-remove'
-# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
-# <failed-install-package> <version> `removing'
-# <conflicting-package> <version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- configure)
- DEFAULT=/etc/default/ovn-host
- TEMPLATE=/usr/share/ovn/host/default.template
- if ! test -e $DEFAULT; then
- cp $TEMPLATE $DEFAULT
- else
- for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE)
- do
- if ! grep $var $DEFAULT >/dev/null 2>&1; then
- echo >> $DEFAULT
- sed -n "/$var:/,/$var=/p" $TEMPLATE >> $DEFAULT
- fi
- done
- fi
- ;;
-
- abort-upgrade|abort-remove|abort-deconfigure)
- ;;
-
- *)
- echo "postinst called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/ovn-host.postrm b/debian/ovn-host.postrm
deleted file mode 100755
index 4cceb9087..000000000
--- a/debian/ovn-host.postrm
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-# postrm script for ovn-host
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * <postrm> `remove'
-# * <postrm> `purge'
-# * <old-postrm> `upgrade' <new-version>
-# * <new-postrm> `failed-upgrade' <old-version>
-# * <new-postrm> `abort-install'
-# * <new-postrm> `abort-install' <old-version>
-# * <new-postrm> `abort-upgrade' <old-version>
-# * <disappearer's-postrm> `disappear' <overwriter>
-# <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
- purge)
- rm -f /etc/default/ovn-host
- rm -f /var/log/openvswitch/ovn-controller.log* || true
- ;;
-
- remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
- ;;
-
- *)
- echo "postrm called with unknown argument \`$1'" >&2
- exit 1
- ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
-
diff --git a/debian/ovn-host.template b/debian/ovn-host.template
deleted file mode 100644
index 7fd54efda..000000000
--- a/debian/ovn-host.template
+++ /dev/null
@@ -1,5 +0,0 @@
-# This is a POSIX shell fragment -*- sh -*-
-
-# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example,
-# a suitable place to specify --ovn-controller-wrapper=valgrind.
-# OVN_CTL_OPTS=
diff --git a/debian/rules b/debian/rules
index 9d0a81f1a..77f3a1984 100755
--- a/debian/rules
+++ b/debian/rules
@@ -53,12 +53,6 @@ override_dh_install-arch:
# openvswitch-switch
cp debian/openvswitch-switch.template debian/openvswitch-switch/usr/share/openvswitch/switch/default.template
- # ovn-host
- cp debian/ovn-host.template debian/ovn-host/usr/share/ovn/host/default.template
-
- # ovn-central
- cp debian/ovn-central.template debian/ovn-central/usr/share/ovn/central/default.template
-
override_dh_install-indep:
dh_install
diff --git a/include/automake.mk b/include/automake.mk
index 01031e88d..e982da87d 100644
--- a/include/automake.mk
+++ b/include/automake.mk
@@ -11,7 +11,6 @@ include/odp-netlink-macros.h: include/odp-netlink.h \
EXTRA_DIST += build-aux/extract-odp-netlink-h build-aux/extract-odp-netlink-macros-h
CLEANFILES += include/odp-netlink.h include/odp-netlink-macros.h
-include include/ovn/automake.mk
include include/openflow/automake.mk
include include/openvswitch/automake.mk
include include/sparse/automake.mk
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
deleted file mode 100644
index 63d3907d8..000000000
--- a/include/ovn/actions.h
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_ACTIONS_H
-#define OVN_ACTIONS_H 1
-
-#include <stdbool.h>
-#include <stdint.h>
-#include "compiler.h"
-#include "expr.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/uuid.h"
-#include "util.h"
-
-struct expr;
-struct lexer;
-struct ofpbuf;
-struct shash;
-struct simap;
-struct ovn_extend_table;
-
-/* List of OVN logical actions.
- *
- * This macro is used directly only internally by this header and its
- * corresponding .c file, but the list is still of interest to developers.
- *
- * Each OVNACT invocation has the following parameters:
- *
- * 1. <ENUM>, used below in the enum definition of OVNACT_<ENUM>, and
- * elsewhere.
- *
- * 2. <STRUCT> corresponding to a structure "struct <STRUCT>", that must be a
- * defined below. This structure must be an abstract definition of the
- * action. Its first member must have type "struct ovnact" and name
- * "ovnact". The structure must have a fixed length, that is, it may not
- * end with a flexible array member.
- */
-#define OVNACTS \
- OVNACT(OUTPUT, ovnact_null) \
- OVNACT(NEXT, ovnact_next) \
- OVNACT(LOAD, ovnact_load) \
- OVNACT(MOVE, ovnact_move) \
- OVNACT(EXCHANGE, ovnact_move) \
- OVNACT(DEC_TTL, ovnact_null) \
- OVNACT(CT_NEXT, ovnact_ct_next) \
- OVNACT(CT_COMMIT, ovnact_ct_commit) \
- OVNACT(CT_DNAT, ovnact_ct_nat) \
- OVNACT(CT_SNAT, ovnact_ct_nat) \
- OVNACT(CT_LB, ovnact_ct_lb) \
- OVNACT(CT_CLEAR, ovnact_null) \
- OVNACT(CLONE, ovnact_nest) \
- OVNACT(ARP, ovnact_nest) \
- OVNACT(ICMP4, ovnact_nest) \
- OVNACT(ICMP4_ERROR, ovnact_nest) \
- OVNACT(ICMP6, ovnact_nest) \
- OVNACT(IGMP, ovnact_null) \
- OVNACT(TCP_RESET, ovnact_nest) \
- OVNACT(ND_NA, ovnact_nest) \
- OVNACT(ND_NA_ROUTER, ovnact_nest) \
- OVNACT(GET_ARP, ovnact_get_mac_bind) \
- OVNACT(PUT_ARP, ovnact_put_mac_bind) \
- OVNACT(GET_ND, ovnact_get_mac_bind) \
- OVNACT(PUT_ND, ovnact_put_mac_bind) \
- OVNACT(PUT_DHCPV4_OPTS, ovnact_put_opts) \
- OVNACT(PUT_DHCPV6_OPTS, ovnact_put_opts) \
- OVNACT(SET_QUEUE, ovnact_set_queue) \
- OVNACT(DNS_LOOKUP, ovnact_dns_lookup) \
- OVNACT(LOG, ovnact_log) \
- OVNACT(PUT_ND_RA_OPTS, ovnact_put_opts) \
- OVNACT(ND_NS, ovnact_nest) \
- OVNACT(SET_METER, ovnact_set_meter) \
- OVNACT(OVNFIELD_LOAD, ovnact_load) \
- OVNACT(CHECK_PKT_LARGER, ovnact_check_pkt_larger) \
- OVNACT(TRIGGER_EVENT, ovnact_controller_event)
-
-/* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
-enum OVS_PACKED_ENUM ovnact_type {
-#define OVNACT(ENUM, STRUCT) OVNACT_##ENUM,
- OVNACTS
-#undef OVNACT
-};
-
-/* Define N_OVNACTS to the number of types of ovnacts. */
-enum {
-#define OVNACT(ENUM, STRUCT) + 1
- N_OVNACTS = OVNACTS
-#undef OVNACT
-};
-
-/* Header for an action.
- *
- * Each action is a structure "struct ovnact_*" that begins with "struct
- * ovnact", usually followed by other data that describes the action. Actions
- * are padded out to a multiple of OVNACT_ALIGNTO bytes in length.
- */
-struct ovnact {
- /* We want the space advantage of an 8-bit type here on every
- * implementation, without giving up the advantage of having a useful type
- * on implementations that support packed enums. */
-#ifdef HAVE_PACKED_ENUM
- enum ovnact_type type; /* OVNACT_*. */
-#else
- uint8_t type; /* OVNACT_* */
-#endif
- uint8_t pad; /* Pad to multiple of 16 bits. */
-
- uint16_t len; /* Length of the action, in bytes, including
- * struct ovnact, excluding padding. */
-};
-BUILD_ASSERT_DECL(sizeof(struct ovnact) == 4);
-
-/* Alignment. */
-#define OVNACT_ALIGNTO 8
-#define OVNACT_ALIGN(SIZE) ROUND_UP(SIZE, OVNACT_ALIGNTO)
-
-/* Returns the ovnact following 'ovnact'. */
-static inline struct ovnact *
-ovnact_next(const struct ovnact *ovnact)
-{
- return (void *) ((uint8_t *) ovnact + OVNACT_ALIGN(ovnact->len));
-}
-
-struct ovnact *ovnact_next_flattened(const struct ovnact *);
-
-static inline struct ovnact *
-ovnact_end(const struct ovnact *ovnacts, size_t ovnacts_len)
-{
- return (void *) ((uint8_t *) ovnacts + ovnacts_len);
-}
-
-/* Assigns POS to each ovnact, in turn, in the OVNACTS_LEN bytes of ovnacts
- * starting at OVNACTS. */
-#define OVNACT_FOR_EACH(POS, OVNACTS, OVNACTS_LEN) \
- for ((POS) = (OVNACTS); (POS) < ovnact_end(OVNACTS, OVNACTS_LEN); \
- (POS) = ovnact_next(POS))
-
-static inline int
-ovnacts_count(const struct ovnact *ovnacts, size_t ovnacts_len)
-{
- uint8_t n_ovnacts = 0;
- if (ovnacts) {
- const struct ovnact *a;
-
- OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
- n_ovnacts++;
- }
- }
- return n_ovnacts;
-}
-
-/* Action structure for each OVNACT_*. */
-
-/* Action structure for actions that do not have any extra data beyond the
- * action type. */
-struct ovnact_null {
- struct ovnact ovnact;
-};
-
-/* Logical pipeline in which a set of actions is executed. */
-enum ovnact_pipeline {
- OVNACT_P_INGRESS,
- OVNACT_P_EGRESS,
-};
-
-/* OVNACT_NEXT. */
-struct ovnact_next {
- struct ovnact ovnact;
-
- /* Arguments. */
- uint8_t ltable; /* Logical table ID of next table. */
- enum ovnact_pipeline pipeline; /* Pipeline of next table. */
-
- /* Information about the flow that the action is in. This does not affect
- * behavior, since the implementation of "next" doesn't depend on the
- * source table or pipeline. It does affect how ovnacts_format() prints
- * the action. */
- uint8_t src_ltable; /* Logical table ID of source table. */
- enum ovnact_pipeline src_pipeline; /* Pipeline of source table. */
-};
-
-/* OVNACT_LOAD. */
-struct ovnact_load {
- struct ovnact ovnact;
- struct expr_field dst;
- union expr_constant imm;
-};
-
-/* OVNACT_MOVE, OVNACT_EXCHANGE. */
-struct ovnact_move {
- struct ovnact ovnact;
- struct expr_field lhs;
- struct expr_field rhs;
-};
-
-/* OVNACT_CT_NEXT. */
-struct ovnact_ct_next {
- struct ovnact ovnact;
- uint8_t ltable; /* Logical table ID of next table. */
-};
-
-/* OVNACT_CT_COMMIT. */
-struct ovnact_ct_commit {
- struct ovnact ovnact;
- uint32_t ct_mark, ct_mark_mask;
- ovs_be128 ct_label, ct_label_mask;
-};
-
-/* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */
-struct ovnact_ct_nat {
- struct ovnact ovnact;
- ovs_be32 ip;
- uint8_t ltable; /* Logical table ID of next table. */
-};
-
-struct ovnact_ct_lb_dst {
- int family;
- union {
- struct in6_addr ipv6;
- ovs_be32 ipv4;
- };
- uint16_t port;
-};
-
-/* OVNACT_CT_LB. */
-struct ovnact_ct_lb {
- struct ovnact ovnact;
- struct ovnact_ct_lb_dst *dsts;
- size_t n_dsts;
- uint8_t ltable; /* Logical table ID of next table. */
-};
-
-/* OVNACT_ARP, OVNACT_ND_NA, OVNACT_CLONE. */
-struct ovnact_nest {
- struct ovnact ovnact;
- struct ovnact *nested;
- size_t nested_len;
-};
-
-/* OVNACT_GET_ARP, OVNACT_GET_ND. */
-struct ovnact_get_mac_bind {
- struct ovnact ovnact;
- struct expr_field port; /* Logical port name. */
- struct expr_field ip; /* 32-bit or 128-bit IP address. */
-};
-
-/* OVNACT_PUT_ARP, ONVACT_PUT_ND. */
-struct ovnact_put_mac_bind {
- struct ovnact ovnact;
- struct expr_field port; /* Logical port name. */
- struct expr_field ip; /* 32-bit or 128-bit IP address. */
- struct expr_field mac; /* 48-bit Ethernet address. */
-};
-
-struct ovnact_gen_option {
- const struct gen_opts_map *option;
- struct expr_constant_set value;
-};
-
-/* OVNACT_PUT_DHCPV4_OPTS, OVNACT_PUT_DHCPV6_OPTS. */
-struct ovnact_put_opts {
- struct ovnact ovnact;
- struct expr_field dst; /* 1-bit destination field. */
- struct ovnact_gen_option *options;
- size_t n_options;
-};
-
-/* Valid arguments to SET_QUEUE action.
- *
- * QDISC_MIN_QUEUE_ID is the default queue, so user-defined queues should
- * start at QDISC_MIN_QUEUE_ID+1. */
-#define QDISC_MIN_QUEUE_ID 0
-#define QDISC_MAX_QUEUE_ID 0xf000
-
-/* OVNACT_SET_QUEUE. */
-struct ovnact_set_queue {
- struct ovnact ovnact;
- uint16_t queue_id;
-};
-
-/* OVNACT_DNS_LOOKUP. */
-struct ovnact_dns_lookup {
- struct ovnact ovnact;
- struct expr_field dst; /* 1-bit destination field. */
-};
-
-/* OVNACT_LOG. */
-struct ovnact_log {
- struct ovnact ovnact;
- uint8_t verdict; /* One of LOG_VERDICT_*. */
- uint8_t severity; /* One of LOG_SEVERITY_*. */
- char *name;
- char *meter;
-};
-
-/* OVNACT_SET_METER. */
-struct ovnact_set_meter {
- struct ovnact ovnact;
- uint64_t rate; /* rate field, in kbps. */
- uint64_t burst; /* burst rate field, in kbps. */
-};
-
-/* OVNACT_CHECK_IP_PKT_LARGER. */
-struct ovnact_check_pkt_larger {
- struct ovnact ovnact;
- uint16_t pkt_len;
- struct expr_field dst; /* 1-bit destination field. */
-};
-
-/* OVNACT_EVENT. */
-struct ovnact_controller_event {
- struct ovnact ovnact;
- int event_type; /* controller event type */
- struct ovnact_gen_option *options;
- size_t n_options;
-};
-
-/* Internal use by the helpers below. */
-void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
-void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
-
-/* For each OVNACT_<ENUM> with a corresponding struct <STRUCT>, this defines
- * the following commonly useful functions:
- *
- * struct <STRUCT> *ovnact_put_<ENUM>(struct ofpbuf *ovnacts);
- *
- * Appends a new 'ovnact', of length OVNACT_<ENUM>_SIZE, to 'ovnacts',
- * initializes it with ovnact_init_<ENUM>(), and returns it. Also sets
- * 'ovnacts->header' to the returned action.
- *
- * struct <STRUCT> *ovnact_get_<ENUM>(const struct ovnact *ovnact);
- *
- * Returns 'ovnact' cast to "struct <STRUCT> *". 'ovnact->type' must be
- * OVNACT_<ENUM>.
- *
- * as well as the following more rarely useful definitions:
- *
- * void ovnact_init_<ENUM>(struct <STRUCT> *ovnact);
- *
- * Initializes the parts of 'ovnact' that identify it as having type
- * OVNACT_<ENUM> and length OVNACT_<ENUM>_SIZE and zeros the rest.
- *
- * <ENUM>_SIZE
- *
- * The size of the action structure, that is, sizeof(struct <STRUCT>)
- * rounded up to a multiple of OVNACT_ALIGNTO.
- */
-#define OVNACT(ENUM, STRUCT) \
- BUILD_ASSERT_DECL(offsetof(struct STRUCT, ovnact) == 0); \
- \
- enum { OVNACT_##ENUM##_SIZE = OVNACT_ALIGN(sizeof(struct STRUCT)) }; \
- \
- static inline struct STRUCT * \
- ovnact_get_##ENUM(const struct ovnact *ovnact) \
- { \
- ovs_assert(ovnact->type == OVNACT_##ENUM); \
- return ALIGNED_CAST(struct STRUCT *, ovnact); \
- } \
- \
- static inline struct STRUCT * \
- ovnact_put_##ENUM(struct ofpbuf *ovnacts) \
- { \
- return ovnact_put(ovnacts, OVNACT_##ENUM, \
- OVNACT_##ENUM##_SIZE); \
- } \
- \
- static inline void \
- ovnact_init_##ENUM(struct STRUCT *ovnact) \
- { \
- ovnact_init(&ovnact->ovnact, OVNACT_##ENUM, \
- OVNACT_##ENUM##_SIZE); \
- }
-OVNACTS
-#undef OVNACT
-
-enum action_opcode {
- /* "arp { ...actions... }".
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_ARP,
-
- /* "put_arp(port, ip, mac)"
- *
- * Arguments are passed through the packet metadata and data, as follows:
- *
- * MFF_REG0 = ip
- * MFF_LOG_INPORT = port
- * MFF_ETH_SRC = mac
- */
- ACTION_OPCODE_PUT_ARP,
-
- /* "result = put_dhcp_opts(offer_ip, option, ...)".
- *
- * Arguments follow the action_header, in this format:
- * - A 32-bit or 64-bit OXM header designating the result field.
- * - A 32-bit integer specifying a bit offset within the result field.
- * - The 32-bit DHCP offer IP.
- * - Any number of DHCP options.
- */
- ACTION_OPCODE_PUT_DHCP_OPTS,
-
- /* "nd_na { ...actions... }".
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_ND_NA,
-
- /* "put_nd(port, ip6, mac)"
- *
- * Arguments are passed through the packet metadata and data, as follows:
- *
- * MFF_XXREG0 = ip6
- * MFF_LOG_INPORT = port
- * MFF_ETH_SRC = mac
- */
- ACTION_OPCODE_PUT_ND,
-
- /* "result = put_dhcpv6_opts(option, ...)".
- *
- * Arguments follow the action_header, in this format:
- * - A 32-bit or 64-bit OXM header designating the result field.
- * - A 32-bit integer specifying a bit offset within the result field.
- * - Any number of DHCPv6 options.
- */
- ACTION_OPCODE_PUT_DHCPV6_OPTS,
-
- /* "result = dns_lookup()".
- * Arguments follow the action_header, in this format:
- * - A 32-bit or 64-bit OXM header designating the result field.
- * - A 32-bit integer specifying a bit offset within the result field.
- *
- */
- ACTION_OPCODE_DNS_LOOKUP,
-
- /* "log(arguments)".
- *
- * Arguments are as follows:
- * - An 8-bit verdict.
- * - An 8-bit severity.
- * - A variable length string containing the name.
- */
- ACTION_OPCODE_LOG,
-
- /* "result = put_nd_ra_opts(option, ...)".
- * Arguments follow the action_header, in this format:
- * - A 32-bit or 64-bit OXM header designating the result field.
- * - A 32-bit integer specifying a bit offset within the result field.
- * - Any number of ICMPv6 options.
- */
- ACTION_OPCODE_PUT_ND_RA_OPTS,
-
- /* "nd_ns { ...actions... }".
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_ND_NS,
-
- /* "icmp4 { ...actions... } and icmp6 { ...actions... }".
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_ICMP,
-
- /* "tcp_reset { ...actions... }".
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_TCP_RESET,
-
- /* "nd_na_router { ...actions... }" with rso flag 'ND_RSO_ROUTER' set.
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_ND_NA_ROUTER,
-
- /* MTU value (to put in the icmp4 header field - frag_mtu) follow the
- * action header. */
- ACTION_OPCODE_PUT_ICMP4_FRAG_MTU,
-
- /* "icmp4_error { ...actions... }".
- *
- * The actions, in OpenFlow 1.3 format, follow the action_header.
- */
- ACTION_OPCODE_ICMP4_ERROR,
-
- /* "trigger_event (event_type)" */
- ACTION_OPCODE_EVENT,
-
- /* "igmp".
- *
- * Snoop IGMP, learn the multicast participants
- */
- ACTION_OPCODE_IGMP,
-};
-
-/* Header. */
-struct action_header {
- ovs_be32 opcode; /* One of ACTION_OPCODE_* */
- uint8_t pad[4];
-};
-BUILD_ASSERT_DECL(sizeof(struct action_header) == 8);
-
-OVS_PACKED(
-struct ovnfield_act_header {
- ovs_be16 id; /* one of enum ovnfield_id. */
- ovs_be16 len; /* Length of the ovnfield data. */
-});
-
-struct ovnact_parse_params {
- /* A table of "struct expr_symbol"s to support (as one would provide to
- * expr_parse()). */
- const struct shash *symtab;
-
- /* hmap of 'struct gen_opts_map' to support 'put_dhcp_opts' action */
- const struct hmap *dhcp_opts;
-
- /* hmap of 'struct gen_opts_map' to support 'put_dhcpv6_opts' action */
- const struct hmap *dhcpv6_opts;
-
- /* hmap of 'struct gen_opts_map' to support 'put_nd_ra_opts' action */
- const struct hmap *nd_ra_opts;
-
- /* Array of hmap of 'struct gen_opts_map' to support 'trigger_event'
- * action */
- const struct controller_event_options *controller_event_opts;
-
- /* Each OVN flow exists in a logical table within a logical pipeline.
- * These parameters express this context for a set of OVN actions being
- * parsed:
- *
- * - 'n_tables' is the number of tables in the logical ingress and
- * egress pipelines, that is, "next" may specify a table less than
- * or equal to 'n_tables'. If 'n_tables' is 0 then "next" is
- * disallowed entirely.
- *
- * - 'cur_ltable' is the logical table of the current flow, within
- * 'pipeline'. If cur_ltable + 1 < n_tables, then this defines the
- * default table that "next" will jump to.
- *
- * - 'pipeline' is the logical pipeline. It is the default pipeline to
- * which 'next' will jump. If 'pipeline' is OVNACT_P_EGRESS, then
- * 'next' will also be able to jump into the ingress pipeline, but
- * the reverse is not true. */
- enum ovnact_pipeline pipeline; /* Logical pipeline. */
- uint8_t n_tables; /* Number of logical flow tables. */
- uint8_t cur_ltable; /* 0 <= cur_ltable < n_tables. */
-};
-
-bool ovnacts_parse(struct lexer *, const struct ovnact_parse_params *,
- struct ofpbuf *ovnacts, struct expr **prereqsp);
-char *ovnacts_parse_string(const char *s, const struct ovnact_parse_params *,
- struct ofpbuf *ovnacts, struct expr **prereqsp)
- OVS_WARN_UNUSED_RESULT;
-
-void ovnacts_format(const struct ovnact[], size_t ovnacts_len, struct ds *);
-
-struct ovnact_encode_params {
- /* Looks up logical port 'port_name'. If found, stores its port number in
- * '*portp' and returns true; otherwise, returns false. */
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp);
- const void *aux;
-
- /* 'true' if the flow is for a switch. */
- bool is_switch;
-
- /* A struct to figure out the group_id for group actions. */
- struct ovn_extend_table *group_table;
-
- /* A struct to figure out the meter_id for meter actions. */
- struct ovn_extend_table *meter_table;
-
- /* The logical flow uuid that drove this action. */
- struct uuid lflow_uuid;
-
- /* OVN maps each logical flow table (ltable), one-to-one, onto a physical
- * OpenFlow flow table (ptable). A number of parameters describe this
- * mapping and data related to flow tables:
- *
- * - 'pipeline' is the logical pipeline in which the actions are
- * executing.
- *
- * - 'ingress_ptable' is the OpenFlow table that corresponds to OVN
- * ingress table 0.
- *
- * - 'egress_ptable' is the OpenFlow table that corresponds to OVN
- * egress table 0.
- *
- * - 'output_ptable' should be the OpenFlow table to which the logical
- * "output" action will resubmit.
- *
- * - 'mac_bind_ptable' should be the OpenFlow table used to track MAC
- * bindings. */
- enum ovnact_pipeline pipeline; /* Logical pipeline. */
- uint8_t ingress_ptable; /* First OpenFlow ingress table. */
- uint8_t egress_ptable; /* First OpenFlow egress table. */
- uint8_t output_ptable; /* OpenFlow table for 'output' to resubmit. */
- uint8_t mac_bind_ptable; /* OpenFlow table for 'get_arp'/'get_nd' to
- resubmit. */
-};
-
-void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
- const struct ovnact_encode_params *,
- struct ofpbuf *ofpacts);
-
-void ovnacts_free(struct ovnact[], size_t ovnacts_len);
-
-#endif /* ovn/actions.h */
diff --git a/include/ovn/automake.mk b/include/ovn/automake.mk
deleted file mode 100644
index 54b0e2c0e..000000000
--- a/include/ovn/automake.mk
+++ /dev/null
@@ -1,6 +0,0 @@
-ovnincludedir = $(includedir)/ovn
-ovninclude_HEADERS = \
- include/ovn/actions.h \
- include/ovn/expr.h \
- include/ovn/lex.h \
- include/ovn/logical-fields.h
diff --git a/include/ovn/expr.h b/include/ovn/expr.h
deleted file mode 100644
index 22f633e57..000000000
--- a/include/ovn/expr.h
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_EXPR_H
-#define OVN_EXPR_H 1
-
-/* OVN matching expression tree
- * ============================
- *
- * The data structures here form an abstract expression tree for matching
- * expressions in OVN.
- *
- * The abstract syntax tree representation of a matching expression is one of:
- *
- * - A Boolean literal ("true" or "false").
- *
- * - A comparison of a field (or part of a field) against a constant
- * with one of the operators == != < <= > >=.
- *
- * - The logical AND or OR of two or more matching expressions.
- *
- * Literals and comparisons are called "terminal" nodes, logical AND and OR
- * nodes are "nonterminal" nodes.
- *
- * The syntax for expressions includes a few other concepts that are not part
- * of the abstract syntax tree. In these examples, x is a field, a, b, and c
- * are constants, and e1 and e2 are arbitrary expressions:
- *
- * - Logical NOT. The parser implements NOT by inverting the sense of the
- * operand: !(x == a) becomes x != a, !(e1 && e2) becomes !e1 || !e2, and
- * so on.
- *
- * - Set membership. The parser translates x == {a, b, c} into
- * x == a || x == b || x == c.
- *
- * - Reversed comparisons. The parser translates a < x into x > a.
- *
- * - Range expressions. The parser translates a < x < b into
- * x > a && x < b.
- */
-
-#include "classifier.h"
-#include "lex.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/list.h"
-#include "openvswitch/match.h"
-#include "openvswitch/meta-flow.h"
-#include "logical-fields.h"
-
-struct ds;
-struct expr;
-struct flow;
-struct ofpbuf;
-struct shash;
-struct simap;
-struct sset;
-
-/* "Measurement level" of a field. See "Level of Measurement" in the large
- * comment on struct expr_symbol below for more information. */
-enum expr_level {
- EXPR_L_NOMINAL,
-
- /* Boolean values are nominal, however because of their simple nature OVN
- * can allow both equality and inequality tests on them. */
- EXPR_L_BOOLEAN,
-
- /* Ordinal values can at least be ordered on a scale. OVN allows equality
- * and inequality and relational tests on ordinal values. These are the
- * fields on which OVS allows bitwise matching. */
- EXPR_L_ORDINAL
-};
-
-const char *expr_level_to_string(enum expr_level);
-
-/* A symbol.
- *
- *
- * Name
- * ====
- *
- * Every symbol must have a name. To be useful, the name must satisfy the
- * lexer's syntax for an identifier.
- *
- *
- * Width
- * =====
- *
- * Every symbol has a width. For integer symbols, this is the number of bits
- * in the value; for string symbols, this is 0.
- *
- *
- * Types
- * =====
- *
- * There are three kinds of symbols:
- *
- * Fields:
- *
- * One might, for example, define a field named "vlan.tci" to refer to
- * MFF_VLAN_TCI. 'field' specifies the field.
- *
- * 'parent' and 'predicate' are NULL, and 'parent_ofs' is 0.
- *
- * Integer fields can be nominal or ordinal (see below). String fields are
- * always nominal.
- *
- * Subfields:
- *
- * 'parent' specifies the field (which may itself be a subfield,
- * recursively) in which the subfield is embedded, and 'parent_ofs' a
- * bitwise offset from the least-significant bit of the parent. The
- * subfield can contain a subset of the bits of the parent or all of them
- * (in the latter case the subfield is really just a synonym for the
- * parent).
- *
- * 'field' and 'predicate' are NULL.
- *
- * Only ordinal fields (see below) may have subfields, and subfields are
- * always ordinal.
- *
- * Predicates:
- *
- * A predicate is an arbitrary Boolean expression that can be used in an
- * expression much like a 1-bit field. 'predicate' specifies the Boolean
- * expression, e.g. "ip4" might expand to "eth.type == 0x800". The
- * epxression might refer to other predicates, e.g. "icmp4" might expand to
- * "ip4 && ip4.proto == 1".
- *
- * 'field' and 'parent' are NULL, and 'parent_ofs' is 0.
- *
- * A predicate that refers to any nominal field or predicate (see below) is
- * nominal; other predicates have Boolean level of measurement.
- *
- *
- * Level of Measurement
- * ====================
- *
- * See http://en.wikipedia.org/wiki/Level_of_measurement for the statistical
- * concept on which this classification is based. There are three levels:
- *
- * Ordinal:
- *
- * In statistics, ordinal values can be ordered on a scale. Here, we
- * consider a field (or subfield) to be ordinal if its bits can be examined
- * individually. This is true for the OpenFlow fields that OpenFlow or
- * Open vSwitch makes "maskable".
- *
- * OVN supports all the usual arithmetic relations (== != < <= > >=) on
- * ordinal fields and their subfields, because all of these can be
- * implemented as collections of bitwise tests.
- *
- * Nominal:
- *
- * In statistics, nominal values cannot be usefully compared except for
- * equality. This is true of OpenFlow port numbers, Ethernet types, and IP
- * protocols are examples: all of these are just identifiers assigned
- * arbitrarily with no deeper meaning. In OpenFlow and Open vSwitch, bits
- * in these fields generally aren't individually addressable.
- *
- * OVN only supports arithmetic tests for equality on nominal fields,
- * because OpenFlow and Open vSwitch provide no way for a flow to
- * efficiently implement other comparisons on them. (A test for inequality
- * can be sort of built out of two flows with different priorities, but OVN
- * matching expressions always generate flows with a single priority.)
- *
- * String fields are always nominal.
- *
- * Boolean:
- *
- * A nominal field that has only two values, 0 and 1, is somewhat
- * exceptional, since it is easy to support both equality and inequality
- * tests on such a field: either one can be implemented as a test for 0 or
- * 1.
- *
- * Only predicates (see above) have a Boolean level of measurement.
- *
- * This isn't a standard level of measurement.
- *
- *
- * Prerequisites
- * =============
- *
- * Any symbol can have prerequisites, which are specified as a string giving an
- * additional expression that must be true whenever the symbol is referenced.
- * For example, the "icmp4.type" symbol might have prerequisite "icmp4", which
- * would cause an expression "icmp4.type == 0" to be interpreted as "icmp4.type
- * == 0 && icmp4", which would in turn expand to "icmp4.type == 0 && eth.type
- * == 0x800 && ip4.proto == 1" (assuming "icmp4" is a predicate defined as
- * suggested under "Types" above).
- *
- *
- * Crossproducting
- * ===============
- *
- * Ordinarily OVN is willing to consider using any field as a dimension in the
- * Open vSwitch "conjunctive match" extension (see ovs-ofctl(8)). However,
- * some fields can't actually be used that way because they are necessary as
- * prerequisites. For example, from an expression like "tcp.src == {1,2,3}
- * && tcp.dst == {4,5,6}", OVN might naturally generate flows like this:
- *
- * conj_id=1,actions=...
- * ip,actions=conjunction(1,1/3)
- * ip6,actions=conjunction(1,1/3)
- * tp_src=1,actions=conjunction(1,2/3)
- * tp_src=2,actions=conjunction(1,2/3)
- * tp_src=3,actions=conjunction(1,2/3)
- * tp_dst=4,actions=conjunction(1,3/3)
- * tp_dst=5,actions=conjunction(1,3/3)
- * tp_dst=6,actions=conjunction(1,3/3)
- *
- * but that's not valid because any flow that matches on tp_src or tp_dst must
- * also match on either ip or ip6. Thus, one would mark eth.type as "must
- * crossproduct", to force generating flows like this:
- *
- * conj_id=1,actions=...
- * ip,tp_src=1,actions=conjunction(1,1/2)
- * ip,tp_src=2,actions=conjunction(1,1/2)
- * ip,tp_src=3,actions=conjunction(1,1/2)
- * ip6,tp_src=1,actions=conjunction(1,1/2)
- * ip6,tp_src=2,actions=conjunction(1,1/2)
- * ip6,tp_src=3,actions=conjunction(1,1/2)
- * ip,tp_dst=4,actions=conjunction(1,2/2)
- * ip,tp_dst=5,actions=conjunction(1,2/2)
- * ip,tp_dst=6,actions=conjunction(1,2/2)
- * ip6,tp_dst=4,actions=conjunction(1,2/2)
- * ip6,tp_dst=5,actions=conjunction(1,2/2)
- * ip6,tp_dst=6,actions=conjunction(1,2/2)
- *
- * which are acceptable.
- */
-struct expr_symbol {
- char *name;
- int width;
-
- const struct mf_field *field; /* Fields only, otherwise NULL. */
- const struct ovn_field *ovn_field; /* OVN Fields only, otherwise NULL. */
- const struct expr_symbol *parent; /* Subfields only, otherwise NULL. */
- int parent_ofs; /* Subfields only, otherwise 0. */
- char *predicate; /* Predicates only, otherwise NULL. */
-
- enum expr_level level;
-
- char *prereqs;
- bool must_crossproduct;
- bool rw;
-};
-
-void expr_symbol_format(const struct expr_symbol *, struct ds *);
-
-/* A reference to a symbol or a subfield of a symbol.
- *
- * For string fields, ofs and n_bits are 0. */
-struct expr_field {
- const struct expr_symbol *symbol; /* The symbol. */
- int ofs; /* Starting bit offset. */
- int n_bits; /* Number of bits. */
-};
-
-bool expr_field_parse(struct lexer *, const struct shash *symtab,
- struct expr_field *, struct expr **prereqsp);
-void expr_field_format(const struct expr_field *, struct ds *);
-
-struct expr_symbol *expr_symtab_add_field(struct shash *symtab,
- const char *name, enum mf_field_id,
- const char *prereqs,
- bool must_crossproduct);
-struct expr_symbol *expr_symtab_add_subfield(struct shash *symtab,
- const char *name,
- const char *prereqs,
- const char *subfield);
-struct expr_symbol *expr_symtab_add_string(struct shash *symtab,
- const char *name, enum mf_field_id,
- const char *prereqs);
-struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab,
- const char *name,
- const char *expansion);
-struct expr_symbol *expr_symtab_add_ovn_field(struct shash *symtab,
- const char *name,
- enum ovn_field_id id);
-void expr_symtab_destroy(struct shash *symtab);
-
-/* Expression type. */
-enum expr_type {
- EXPR_T_CMP, /* Compare symbol with constant. */
- EXPR_T_AND, /* Logical AND of 2 or more subexpressions. */
- EXPR_T_OR, /* Logical OR of 2 or more subexpressions. */
- EXPR_T_BOOLEAN, /* True or false constant. */
- EXPR_T_CONDITION, /* Conditional to be evaluated in the
- * controller during expr_simplify(),
- * prior to constructing OpenFlow matches. */
-};
-
-/* Expression condition type. */
-enum expr_cond_type {
- EXPR_COND_CHASSIS_RESIDENT, /* Check if specified logical port name is
- * resident on the controller chassis. */
-};
-
-/* Relational operator. */
-enum expr_relop {
- EXPR_R_EQ, /* == */
- EXPR_R_NE, /* != */
- EXPR_R_LT, /* < */
- EXPR_R_LE, /* <= */
- EXPR_R_GT, /* > */
- EXPR_R_GE, /* >= */
-};
-const char *expr_relop_to_string(enum expr_relop);
-bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop);
-
-/* An abstract syntax tree for a matching expression.
- *
- * The expression code maintains and relies on a few important invariants:
- *
- * - An EXPR_T_AND or EXPR_T_OR node never has a child of the same type.
- * (Any such children could be merged into their parent.) A node may
- * have grandchildren of its own type.
- *
- * As a consequence, every nonterminal node at the same distance from the
- * root has the same type.
- *
- * - EXPR_T_AND and EXPR_T_OR nodes must have at least two children.
- *
- * - An EXPR_T_CMP node always has a nonzero mask, and never has a 1-bit
- * in its value in a position where the mask is a 0-bit.
- *
- * The expr_honors_invariants() function can check invariants. */
-struct expr {
- struct ovs_list node; /* In parent EXPR_T_AND or EXPR_T_OR if any. */
- enum expr_type type; /* Expression type. */
-
- union {
- /* EXPR_T_CMP.
- *
- * The symbol is on the left, e.g. "field < constant". */
- struct {
- const struct expr_symbol *symbol;
- enum expr_relop relop;
-
- union {
- char *string;
- struct {
- union mf_subvalue value;
- union mf_subvalue mask;
- };
- };
- } cmp;
-
- /* EXPR_T_AND, EXPR_T_OR. */
- struct ovs_list andor;
-
- /* EXPR_T_BOOLEAN. */
- bool boolean;
-
- /* EXPR_T_CONDITION. */
- struct {
- enum expr_cond_type type;
- bool not;
- /* XXX Should arguments for conditions be generic? */
- char *string;
- } cond;
- };
-};
-
-struct expr *expr_create_boolean(bool b);
-struct expr *expr_create_andor(enum expr_type);
-struct expr *expr_combine(enum expr_type, struct expr *a, struct expr *b);
-
-static inline struct expr *
-expr_from_node(const struct ovs_list *node)
-{
- return CONTAINER_OF(node, struct expr, node);
-}
-
-void expr_format(const struct expr *, struct ds *);
-void expr_print(const struct expr *);
-struct expr *expr_parse(struct lexer *, const struct shash *symtab,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- struct sset *addr_sets_ref);
-struct expr *expr_parse_string(const char *, const struct shash *symtab,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- struct sset *addr_sets_ref,
- char **errorp);
-
-struct expr *expr_clone(struct expr *);
-void expr_destroy(struct expr *);
-
-struct expr *expr_annotate(struct expr *, const struct shash *symtab,
- char **errorp);
-struct expr *expr_simplify(struct expr *,
- bool (*is_chassis_resident)(const void *c_aux,
- const char *port_name),
- const void *c_aux);
-struct expr *expr_normalize(struct expr *);
-
-bool expr_honors_invariants(const struct expr *);
-bool expr_is_simplified(const struct expr *);
-bool expr_is_normalized(const struct expr *);
-
-char *expr_parse_microflow(const char *, const struct shash *symtab,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- bool (*lookup_port)(const void *aux,
- const char *port_name,
- unsigned int *portp),
- const void *aux, struct flow *uflow)
- OVS_WARN_UNUSED_RESULT;
-
-bool expr_evaluate(const struct expr *, const struct flow *uflow,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux);
-
-/* Converting expressions to OpenFlow flows. */
-
-/* An OpenFlow match generated from a Boolean expression. See
- * expr_to_matches() for more information. */
-struct expr_match {
- struct hmap_node hmap_node;
- struct match match;
- struct cls_conjunction *conjunctions;
- size_t n, allocated;
-};
-
-uint32_t expr_to_matches(const struct expr *,
- bool (*lookup_port)(const void *aux,
- const char *port_name,
- unsigned int *portp),
- const void *aux,
- struct hmap *matches);
-void expr_matches_destroy(struct hmap *matches);
-void expr_matches_print(const struct hmap *matches, FILE *);
-
-/* Action parsing helper. */
-
-char *expr_type_check(const struct expr_field *, int n_bits, bool rw)
- OVS_WARN_UNUSED_RESULT;
-struct mf_subfield expr_resolve_field(const struct expr_field *);
-
-/* Type of a "union expr_constant" or "struct expr_constant_set". */
-enum expr_constant_type {
- EXPR_C_INTEGER,
- EXPR_C_STRING
-};
-
-/* A string or integer constant (one must know which from context). */
-union expr_constant {
- /* Integer constant.
- *
- * The width of a constant isn't always clear, e.g. if you write "1",
- * there's no way to tell whether you mean for that to be a 1-bit constant
- * or a 128-bit constant or somewhere in between. */
- struct {
- union mf_subvalue value;
- union mf_subvalue mask; /* Only initialized if 'masked'. */
- bool masked;
-
- enum lex_format format; /* From the constant's lex_token. */
- };
-
- /* Null-terminated string constant. */
- char *string;
-};
-
-bool expr_constant_parse(struct lexer *, const struct expr_field *,
- union expr_constant *);
-void expr_constant_format(const union expr_constant *,
- enum expr_constant_type, struct ds *);
-void expr_constant_destroy(const union expr_constant *,
- enum expr_constant_type);
-
-/* A collection of "union expr_constant"s of the same type. */
-struct expr_constant_set {
- union expr_constant *values; /* Constants. */
- size_t n_values; /* Number of constants. */
- enum expr_constant_type type; /* Type of the constants. */
- bool in_curlies; /* Whether the constants were in {}. */
-};
-
-bool expr_constant_set_parse(struct lexer *, struct expr_constant_set *);
-void expr_constant_set_format(const struct expr_constant_set *, struct ds *);
-void expr_constant_set_destroy(struct expr_constant_set *cs);
-
-
-/* Constant sets.
- *
- * For example, instead of referring to a set of IP addresses as:
- * {addr1, addr2, ..., addrN}
- * You can register a set of values and refer to them as:
- * $name
- *
- * If convert_to_integer is true, the set must contain
- * integer/masked-integer values. The values that don't qualify
- * are ignored.
- */
-
-void expr_const_sets_add(struct shash *const_sets, const char *name,
- const char * const *values, size_t n_values,
- bool convert_to_integer);
-void expr_const_sets_remove(struct shash *const_sets, const char *name);
-void expr_const_sets_destroy(struct shash *const_sets);
-
-#endif /* ovn/expr.h */
diff --git a/include/ovn/lex.h b/include/ovn/lex.h
deleted file mode 100644
index 8d5585766..000000000
--- a/include/ovn/lex.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_LEX_H
-#define OVN_LEX_H 1
-
-/* OVN lexical analyzer
- * ====================
- *
- * This is a simple lexical analyzer (or tokenizer) for OVN match expressions
- * and ACLs. */
-
-#include "openvswitch/meta-flow.h"
-
-struct ds;
-
-/* Token type. */
-enum lex_type {
- LEX_T_END, /* end of input */
-
- /* Tokens with auxiliary data. */
- LEX_T_ID, /* foo */
- LEX_T_STRING, /* "foo" */
- LEX_T_INTEGER, /* 12345 or 1.2.3.4 or ::1 or 01:02:03:04:05 */
- LEX_T_MASKED_INTEGER, /* 12345/10 or 1.2.0.0/16 or ::2/127 or... */
- LEX_T_MACRO, /* $NAME */
- LEX_T_PORT_GROUP, /* @NAME */
- LEX_T_ERROR, /* invalid input */
-
- /* Bare tokens. */
- LEX_T_LPAREN, /* ( */
- LEX_T_RPAREN, /* ) */
- LEX_T_LCURLY, /* { */
- LEX_T_RCURLY, /* } */
- LEX_T_LSQUARE, /* [ */
- LEX_T_RSQUARE, /* ] */
- LEX_T_EQ, /* == */
- LEX_T_NE, /* != */
- LEX_T_LT, /* < */
- LEX_T_LE, /* <= */
- LEX_T_GT, /* > */
- LEX_T_GE, /* >= */
- LEX_T_LOG_NOT, /* ! */
- LEX_T_LOG_AND, /* && */
- LEX_T_LOG_OR, /* || */
- LEX_T_ELLIPSIS, /* .. */
- LEX_T_COMMA, /* , */
- LEX_T_SEMICOLON, /* ; */
- LEX_T_EQUALS, /* = */
- LEX_T_EXCHANGE, /* <-> */
- LEX_T_DECREMENT, /* -- */
- LEX_T_COLON, /* : */
-};
-
-/* Subtype for LEX_T_INTEGER and LEX_T_MASKED_INTEGER tokens.
- *
- * These do not change the semantics of a token; instead, they determine the
- * format used when a token is serialized back to a text form. That's
- * important because 3232268289 is meaningless to a human whereas 192.168.128.1
- * has some actual significance. */
-enum lex_format {
- LEX_F_DECIMAL,
- LEX_F_HEXADECIMAL,
- LEX_F_IPV4,
- LEX_F_IPV6,
- LEX_F_ETHERNET,
-};
-const char *lex_format_to_string(enum lex_format);
-
-/* A token. */
-struct lex_token {
- /* One of LEX_*. */
- enum lex_type type;
-
- /* Meaningful for LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only.
- * For these token types, 's' may point to 'buffer'; otherwise, it points
- * to malloc()ed memory owned by the token.
- *
- * Must be NULL for other token types.
- *
- * For LEX_T_MACRO, 's' does not include the leading $. */
- char *s;
-
- /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
- enum lex_format format;
-
- union {
- /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
- struct {
- union mf_subvalue value; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER. */
- union mf_subvalue mask; /* LEX_T_MASKED_INTEGER only. */
- };
-
- /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only. */
- char buffer[256];
- };
-};
-
-void lex_token_init(struct lex_token *);
-void lex_token_destroy(struct lex_token *);
-void lex_token_swap(struct lex_token *, struct lex_token *);
-void lex_token_strcpy(struct lex_token *, const char *s, size_t length);
-void lex_token_strset(struct lex_token *, char *s);
-void lex_token_vsprintf(struct lex_token *, const char *format, va_list args);
-
-void lex_token_format(const struct lex_token *, struct ds *);
-const char *lex_token_parse(struct lex_token *, const char *input,
- const char **startp);
-
-/* A lexical analyzer. */
-struct lexer {
- const char *input; /* Remaining input (not owned by lexer). */
- const char *start; /* Start of current token in 'input'. */
- struct lex_token token; /* Current token (owned by lexer). */
- char *error; /* Error message, if any (owned by lexer). */
-};
-
-void lexer_init(struct lexer *, const char *input);
-void lexer_destroy(struct lexer *);
-
-enum lex_type lexer_get(struct lexer *);
-enum lex_type lexer_lookahead(const struct lexer *);
-bool lexer_match(struct lexer *, enum lex_type);
-bool lexer_force_match(struct lexer *, enum lex_type);
-bool lexer_match_id(struct lexer *, const char *id);
-bool lexer_is_int(const struct lexer *);
-bool lexer_get_int(struct lexer *, int *value);
-bool lexer_force_int(struct lexer *, int *value);
-
-bool lexer_force_end(struct lexer *);
-
-void lexer_error(struct lexer *, const char *message, ...)
- OVS_PRINTF_FORMAT(2, 3);
-void lexer_syntax_error(struct lexer *, const char *message, ...)
- OVS_PRINTF_FORMAT(2, 3);
-
-char *lexer_steal_error(struct lexer *);
-
-#endif /* ovn/lex.h */
diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
deleted file mode 100644
index 9bac8e027..000000000
--- a/include/ovn/logical-fields.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_LOGICAL_FIELDS_H
-#define OVN_LOGICAL_FIELDS_H 1
-
-#include "openvswitch/meta-flow.h"
-
-struct shash;
-
-enum ovn_controller_event {
- OVN_EVENT_EMPTY_LB_BACKENDS = 0,
- OVN_EVENT_MAX,
-};
-
-/* Logical fields.
- *
- * These values are documented in ovn-architecture(7), please update the
- * documentation if you change any of them. */
-#define MFF_LOG_DATAPATH MFF_METADATA /* Logical datapath (64 bits). */
-#define MFF_LOG_FLAGS MFF_REG10 /* One of MLF_* (32 bits). */
-#define MFF_LOG_DNAT_ZONE MFF_REG11 /* conntrack dnat zone for gateway router
- * (32 bits). */
-#define MFF_LOG_SNAT_ZONE MFF_REG12 /* conntrack snat zone for gateway router
- * (32 bits). */
-#define MFF_LOG_CT_ZONE MFF_REG13 /* Logical conntrack zone for lports
- * (32 bits). */
-#define MFF_LOG_INPORT MFF_REG14 /* Logical input port (32 bits). */
-#define MFF_LOG_OUTPORT MFF_REG15 /* Logical output port (32 bits). */
-
-/* Logical registers.
- *
- * Make sure these don't overlap with the logical fields! */
-#define MFF_LOG_REG0 MFF_REG0
-#define MFF_N_LOG_REGS 10
-
-void ovn_init_symtab(struct shash *symtab);
-
-/* MFF_LOG_FLAGS_REG bit assignments */
-enum mff_log_flags_bits {
- MLF_ALLOW_LOOPBACK_BIT = 0,
- MLF_RCV_FROM_VXLAN_BIT = 1,
- MLF_FORCE_SNAT_FOR_DNAT_BIT = 2,
- MLF_FORCE_SNAT_FOR_LB_BIT = 3,
- MLF_LOCAL_ONLY_BIT = 4,
- MLF_NESTED_CONTAINER_BIT = 5,
-};
-
-/* MFF_LOG_FLAGS_REG flag assignments */
-enum mff_log_flags {
- /* Allow outputting back to inport. */
- MLF_ALLOW_LOOPBACK = (1 << MLF_ALLOW_LOOPBACK_BIT),
-
- /* Indicate that a packet was received from a VXLAN tunnel to
- * compensate for the lack of egress port information available in
- * VXLAN encapsulation. Egress port information is available for
- * Geneve and STT tunnel types. */
- MLF_RCV_FROM_VXLAN = (1 << MLF_RCV_FROM_VXLAN_BIT),
-
- /* Indicate that a packet needs a force SNAT in the gateway router when
- * DNAT has taken place. */
- MLF_FORCE_SNAT_FOR_DNAT = (1 << MLF_FORCE_SNAT_FOR_DNAT_BIT),
-
- /* Indicate that a packet needs a force SNAT in the gateway router when
- * load-balancing has taken place. */
- MLF_FORCE_SNAT_FOR_LB = (1 << MLF_FORCE_SNAT_FOR_LB_BIT),
-
- /* Indicate that a packet that should be distributed across multiple
- * hypervisors should instead only be output to local targets
- */
- MLF_LOCAL_ONLY = (1 << MLF_LOCAL_ONLY_BIT),
-
- /* Indicate that a packet was received from a nested container. */
- MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT),
-};
-
-/* OVN logical fields
- * ===================
- * These are the fields which OVN supports modifying which gets translated
- * to OFFlow controller action.
- *
- * OpenvSwitch doesn't support modifying these fields yet. If a field is
- * supported later by OpenvSwitch, it can be deleted from here.
- */
-
-enum ovn_field_id {
- /*
- * Name: "icmp4.frag_mtu" -
- * Type: be16
- * Description: Sets the low-order 16 bits of the ICMP4 header field
- * (that is labelled "unused" in the ICMP specification) of the ICMP4
- * packet as per the RFC 1191.
- */
- OVN_ICMP4_FRAG_MTU,
-
- OVN_FIELD_N_IDS
-};
-
-struct ovn_field {
- enum ovn_field_id id;
- const char *name;
- unsigned int n_bytes; /* Width of the field in bytes. */
- unsigned int n_bits; /* Number of significant bits in field. */
-};
-
-static inline const struct ovn_field *
-ovn_field_from_id(enum ovn_field_id id)
-{
- extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS];
- ovs_assert((unsigned int) id < OVN_FIELD_N_IDS);
- return &ovn_fields[id];
-}
-
-const char *event_to_string(enum ovn_controller_event event);
-int string_to_event(const char *s);
-const struct ovn_field *ovn_field_from_name(const char *name);
-void ovn_destroy_ovnfields(void);
-#endif /* ovn/lib/logical-fields.h */
diff --git a/lib/db-ctl-base.xml b/lib/db-ctl-base.xml
index a5fcc901c..10124c3ad 100644
--- a/lib/db-ctl-base.xml
+++ b/lib/db-ctl-base.xml
@@ -40,8 +40,8 @@
<dd>
Either a universally unique identifier in the style of RFC 4122,
e.g. <code>f81d4fae-7dec-11d0-a765-00a0c91e6bf6</code>, or an <code>@</code><var>name</var>
- defined by a <code>get</code> or <code>create</code> command within the same <code>ovn-nbctl</code>
- invocation.
+ defined by a <code>get</code> or <code>create</code> command within the
+ same <code>ovs-vsctl</code> invocation.
</dd>
</dl>
@@ -177,7 +177,7 @@
</p>
<p>
- The UUIDs shown for rows created in the same <code>ovn-nbctl</code>
+ The UUIDs shown for rows created in the same <code>ovs-vsctl</code>
invocation will be wrong.
</p>
@@ -199,7 +199,7 @@
</p>
<p>
If <code>@</code><var>name</var> is specified, then the UUID for <var>record</var> may be
- referred to by that name later in the same <code>ovn-nbctl</code>
+ referred to by that name later in the same <code>ovs-vsctl</code>
invocation in contexts where a UUID is expected.
</p>
<p>
@@ -379,8 +379,8 @@
</dl>
<p>
Consider specifying <code>--timeout=0</code> along with
- <code>--wait-until</code>, to prevent <code>ovn-nbctl</code> from terminating
- after waiting only at most 5 seconds.
+ <code>--wait-until</code>, to prevent <code>ovs-vsctl</code> from
+ terminating after waiting only at most 5 seconds.
</p>
</dd>
diff --git a/manpages.mk b/manpages.mk
index 5f43aa387..a66d109e3 100644
--- a/manpages.mk
+++ b/manpages.mk
@@ -1,15 +1,5 @@
# Generated automatically -- do not modify! -*- buffer-read-only: t -*-
-ovn/utilities/ovn-detrace.1: \
- ovn/utilities/ovn-detrace.1.in \
- lib/common-syn.man \
- lib/common.man \
- lib/ovs.tmac
-ovn/utilities/ovn-detrace.1.in:
-lib/common-syn.man:
-lib/common.man:
-lib/ovs.tmac:
-
ovn/utilities/ovn-sbctl.8: \
ovn/utilities/ovn-sbctl.8.in \
lib/common.man \
diff --git a/ovn/TODO.rst b/ovn/TODO.rst
deleted file mode 100644
index 33489174f..000000000
--- a/ovn/TODO.rst
+++ /dev/null
@@ -1,147 +0,0 @@
-..
- Licensed under the Apache License, Version 2.0 (the "License"); you may
- not use this file except in compliance with the License. You may obtain
- a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
-
- Convention for heading levels in Open vSwitch documentation:
-
- ======= Heading 0 (reserved for the title in a document)
- ------- Heading 1
- ~~~~~~~ Heading 2
- +++++++ Heading 3
- ''''''' Heading 4
-
- Avoid deeper levels because they do not render well.
-
-==============
-OVN To-do List
-==============
-
-* Get incremental updates in ovn-controller and ovn-northd in some
- sensible way.
-
-* Live migration.
-
- Russell Bryant: "When you're ready to have the destination take over, you
- have to remove the iface-id from the source and add it at the destination and
- I think it'd typically be configured on both ends, since it's a clone of the
- source VM (and it's config)."
-
-* VLAN trunk ports.
-
- Russell Bryant: "Today that would require creating 4096 ports for the VM and
- attach to 4096 OVN networks, so doable, but not quite ideal."
-
-* Service function chaining.
-
-* MAC learning.
-
- Han Zhou: "To support VMs that hosts workloads with their own macs, e.g.
- containers, if not using OVN native container support."
-
-* Finish up ARP/ND support: re-checking bindings, expiring bindings.
-
-* Hitless upgrade, especially for data plane.
-
-* Use OpenFlow "bundles" for transactional data plane updates.
-
-* Dynamic IP to MAC binding enhancements.
-
- OVN has basic support for establishing IP to MAC bindings dynamically, using
- ARP.
-
- * Ratelimiting.
-
- From casual observation, Linux appears to generate at most one ARP per
- second per destination.
-
- This might be supported by adding a new OVN logical action for
- rate-limiting.
-
- * Tracking queries
-
- It's probably best to only record in the database responses to queries
- actually issued by an L3 logical router, so somehow they have to be
- tracked, probably by putting a tentative binding without a MAC address
- into the database.
-
- * Renewal and expiration.
-
- Something needs to make sure that bindings remain valid and expire those
- that become stale.
-
- One way to do this might be to add some support for time to the database
- server itself.
-
- * Table size limiting.
-
- The table of MAC bindings must not be allowed to grow unreasonably large.
-
- * MTU handling (fragmentation on output)
-
-* ovsdb-server
-
- ovsdb-server should have adequate features for OVN but it probably needs work
- for scale and possibly for availability as deployments grow. Here are some
- thoughts.
-
- * Multithreading.
-
- If it turns out that other changes don't let ovsdb-server scale
- adequately, we can multithread ovsdb-server. Initially one might
- only break protocol handling into separate threads, leaving the
- actual database work serialized through a lock.
-
- * Reducing startup time.
-
- As-is, if ovsdb-server restarts, every client will fetch a fresh copy of
- the part of the database that it cares about. With hundreds of clients,
- this could cause heavy CPU load on ovsdb-server and use excessive network
- bandwidth. It would be better to allow incremental updates even across
- connection loss. One way might be to use "Difference Digests" as described
- in Epstein et al., "What's the Difference? Efficient Set Reconciliation
- Without Prior Context". (I'm not yet aware of previous non-academic use of
- this technique.)
-
-* Support multiple tunnel encapsulations in Chassis.
-
- So far, both ovn-controller and ovn-controller-vtep only allow chassis to
- have one tunnel encapsulation entry. We should extend the implementation
- to support multiple tunnel encapsulations.
-
-* Update learned MAC addresses from VTEP to OVN
-
- The VTEP gateway stores all MAC addresses learned from its physical
- interfaces in the 'Ucast_Macs_Local' and the 'Mcast_Macs_Local' tables.
- ovn-controller-vtep should be able to update that information back to
- ovn-sb database, so that other chassis know where to send packets destined
- to the extended external network instead of broadcasting.
-
-* Translate ovn-sb Multicast_Group table into VTEP config
-
- The ovn-controller-vtep daemon should be able to translate the
- Multicast_Group table entry in ovn-sb database into Mcast_Macs_Remote table
- configuration in VTEP database.
-
-* OVN OCF pacemaker script to support Active / Passive HA for OVN dbs provides
- the option to configure the inactivity_probe value. The default 5 seconds
- inactivity_probe value is not sufficient and ovsdb-server drops the client
- IDL connections for openstack deployments when the neutron server is heavily
- loaded.
-
- We need to find a proper solution to solve this issue instead of increasing
- the inactivity_probe value.
-
-* ACL
-
- * Support FTP ALGs.
-
- * Support reject action.
diff --git a/ovn/automake.mk b/ovn/automake.mk
index b33112ef1..afaf0688c 100644
--- a/ovn/automake.mk
+++ b/ovn/automake.mk
@@ -66,13 +66,6 @@ ovn/ovn-nb.5: \
$(srcdir)/ovn/ovn-nb.xml > $@.tmp && \
mv $@.tmp $@
-man_MANS += ovn/ovn-architecture.7
-EXTRA_DIST += ovn/ovn-architecture.7.xml
-CLEANFILES += ovn/ovn-architecture.7
-
-EXTRA_DIST += \
- ovn/TODO.rst
-
# Version checking for ovn-nb.ovsschema.
ALL_LOCAL += ovn/ovn-nb.ovsschema.stamp
ovn/ovn-nb.ovsschema.stamp: ovn/ovn-nb.ovsschema
@@ -85,8 +78,5 @@ ovn/ovn-sb.ovsschema.stamp: ovn/ovn-sb.ovsschema
$(srcdir)/build-aux/cksum-schema-check $? $@
CLEANFILES += ovn/ovn-sb.ovsschema.stamp
-include ovn/controller/automake.mk
-include ovn/controller-vtep/automake.mk
include ovn/lib/automake.mk
-include ovn/northd/automake.mk
include ovn/utilities/automake.mk
diff --git a/ovn/controller-vtep/.gitignore b/ovn/controller-vtep/.gitignore
deleted file mode 100644
index 3ec8072c7..000000000
--- a/ovn/controller-vtep/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/ovn-controller-vtep
-/ovn-controller-vtep.8
diff --git a/ovn/controller-vtep/automake.mk b/ovn/controller-vtep/automake.mk
deleted file mode 100644
index 0c83dc70a..000000000
--- a/ovn/controller-vtep/automake.mk
+++ /dev/null
@@ -1,14 +0,0 @@
-bin_PROGRAMS += ovn/controller-vtep/ovn-controller-vtep
-ovn_controller_vtep_ovn_controller_vtep_SOURCES = \
- ovn/controller-vtep/binding.c \
- ovn/controller-vtep/binding.h \
- ovn/controller-vtep/gateway.c \
- ovn/controller-vtep/gateway.h \
- ovn/controller-vtep/ovn-controller-vtep.c \
- ovn/controller-vtep/ovn-controller-vtep.h \
- ovn/controller-vtep/vtep.c \
- ovn/controller-vtep/vtep.h
-ovn_controller_vtep_ovn_controller_vtep_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la vtep/libvtep.la
-man_MANS += ovn/controller-vtep/ovn-controller-vtep.8
-EXTRA_DIST += ovn/controller-vtep/ovn-controller-vtep.8.xml
-CLEANFILES += ovn/controller-vtep/ovn-controller-vtep.8
diff --git a/ovn/controller-vtep/binding.c b/ovn/controller-vtep/binding.c
deleted file mode 100644
index 9cbfadc71..000000000
--- a/ovn/controller-vtep/binding.c
+++ /dev/null
@@ -1,274 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "binding.h"
-
-#include "openvswitch/shash.h"
-#include "lib/smap.h"
-#include "lib/util.h"
-#include "openvswitch/vlog.h"
-#include "ovn-controller-vtep.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "vtep/vtep-idl.h"
-
-VLOG_DEFINE_THIS_MODULE(binding);
-
-/*
- * This module scans through the Port_Binding table in ovnsb. If there is a
- * logical port binding entry for logical switch in vtep gateway chassis's
- * 'vtep_logical_switches' column, sets the binding's chassis column to the
- * corresponding vtep gateway chassis.
- *
- */
-
-
-/* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec'
- * has already been bound to another port binding entry, and resets
- * 'port_binding_rec''s chassis column. Otherwise, updates 'ls_to_pb'
- * and returns false. */
-static bool
-check_pb_conflict(struct shash *ls_to_pb,
- const struct sbrec_port_binding *port_binding_rec,
- const char *chassis_name,
- const char *vtep_lswitch)
-{
- const struct sbrec_port_binding *pb_conflict =
- shash_find_data(ls_to_pb, vtep_lswitch);
-
- if (pb_conflict) {
- VLOG_WARN("logical switch (%s), on vtep gateway chassis "
- "(%s) has already been associated with logical "
- "port (%s), ignore logical port (%s)",
- vtep_lswitch, chassis_name,
- pb_conflict->logical_port,
- port_binding_rec->logical_port);
- sbrec_port_binding_set_chassis(port_binding_rec, NULL);
-
- return true;
- }
-
- shash_add(ls_to_pb, vtep_lswitch, port_binding_rec);
- return false;
-}
-
-/* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec'
- * has already been bound to a different datapath, and resets
- * 'port_binding_rec''s chassis column. Otherwise, updates 'ls_to_db' and
- * returns false. */
-static bool
-check_db_conflict(struct shash *ls_to_db,
- const struct sbrec_port_binding *port_binding_rec,
- const char *chassis_name,
- const char *vtep_lswitch)
-{
- const struct sbrec_datapath_binding *db_conflict =
- shash_find_data(ls_to_db, vtep_lswitch);
-
- if (db_conflict && db_conflict != port_binding_rec->datapath) {
- VLOG_WARN("logical switch (%s), on vtep gateway chassis "
- "(%s) has already been associated with logical "
- "datapath (with tunnel key %"PRId64"), ignore "
- "logical port (%s) which belongs to logical "
- "datapath (with tunnel key %"PRId64")",
- vtep_lswitch, chassis_name,
- db_conflict->tunnel_key,
- port_binding_rec->logical_port,
- port_binding_rec->datapath->tunnel_key);
- sbrec_port_binding_set_chassis(port_binding_rec, NULL);
-
- return true;
- }
-
- shash_replace(ls_to_db, vtep_lswitch, port_binding_rec->datapath);
- return false;
-}
-
-/* Updates the 'port_binding_rec''s chassis column to 'chassis_rec'. */
-static void
-update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
- const struct sbrec_chassis *chassis_rec)
-{
- if (port_binding_rec->chassis != chassis_rec) {
- if (chassis_rec && port_binding_rec->chassis) {
- VLOG_DBG("Changing chassis association of logical "
- "port (%s) from (%s) to (%s)",
- port_binding_rec->logical_port,
- port_binding_rec->chassis->name,
- chassis_rec->name);
- }
- sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
- }
-}
-
-
-/* Checks and updates logical port to vtep logical switch bindings for each
- * physical switch in VTEP. */
-void
-binding_run(struct controller_vtep_ctx *ctx)
-{
- if (!ctx->ovnsb_idl_txn) {
- return;
- }
-
- /* 'ls_to_db'
- *
- * Maps vtep logical switch name to the datapath binding entry. This is
- * used to guarantee that each vtep logical switch is only included
- * in only one ovn datapath (ovn logical switch). See check_db_conflict()
- * for details.
- *
- * 'ls_to_pb'
- *
- * Maps vtep logical switch name to the port binding entry. This is used
- * to guarantee that each vtep logical switch on a vtep physical switch
- * is only bound to one logical port. See check_pb_conflict() for
- * details.
- *
- */
- struct shash ls_to_db = SHASH_INITIALIZER(&ls_to_db);
-
- /* Stores the 'chassis' and the 'ls_to_pb' map related to
- * a vtep physcial switch. */
- struct ps {
- const struct sbrec_chassis *chassis_rec;
- struct shash ls_to_pb;
- };
- struct shash ps_map = SHASH_INITIALIZER(&ps_map);
- const struct vteprec_physical_switch *pswitch;
- VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
- const struct sbrec_chassis *chassis_rec
- = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
- struct ps *ps = xmalloc(sizeof *ps);
- size_t i;
-
- /* 'chassis_rec' must exist. */
- ovs_assert(chassis_rec);
- ps->chassis_rec = chassis_rec;
- shash_init(&ps->ls_to_pb);
- for (i = 0; i < chassis_rec->n_vtep_logical_switches; i++) {
- shash_add(&ps->ls_to_pb, chassis_rec->vtep_logical_switches[i],
- NULL);
- }
- shash_add(&ps_map, chassis_rec->name, ps);
- }
-
- ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
- "ovn-controller-vtep: updating bindings");
-
- const struct sbrec_port_binding *port_binding_rec;
- /* Port binding for vtep gateway chassis must have type "vtep",
- * and matched physical switch name and logical switch name. */
- SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
- const char *type = port_binding_rec->type;
- const char *vtep_pswitch = smap_get(&port_binding_rec->options,
- "vtep-physical-switch");
- const char *vtep_lswitch = smap_get(&port_binding_rec->options,
- "vtep-logical-switch");
- struct ps *ps
- = vtep_pswitch ? shash_find_data(&ps_map, vtep_pswitch) : NULL;
- bool found_ls
- = ps && vtep_lswitch && shash_find(&ps->ls_to_pb, vtep_lswitch);
-
- if (!strcmp(type, "vtep") && found_ls) {
- bool pb_conflict, db_conflict;
-
- pb_conflict = check_pb_conflict(&ps->ls_to_pb, port_binding_rec,
- ps->chassis_rec->name,
- vtep_lswitch);
- db_conflict = check_db_conflict(&ls_to_db, port_binding_rec,
- ps->chassis_rec->name,
- vtep_lswitch);
- /* Updates port binding's chassis column when there
- * is no conflict. */
- if (!pb_conflict && !db_conflict) {
- update_pb_chassis(port_binding_rec, ps->chassis_rec);
- }
- } else if (port_binding_rec->chassis
- && shash_find(&ps_map, port_binding_rec->chassis->name)) {
- /* Resets 'port_binding_rec' since it is no longer bound to
- * any vtep logical switch. */
- update_pb_chassis(port_binding_rec, NULL);
- }
- }
-
- struct shash_node *iter, *next;
- SHASH_FOR_EACH_SAFE (iter, next, &ps_map) {
- struct ps *ps = iter->data;
- struct shash_node *node;
-
- SHASH_FOR_EACH (node, &ps->ls_to_pb) {
- if (!node->data) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_DBG_RL(&rl, "No port binding entry for logical switch (%s)"
- "on vtep gateway chassis (%s)", node->name,
- ps->chassis_rec->name);
- }
- }
- shash_delete(&ps_map, iter);
- shash_destroy(&ps->ls_to_pb);
- free(ps);
- }
- shash_destroy(&ls_to_db);
- shash_destroy(&ps_map);
-}
-
-/* Removes all port binding association with vtep gateway chassis.
- * Returns true when done (i.e. there is no change made to 'ctx->ovnsb_idl'),
- * otherwise returns false. */
-bool
-binding_cleanup(struct controller_vtep_ctx *ctx)
-{
- if (!ctx->ovnsb_idl_txn) {
- return false;
- }
-
- struct shash ch_to_pb = SHASH_INITIALIZER(&ch_to_pb);
- const struct sbrec_port_binding *port_binding_rec;
- bool all_done = true;
- /* Hashs all port binding entries using the associated chassis name. */
- SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
- if (port_binding_rec->chassis) {
- shash_add(&ch_to_pb, port_binding_rec->chassis->name,
- port_binding_rec);
- }
- }
-
- ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
- "ovn-controller-vtep: removing bindings");
-
- const struct vteprec_physical_switch *pswitch;
- VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
- const struct sbrec_chassis *chassis_rec
- = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
-
- if (!chassis_rec) {
- continue;
- }
-
- for (;;) {
- port_binding_rec = shash_find_and_delete(&ch_to_pb,
- chassis_rec->name);
- if (!port_binding_rec) {
- break;
- }
- all_done = false;
- update_pb_chassis(port_binding_rec, NULL);
- }
- }
- shash_destroy(&ch_to_pb);
-
- return all_done;
-}
diff --git a/ovn/controller-vtep/binding.h b/ovn/controller-vtep/binding.h
deleted file mode 100644
index 374c1ccf8..000000000
--- a/ovn/controller-vtep/binding.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef OVN_BINDING_H
-#define OVN_BINDING_H 1
-
-#include <stdbool.h>
-
-struct controller_vtep_ctx;
-
-void binding_run(struct controller_vtep_ctx *);
-bool binding_cleanup(struct controller_vtep_ctx *);
-
-#endif /* ovn/controller-gw/binding.h */
diff --git a/ovn/controller-vtep/gateway.c b/ovn/controller-vtep/gateway.c
deleted file mode 100644
index 619c3c49a..000000000
--- a/ovn/controller-vtep/gateway.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "gateway.h"
-
-#include "openvswitch/poll-loop.h"
-#include "lib/simap.h"
-#include "lib/sset.h"
-#include "lib/util.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "vtep/vtep-idl.h"
-#include "ovn-controller-vtep.h"
-
-VLOG_DEFINE_THIS_MODULE(gateway);
-
-/*
- * Registers the physical switches in vtep to ovnsb as chassis. For each
- * physical switch in the vtep database, finds all vtep logical switches that
- * are associated with the physical switch, and updates the corresponding
- * chassis's 'vtep_logical_switches' column.
- *
- */
-
-/* Global revalidation sequence number, incremented at each call to
- * 'revalidate_gateway()'. */
-static unsigned int gw_reval_seq;
-
-/* Maps all chassis created by the gateway module to their own reval_seq. */
-static struct simap gw_chassis_map = SIMAP_INITIALIZER(&gw_chassis_map);
-
-/* Creates and returns a new instance of 'struct sbrec_chassis'. */
-static const struct sbrec_chassis *
-create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name,
- const char *encap_ip)
-{
- const struct sbrec_chassis *chassis_rec;
- struct sbrec_encap *encap_rec;
-
- VLOG_INFO("add Chassis row for VTEP physical switch (%s)", name);
-
- chassis_rec = sbrec_chassis_insert(txn);
- sbrec_chassis_set_name(chassis_rec, name);
- encap_rec = sbrec_encap_insert(txn);
- sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE);
- sbrec_encap_set_ip(encap_rec, encap_ip);
- const struct smap options = SMAP_CONST1(&options, "csum", "false");
- sbrec_encap_set_options(encap_rec, &options);
- sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
-
- return chassis_rec;
-}
-
-/* Revalidates chassis in ovnsb against vtep database. Creates chassis for
- * new vtep physical switch. And removes chassis which no longer have
- * physical switch in vtep.
- *
- * xxx: Support multiple tunnel encaps.
- *
- * */
-static void
-revalidate_gateway(struct controller_vtep_ctx *ctx)
-{
- const struct vteprec_physical_switch *pswitch;
-
- /* Increments the global revalidation sequence number. */
- gw_reval_seq++;
-
- ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
- "ovn-controller-vtep: updating vtep chassis");
-
- VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
- const struct sbrec_chassis *chassis_rec;
- struct simap_node *gw_node;
- const char *encap_ip;
-
- encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : "";
- gw_node = simap_find(&gw_chassis_map, pswitch->name);
- chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
- if (chassis_rec) {
- if (!gw_node &&
- (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)
- || strcmp(chassis_rec->encaps[0]->ip, encap_ip))) {
- VLOG_WARN("Chassis config changing on startup, make sure "
- "multiple chassis are not configured : %s/%s->%s/%s",
- chassis_rec->encaps[0]->type,
- chassis_rec->encaps[0]->ip,
- OVN_SB_ENCAP_TYPE, encap_ip);
- }
- /* Updates chassis's encap if anything changed. */
- if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) {
- VLOG_WARN("Chassis for VTEP physical switch (%s) can only have "
- "encap type \"%s\"", pswitch->name, OVN_SB_ENCAP_TYPE);
- sbrec_encap_set_type(chassis_rec->encaps[0], OVN_SB_ENCAP_TYPE);
- }
- if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) {
- sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip);
- }
- if (smap_get_bool(&chassis_rec->encaps[0]->options, "csum", true)) {
- const struct smap options = SMAP_CONST1(&options, "csum",
- "false");
- sbrec_encap_set_options(chassis_rec->encaps[0], &options);
- }
- } else {
- if (gw_node) {
- VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, "
- "maybe deleted by ovn-sbctl, adding it back",
- pswitch->name);
- }
- /* Creates a new chassis for the VTEP physical switch. */
- create_chassis_rec(ctx->ovnsb_idl_txn, pswitch->name, encap_ip);
- }
- /* Updates or creates the simap node for 'pswitch->name'. */
- simap_put(&gw_chassis_map, pswitch->name, gw_reval_seq);
- }
-
- struct simap_node *iter, *next;
- /* For 'gw_node' in 'gw_chassis_map' whose data is not
- * 'gw_reval_seq', it means the corresponding physical switch no
- * longer exist. So, garbage collects them. */
- SIMAP_FOR_EACH_SAFE (iter, next, &gw_chassis_map) {
- if (iter->data != gw_reval_seq) {
- const struct sbrec_chassis *chassis_rec;
-
- chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name);
- if (chassis_rec) {
- sbrec_chassis_delete(chassis_rec);
- }
- simap_delete(&gw_chassis_map, iter);
- }
- }
-}
-
-/* Updates the 'vtep_logical_switches' column in the Chassis table based
- * on vtep database configuration. */
-static void
-update_vtep_logical_switches(struct controller_vtep_ctx *ctx)
-{
- const struct vteprec_physical_switch *pswitch;
-
- ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
- "updating chassis's vtep_logical_switches");
-
- VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
- const struct sbrec_chassis *chassis_rec =
- get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
- struct sset lswitches = SSET_INITIALIZER(&lswitches);
- size_t i;
-
- for (i = 0; i < pswitch->n_ports; i++) {
- const struct vteprec_physical_port *port = pswitch->ports[i];
- size_t j;
-
- for (j = 0; j < port->n_vlan_bindings; j++) {
- const struct vteprec_logical_switch *vtep_lswitch;
-
- vtep_lswitch = port->value_vlan_bindings[j];
- /* If not already in 'lswitches', records it. */
- if (!sset_find(&lswitches, vtep_lswitch->name)) {
- sset_add(&lswitches, vtep_lswitch->name);
- }
- }
- }
-
- const char **ls_arr = sset_array(&lswitches);
- sbrec_chassis_set_vtep_logical_switches(chassis_rec, ls_arr,
- sset_count(&lswitches));
- free(ls_arr);
- sset_destroy(&lswitches);
- }
-}
-
-
-void
-gateway_run(struct controller_vtep_ctx *ctx)
-{
- if (!ctx->ovnsb_idl_txn) {
- return;
- }
-
- revalidate_gateway(ctx);
- update_vtep_logical_switches(ctx);
-}
-
-/* Destroys the chassis table entries for vtep physical switches.
- * Returns true when done (i.e. there is no change made to 'ctx->ovnsb_idl'),
- * otherwise returns false. */
-bool
-gateway_cleanup(struct controller_vtep_ctx *ctx)
-{
- static bool simap_destroyed = false;
- const struct vteprec_physical_switch *pswitch;
-
- if (!ctx->ovnsb_idl_txn) {
- return false;
- }
-
- bool all_done = true;
- ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
- "unregistering vtep chassis");
- VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
- const struct sbrec_chassis *chassis_rec;
-
- chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
- if (!chassis_rec) {
- continue;
- }
- all_done = false;
- sbrec_chassis_delete(chassis_rec);
- }
- if (!simap_destroyed) {
- simap_destroy(&gw_chassis_map);
- simap_destroyed = true;
- }
-
- return all_done;
-}
diff --git a/ovn/controller-vtep/gateway.h b/ovn/controller-vtep/gateway.h
deleted file mode 100644
index 0086191d9..000000000
--- a/ovn/controller-vtep/gateway.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_GATEWAY_H
-#define OVN_GATEWAY_H 1
-
-#include <stdbool.h>
-
-struct controller_vtep_ctx;
-
-void gateway_run(struct controller_vtep_ctx *);
-bool gateway_cleanup(struct controller_vtep_ctx *);
-
-#endif /* ovn/controller-gw/gateway.h */
diff --git a/ovn/controller-vtep/ovn-controller-vtep.8.xml b/ovn/controller-vtep/ovn-controller-vtep.8.xml
deleted file mode 100644
index 2c706e46e..000000000
--- a/ovn/controller-vtep/ovn-controller-vtep.8.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manpage program="ovn-controller-vtep" section="8" title="ovn-controller-vtep">
- <h1>Name</h1>
- <p>ovn-controller-vtep -- Open Virtual Network local controller for
- vtep enabled physical switches.
- </p>
-
- <h1>Synopsis</h1>
- <p><code>ovn-controller-vtep</code> [<var>options</var>]
- [<var>--vtep-db=vtep-database</var>] [<var>--ovnsb-db=ovnsb-database</var>]
- </p>
-
- <h1>Description</h1>
- <p>
- <code>ovn-controller-vtep</code> is the local controller daemon in
- OVN, the Open Virtual Network, for VTEP enabled physical switches.
- It connects up to the OVN Southbound database (see
- <code>ovn-sb</code>(5)) over the OVSDB protocol, and down to the VTEP
- database (see <code>vtep</code>(5)) over the OVSDB protocol.
- </p>
-
- <h2>PKI Options</h2>
- <p>
- PKI configuration is required in order to use SSL for the connections to
- the VTEP and Southbound databases.
- </p>
- <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
- <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
- <xi:include href="lib/ssl-peer-ca-cert.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h1>Configuration</h1>
- <p>
- <code>ovn-controller-vtep</code> retrieves its configuration
- information from both the ovnsb and the vtep database. If the
- database locations are not given from command line, the default
- is the <code>db.sock</code> in local OVSDB's 'run' directory.
- The datapath location must take one of the following forms:
- </p>
- <ul>
- <li>
- <p>
- <code>ssl:<var>host</var>:<var>port</var></code>
- </p>
- <p>
- The specified SSL <var>port</var> on the give <var>host</var>, which
- can either be a DNS name (if built with unbound library) or an IP
- address (IPv4 or IPv6). If <var>host</var> is an IPv6 address, then
- wrap <var>host</var> with square brackets, e.g.: <code>ssl:[::1]:6640</code>.
- The <code>--private-key</code>, <code>--certificate</code> and either
- of <code>--ca-cert</code> or <code>--bootstrap-ca-cert</code> options
- are mandatory when this form is used.
- </p>
- </li>
- <li>
- <p>
- <code>tcp:<var>host</var>:<var>port</var></code>
- </p>
- <p>
- Connect to the given TCP <var>port</var> on <var>host</var>, where
- <var>host</var> can be a DNS name (if built with unbound library) or
- IP address (IPv4 or IPv6). If <var>host</var> is an IPv6 address,
- then wrap <var>host</var> with square brackets,
- e.g.: <code>tcp:[::1]:6640</code>.
- </p>
- </li>
- <li>
- <p>
- <code>unix:<var>file</var></code>
- </p>
- <p>
- On POSIX, connect to the Unix domain server socket named
- <var>file</var>.
- </p>
- <p>
- On Windows, connect to a localhost TCP port whose value is written
- in <var>file</var>.
- </p>
- </li>
- </ul>
-</manpage>
diff --git a/ovn/controller-vtep/ovn-controller-vtep.c b/ovn/controller-vtep/ovn-controller-vtep.c
deleted file mode 100644
index 292a3f464..000000000
--- a/ovn/controller-vtep/ovn-controller-vtep.c
+++ /dev/null
@@ -1,272 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include <errno.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "command-line.h"
-#include "compiler.h"
-#include "daemon.h"
-#include "dirs.h"
-#include "openvswitch/dynamic-string.h"
-#include "fatal-signal.h"
-#include "openvswitch/poll-loop.h"
-#include "stream.h"
-#include "stream-ssl.h"
-#include "unixctl.h"
-#include "util.h"
-#include "openvswitch/vconn.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn/lib/ovn-util.h"
-#include "vtep/vtep-idl.h"
-
-#include "binding.h"
-#include "gateway.h"
-#include "vtep.h"
-#include "ovn-controller-vtep.h"
-
-static unixctl_cb_func ovn_controller_vtep_exit;
-
-static void parse_options(int argc, char *argv[]);
-OVS_NO_RETURN static void usage(void);
-
-static char *vtep_remote;
-static char *ovnsb_remote;
-static char *default_db_;
-
-int
-main(int argc, char *argv[])
-{
- struct unixctl_server *unixctl;
- bool exiting;
- int retval;
-
- ovs_cmdl_proctitle_init(argc, argv);
- set_program_name(argv[0]);
- service_start(&argc, &argv);
- parse_options(argc, argv);
- fatal_ignore_sigpipe();
-
- daemonize_start(false);
-
- retval = unixctl_server_create(NULL, &unixctl);
- if (retval) {
- exit(EXIT_FAILURE);
- }
- unixctl_command_register("exit", "", 0, 0, ovn_controller_vtep_exit,
- &exiting);
-
- daemonize_complete();
-
- /* Connect to VTEP database. */
- struct ovsdb_idl_loop vtep_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
- ovsdb_idl_create(vtep_remote, &vteprec_idl_class, true, true));
- ovsdb_idl_get_initial_snapshot(vtep_idl_loop.idl);
-
- /* Connect to OVN SB database. */
- struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
- ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
- ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl);
-
- /* Main loop. */
- exiting = false;
- while (!exiting) {
- struct controller_vtep_ctx ctx = {
- .vtep_idl = vtep_idl_loop.idl,
- .vtep_idl_txn = ovsdb_idl_loop_run(&vtep_idl_loop),
- .ovnsb_idl = ovnsb_idl_loop.idl,
- .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
- };
-
- gateway_run(&ctx);
- binding_run(&ctx);
- vtep_run(&ctx);
- unixctl_server_run(unixctl);
-
- unixctl_server_wait(unixctl);
- if (exiting) {
- poll_immediate_wake();
- }
- ovsdb_idl_loop_commit_and_wait(&vtep_idl_loop);
- ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
- poll_block();
- if (should_service_stop()) {
- exiting = true;
- }
- }
-
- /* It's time to exit. Clean up the databases. */
- bool done = false;
- while (!done) {
- struct controller_vtep_ctx ctx = {
- .vtep_idl = vtep_idl_loop.idl,
- .vtep_idl_txn = ovsdb_idl_loop_run(&vtep_idl_loop),
- .ovnsb_idl = ovnsb_idl_loop.idl,
- .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
- };
-
- /* Run all of the cleanup functions, even if one of them returns false.
- * We're done if all of them return true. */
- done = binding_cleanup(&ctx);
- done = gateway_cleanup(&ctx) && done;
- done = vtep_cleanup(&ctx) && done;
- if (done) {
- poll_immediate_wake();
- }
-
- ovsdb_idl_loop_commit_and_wait(&vtep_idl_loop);
- ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
- poll_block();
- }
-
- unixctl_server_destroy(unixctl);
-
- ovsdb_idl_loop_destroy(&vtep_idl_loop);
- ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
-
- free(ovnsb_remote);
- free(vtep_remote);
- free(default_db_);
- service_stop();
-
- exit(retval);
-}
-
-static const char *
-default_db(void)
-{
- if (!default_db_) {
- default_db_ = xasprintf("unix:%s/db.sock", ovs_rundir());
- }
- return default_db_;
-}
-
-static void
-parse_options(int argc, char *argv[])
-{
- enum {
- OPT_PEER_CA_CERT = UCHAR_MAX + 1,
- OPT_BOOTSTRAP_CA_CERT,
- VLOG_OPTION_ENUMS,
- DAEMON_OPTION_ENUMS,
- SSL_OPTION_ENUMS,
- };
-
- static struct option long_options[] = {
- {"ovnsb-db", required_argument, NULL, 'd'},
- {"vtep-db", required_argument, NULL, 'D'},
- {"help", no_argument, NULL, 'h'},
- {"version", no_argument, NULL, 'V'},
- VLOG_LONG_OPTIONS,
- DAEMON_LONG_OPTIONS,
- STREAM_SSL_LONG_OPTIONS,
- {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
- {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
- {NULL, 0, NULL, 0}
- };
- char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
-
- for (;;) {
- int c;
-
- c = getopt_long(argc, argv, short_options, long_options, NULL);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'd':
- ovnsb_remote = xstrdup(optarg);
- break;
-
- case 'D':
- vtep_remote = xstrdup(optarg);
- break;
-
- case 'h':
- usage();
-
- case 'V':
- ovs_print_version(OFP13_VERSION, OFP13_VERSION);
- exit(EXIT_SUCCESS);
-
- VLOG_OPTION_HANDLERS
- DAEMON_OPTION_HANDLERS
- STREAM_SSL_OPTION_HANDLERS
-
- case OPT_PEER_CA_CERT:
- stream_ssl_set_peer_ca_cert_file(optarg);
- break;
-
- case OPT_BOOTSTRAP_CA_CERT:
- stream_ssl_set_ca_cert_file(optarg, true);
- break;
-
- case '?':
- exit(EXIT_FAILURE);
-
- default:
- abort();
- }
- }
- free(short_options);
-
- if (!ovnsb_remote) {
- ovnsb_remote = xstrdup(default_sb_db());
- }
-
- if (!vtep_remote) {
- vtep_remote = xstrdup(default_db());
- }
-}
-
-static void
-usage(void)
-{
- printf("\
-%s: OVN controller VTEP\n\
-usage %s [OPTIONS]\n\
-\n\
-Options:\n\
- --vtep-db=DATABASE connect to vtep database at DATABASE\n\
- (default: %s)\n\
- --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
- (default: %s)\n\
- -h, --help display this help message\n\
- -o, --options list available options\n\
- -V, --version display version information\n\
-", program_name, program_name, default_db(), default_sb_db());
- stream_usage("database", true, false, true);
- daemon_usage();
- vlog_usage();
- exit(EXIT_SUCCESS);
-}
-
-
-static void
-ovn_controller_vtep_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *exiting_)
-{
- bool *exiting = exiting_;
- *exiting = true;
-
- unixctl_command_reply(conn, NULL);
-}
diff --git a/ovn/controller-vtep/ovn-controller-vtep.h b/ovn/controller-vtep/ovn-controller-vtep.h
deleted file mode 100644
index 435a730d9..000000000
--- a/ovn/controller-vtep/ovn-controller-vtep.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef OVN_CONTROLLER_VTEP_H
-#define OVN_CONTROLLER_VTEP_H 1
-
-#include "ovn/lib/ovn-sb-idl.h"
-
-struct ovsdb_idl;
-struct ovsdb_idl_txn;
-
-struct controller_vtep_ctx {
- struct ovsdb_idl *ovnsb_idl;
- struct ovsdb_idl_txn *ovnsb_idl_txn;
-
- struct ovsdb_idl *vtep_idl;
- struct ovsdb_idl_txn *vtep_idl_txn;
-};
-
-/* VTEP needs what VTEP needs. */
-#define OVN_SB_ENCAP_TYPE "vxlan"
-#define VTEP_ENCAP_TYPE "vxlan_over_ipv4"
-
-static inline const struct sbrec_chassis *
-get_chassis_by_name(struct ovsdb_idl *ovnsb_idl, const char *chassis_id)
-{
- const struct sbrec_chassis *chassis_rec;
-
- SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) {
- if (!strcmp(chassis_rec->name, chassis_id)) {
- break;
- }
- }
-
- return chassis_rec;
-}
-
-#endif /* ovn/ovn-controller-vtep.h */
diff --git a/ovn/controller-vtep/vtep.c b/ovn/controller-vtep/vtep.c
deleted file mode 100644
index a72b149eb..000000000
--- a/ovn/controller-vtep/vtep.c
+++ /dev/null
@@ -1,600 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "vtep.h"
-
-#include "lib/hash.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/shash.h"
-#include "lib/smap.h"
-#include "lib/sset.h"
-#include "lib/util.h"
-#include "ovn-controller-vtep.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "vtep/vtep-idl.h"
-
-VLOG_DEFINE_THIS_MODULE(vtep);
-
-struct vtep_rec_physical_locator_list_entry {
- struct ovs_list locators_node;
- const struct vteprec_physical_locator *vteprec_ploc;
-};
-
-struct mmr_hash_node_data {
- const struct vteprec_mcast_macs_remote *mmr;
- struct shash physical_locators;
-};
-
-/*
- * Scans through the Binding table in ovnsb, and updates the vtep logical
- * switch tunnel keys and the 'Ucast_Macs_Remote' table in the VTEP
- * database.
- *
- */
-
-/* Searches the 'chassis_rec->encaps' for the first vtep tunnel
- * configuration, returns the 'ip'. Unless duplicated, the returned
- * pointer cannot live past current vtep_run() execution. */
-static const char *
-get_chassis_vtep_ip(const struct sbrec_chassis *chassis_rec)
-{
- if (chassis_rec) {
- size_t i;
-
- for (i = 0; i < chassis_rec->n_encaps; i++) {
- if (!strcmp(chassis_rec->encaps[i]->type, "vxlan")) {
- return chassis_rec->encaps[i]->ip;
- }
- }
- }
-
- return NULL;
-}
-
-/* Creates a new 'Ucast_Macs_Remote'. */
-static struct vteprec_ucast_macs_remote *
-create_umr(struct ovsdb_idl_txn *vtep_idl_txn, const char *mac,
- const struct vteprec_logical_switch *vtep_ls)
-{
- struct vteprec_ucast_macs_remote *new_umr =
- vteprec_ucast_macs_remote_insert(vtep_idl_txn);
-
- vteprec_ucast_macs_remote_set_MAC(new_umr, mac);
- vteprec_ucast_macs_remote_set_logical_switch(new_umr, vtep_ls);
-
- return new_umr;
-}
-
-/* Creates a new 'Physical_Locator'. */
-static struct vteprec_physical_locator *
-create_pl(struct ovsdb_idl_txn *vtep_idl_txn, const char *chassis_ip)
-{
- struct vteprec_physical_locator *new_pl =
- vteprec_physical_locator_insert(vtep_idl_txn);
-
- vteprec_physical_locator_set_dst_ip(new_pl, chassis_ip);
- vteprec_physical_locator_set_encapsulation_type(new_pl, VTEP_ENCAP_TYPE);
-
- return new_pl;
-}
-
-/* Creates a new 'Mcast_Macs_Remote'. */
-static void
-vtep_create_mmr(struct ovsdb_idl_txn *vtep_idl_txn, const char *mac,
- const struct vteprec_logical_switch *vtep_ls,
- const struct vteprec_physical_locator_set *ploc_set)
-{
- struct vteprec_mcast_macs_remote *new_mmr =
- vteprec_mcast_macs_remote_insert(vtep_idl_txn);
-
- vteprec_mcast_macs_remote_set_MAC(new_mmr, mac);
- vteprec_mcast_macs_remote_set_logical_switch(new_mmr, vtep_ls);
- vteprec_mcast_macs_remote_set_locator_set(new_mmr, ploc_set);
-}
-
-/* Compares previous and new mmr locator sets and returns true if they
- * differ and false otherwise. This function also preps a new locator
- * set for database write.
- *
- * 'locators_list' is the new set of locators for the associated
- * 'Mcast_Macs_Remote' entry passed in and is queried to generate the
- * new set of locators in vtep database format. */
-static bool
-vtep_process_pls(const struct ovs_list *locators_list,
- const struct mmr_hash_node_data *mmr_ext,
- struct vteprec_physical_locator **locators)
-{
- size_t n_locators_prev = 0;
- size_t n_locators_new = ovs_list_size(locators_list);
- bool locator_lists_differ = false;
-
- if (mmr_ext) {
- n_locators_prev = mmr_ext->mmr->locator_set->n_locators;
- }
- if (n_locators_prev != n_locators_new) {
- locator_lists_differ = true;
- }
-
- if (n_locators_new) {
- int i = 0;
- struct vtep_rec_physical_locator_list_entry *ploc_entry;
- LIST_FOR_EACH (ploc_entry, locators_node, locators_list) {
- locators[i] = (struct vteprec_physical_locator *)
- ploc_entry->vteprec_ploc;
- if (mmr_ext && !shash_find_data(&mmr_ext->physical_locators,
- locators[i]->dst_ip)) {
- locator_lists_differ = true;
- }
- i++;
- }
- }
-
- return locator_lists_differ;
-}
-
-/* Creates a new 'Mcast_Macs_Remote' entry if needed and also cleans up
- * out-dated remote mcast mac entries as needed. */
-static void
-vtep_update_mmr(struct ovsdb_idl_txn *vtep_idl_txn,
- struct ovs_list *locators_list,
- const struct vteprec_logical_switch *vtep_ls,
- const struct mmr_hash_node_data *mmr_ext)
-{
- struct vteprec_physical_locator **locators = NULL;
- size_t n_locators_new = ovs_list_size(locators_list);
- bool mmr_changed;
-
- locators = xmalloc(n_locators_new * sizeof *locators);
-
- mmr_changed = vtep_process_pls(locators_list, mmr_ext, locators);
-
- if (mmr_ext && !n_locators_new) {
- vteprec_mcast_macs_remote_delete(mmr_ext->mmr);
- } else if ((mmr_ext && mmr_changed) ||
- (!mmr_ext && n_locators_new)) {
-
- const struct vteprec_physical_locator_set *ploc_set =
- vteprec_physical_locator_set_insert(vtep_idl_txn);
-
- vtep_create_mmr(vtep_idl_txn, "unknown-dst", vtep_ls, ploc_set);
-
- vteprec_physical_locator_set_set_locators(ploc_set, locators,
- n_locators_new);
- }
- free(locators);
-}
-
-/* Updates the vtep Logical_Switch table entries' tunnel keys based
- * on the port bindings. */
-static void
-vtep_lswitch_run(struct shash *vtep_pbs, struct sset *vtep_pswitches,
- struct shash *vtep_lswitches)
-{
- struct sset used_ls = SSET_INITIALIZER(&used_ls);
- struct shash_node *node;
-
- /* Collects the logical switch bindings from port binding entries.
- * Since the binding module has already guaranteed that each vtep
- * logical switch is bound only to one ovn-sb logical datapath,
- * we can just iterate and assign tunnel key to vtep logical switch. */
- SHASH_FOR_EACH (node, vtep_pbs) {
- const struct sbrec_port_binding *port_binding_rec = node->data;
- const char *pswitch_name = smap_get(&port_binding_rec->options,
- "vtep-physical-switch");
- const char *lswitch_name = smap_get(&port_binding_rec->options,
- "vtep-logical-switch");
- const struct vteprec_logical_switch *vtep_ls;
-
- /* If 'port_binding_rec->chassis' exists then 'pswitch_name'
- * and 'lswitch_name' must also exist. */
- if (!pswitch_name || !lswitch_name) {
- /* This could only happen when someone directly modifies the
- * database, (e.g. using ovn-sbctl). */
- VLOG_ERR("logical port (%s) with no 'options:vtep-physical-"
- "switch' or 'options:vtep-logical-switch' specified "
- "is bound to chassis (%s).",
- port_binding_rec->logical_port,
- port_binding_rec->chassis->name);
- continue;
- }
- vtep_ls = shash_find_data(vtep_lswitches, lswitch_name);
- /* Also checks 'pswitch_name' since the same 'lswitch_name' could
- * exist in multiple vtep database instances and be bound to different
- * ovn logical networks. */
- if (vtep_ls && sset_find(vtep_pswitches, pswitch_name)) {
- int64_t tnl_key;
-
- if (sset_find(&used_ls, lswitch_name)) {
- continue;
- }
-
- tnl_key = port_binding_rec->datapath->tunnel_key;
- if (vtep_ls->n_tunnel_key
- && vtep_ls->tunnel_key[0] != tnl_key) {
- VLOG_DBG("set vtep logical switch (%s) tunnel key from "
- "(%"PRId64") to (%"PRId64")", vtep_ls->name,
- vtep_ls->tunnel_key[0], tnl_key);
- }
- vteprec_logical_switch_set_tunnel_key(vtep_ls, &tnl_key, 1);
-
- /* OVN is expected to always use source node replication mode,
- * hence the replication mode is hard-coded for each logical
- * switch in the context of ovn-controller-vtep. */
- vteprec_logical_switch_set_replication_mode(vtep_ls, "source_node");
- sset_add(&used_ls, lswitch_name);
- }
- }
- /* Resets the tunnel keys for unused vtep logical switches. */
- SHASH_FOR_EACH (node, vtep_lswitches) {
- if (!sset_find(&used_ls, node->name)) {
- int64_t tnl_key = 0;
- vteprec_logical_switch_set_tunnel_key(node->data, &tnl_key, 1);
- }
- }
- sset_destroy(&used_ls);
-}
-
-/* Updates the vtep 'Ucast_Macs_Remote' and 'Mcast_Macs_Remote' tables based
- * on non-vtep port bindings. */
-static void
-vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct shash *ucast_macs_rmts,
- struct shash *mcast_macs_rmts, struct shash *physical_locators,
- struct shash *vtep_lswitches, struct shash *non_vtep_pbs)
-{
- struct shash_node *node;
- struct hmap ls_map;
-
- /* Maps from ovn logical datapath tunnel key (which is also the vtep
- * logical switch tunnel key) to the corresponding vtep logical switch
- * instance. Also, the shash map 'added_macs' is used for checking
- * duplicated MAC addresses in the same ovn logical datapath. 'mmr_ext'
- * is used to track mmr info per LS that needs creation/update and
- * 'locators_list' collects the new physical locators to be bound for
- * an mmr_ext; 'physical_locators' is used to track existing locators and
- * filter duplicates per logical switch. */
- struct ls_hash_node {
- struct hmap_node hmap_node;
-
- const struct vteprec_logical_switch *vtep_ls;
- struct shash added_macs;
-
- struct ovs_list locators_list;
- struct shash physical_locators;
- struct mmr_hash_node_data *mmr_ext;
- };
-
- hmap_init(&ls_map);
- SHASH_FOR_EACH (node, vtep_lswitches) {
- const struct vteprec_logical_switch *vtep_ls = node->data;
- struct ls_hash_node *ls_node;
-
- if (!vtep_ls->n_tunnel_key) {
- continue;
- }
- ls_node = xmalloc(sizeof *ls_node);
- ls_node->vtep_ls = vtep_ls;
- shash_init(&ls_node->added_macs);
- shash_init(&ls_node->physical_locators);
- ovs_list_init(&ls_node->locators_list);
- ls_node->mmr_ext = NULL;
- hmap_insert(&ls_map, &ls_node->hmap_node,
- hash_uint64((uint64_t) vtep_ls->tunnel_key[0]));
- }
-
- SHASH_FOR_EACH (node, non_vtep_pbs) {
- const struct sbrec_port_binding *port_binding_rec = node->data;
- const struct sbrec_chassis *chassis_rec;
- struct ls_hash_node *ls_node;
- const char *chassis_ip;
- int64_t tnl_key;
- size_t i;
-
- chassis_rec = port_binding_rec->chassis;
- if (!chassis_rec) {
- continue;
- }
-
- tnl_key = port_binding_rec->datapath->tunnel_key;
- HMAP_FOR_EACH_WITH_HASH (ls_node, hmap_node,
- hash_uint64((uint64_t) tnl_key),
- &ls_map) {
- if (ls_node->vtep_ls->tunnel_key[0] == tnl_key) {
- break;
- }
- }
- /* If 'ls_node' is NULL, that means no vtep logical switch is
- * attached to the corresponding ovn logical datapath, so pass.
- */
- if (!ls_node) {
- continue;
- }
-
- chassis_ip = get_chassis_vtep_ip(chassis_rec);
- /* Unreachable chassis, continue. */
- if (!chassis_ip) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_INFO_RL(&rl, "VTEP tunnel encap on chassis (%s) not found",
- chassis_rec->name);
- continue;
- }
-
- const struct vteprec_physical_locator *pl =
- shash_find_data(physical_locators, chassis_ip);
- if (!pl) {
- pl = create_pl(vtep_idl_txn, chassis_ip);
- shash_add(physical_locators, chassis_ip, pl);
- }
-
- const struct vteprec_physical_locator *ls_pl =
- shash_find_data(&ls_node->physical_locators, chassis_ip);
- if (!ls_pl) {
- struct vtep_rec_physical_locator_list_entry *ploc_entry =
- xmalloc(sizeof *ploc_entry);
- ploc_entry->vteprec_ploc = pl;
- ovs_list_push_back(&ls_node->locators_list,
- &ploc_entry->locators_node);
- shash_add(&ls_node->physical_locators, chassis_ip, pl);
- }
-
- char *mac_tnlkey = xasprintf("%s_%"PRId64, "unknown-dst", tnl_key);
- ls_node->mmr_ext = shash_find_data(mcast_macs_rmts, mac_tnlkey);
-
- if (ls_node->mmr_ext &&
- ls_node->mmr_ext->mmr->logical_switch == ls_node->vtep_ls) {
-
- /* Delete the entry from the hash table so the mmr does not get
- * removed from the DB later on during stale checking. */
- shash_find_and_delete(mcast_macs_rmts, mac_tnlkey);
- }
- free(mac_tnlkey);
-
- for (i = 0; i < port_binding_rec->n_mac; i++) {
- const struct vteprec_ucast_macs_remote *umr;
- const struct sbrec_port_binding *conflict;
- char *mac = port_binding_rec->mac[i];
-
- /* Checks for duplicate MAC in the same vtep logical switch. */
- conflict = shash_find_data(&ls_node->added_macs, mac);
- if (conflict) {
- VLOG_WARN("MAC address (%s) has already been known to be "
- "on logical port (%s) in the same logical "
- "datapath, so just ignore this logical port (%s)",
- mac, conflict->logical_port,
- port_binding_rec->logical_port);
- continue;
- }
- shash_add(&ls_node->added_macs, mac, port_binding_rec);
-
- char *mac_ip_tnlkey = xasprintf("%s_%s_%"PRId64, mac, chassis_ip,
- tnl_key);
- umr = shash_find_data(ucast_macs_rmts, mac_ip_tnlkey);
- /* If finds the 'umr' entry for the mac, ip, and tnl_key, deletes
- * the entry from shash so that it is not gargage collected.
- *
- * If not found, creates a new 'umr' entry. */
- if (umr && umr->logical_switch == ls_node->vtep_ls) {
- shash_find_and_delete(ucast_macs_rmts, mac_ip_tnlkey);
- } else {
- const struct vteprec_ucast_macs_remote *new_umr;
- new_umr = create_umr(vtep_idl_txn, mac, ls_node->vtep_ls);
- vteprec_ucast_macs_remote_set_locator(new_umr, pl);
- }
- free(mac_ip_tnlkey);
- }
- }
-
- /* Removes all remaining 'umr's, since they do not exist anymore. */
- SHASH_FOR_EACH (node, ucast_macs_rmts) {
- vteprec_ucast_macs_remote_delete(node->data);
- }
- struct ls_hash_node *iter, *next;
- HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &ls_map) {
- struct vtep_rec_physical_locator_list_entry *ploc_entry;
- vtep_update_mmr(vtep_idl_txn, &iter->locators_list,
- iter->vtep_ls, iter->mmr_ext);
- LIST_FOR_EACH_POP(ploc_entry, locators_node,
- &iter->locators_list) {
- free(ploc_entry);
- }
- hmap_remove(&ls_map, &iter->hmap_node);
- shash_destroy(&iter->added_macs);
- shash_destroy(&iter->physical_locators);
- free(iter);
- }
- hmap_destroy(&ls_map);
-
- /* Clean stale 'Mcast_Macs_Remote' */
- struct mmr_hash_node_data *mmr_ext;
- SHASH_FOR_EACH (node, mcast_macs_rmts) {
- mmr_ext = node->data;
- vteprec_mcast_macs_remote_delete(mmr_ext->mmr);
- }
-}
-
-/* Resets all logical switches' 'tunnel_key' to NULL */
-static bool
-vtep_lswitch_cleanup(struct ovsdb_idl *vtep_idl)
-{
- const struct vteprec_logical_switch *vtep_ls;
- bool done = true;
-
- VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, vtep_idl) {
- if (vtep_ls->n_tunnel_key) {
- vteprec_logical_switch_set_tunnel_key(vtep_ls, NULL, 0);
- done = false;
- }
- }
-
- return done;
-}
-
-/* Removes all entries in the 'Ucast_Macs_Remote' table in the vtep database.
- * Returns true when all done (i.e. no entry to remove). */
-static bool
-vtep_ucast_macs_cleanup(struct ovsdb_idl *vtep_idl)
-{
- const struct vteprec_ucast_macs_remote *umr;
-
- VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, vtep_idl) {
- vteprec_ucast_macs_remote_delete(umr);
- return false;
- }
-
- return true;
-}
-
-/* Removes all entries in the 'Mcast_Macs_Remote' table in vtep database.
- * Returns true when all done (i.e. no entry to remove). */
-static bool
-vtep_mcast_macs_cleanup(struct ovsdb_idl *vtep_idl)
-{
- const struct vteprec_mcast_macs_remote *mmr;
-
- VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mmr, vtep_idl) {
- vteprec_mcast_macs_remote_delete(mmr);
- return false;
- }
-
- return true;
-}
-
-/* Updates vtep logical switch tunnel keys. */
-void
-vtep_run(struct controller_vtep_ctx *ctx)
-{
- if (!ctx->vtep_idl_txn) {
- return;
- }
-
- struct sset vtep_pswitches = SSET_INITIALIZER(&vtep_pswitches);
- struct shash vtep_lswitches = SHASH_INITIALIZER(&vtep_lswitches);
- struct shash ucast_macs_rmts = SHASH_INITIALIZER(&ucast_macs_rmts);
- struct shash mcast_macs_rmts = SHASH_INITIALIZER(&mcast_macs_rmts);
- struct shash physical_locators = SHASH_INITIALIZER(&physical_locators);
- struct shash vtep_pbs = SHASH_INITIALIZER(&vtep_pbs);
- struct shash non_vtep_pbs = SHASH_INITIALIZER(&non_vtep_pbs);
- const struct vteprec_physical_switch *vtep_ps;
- const struct vteprec_logical_switch *vtep_ls;
- const struct vteprec_ucast_macs_remote *umr;
- const struct sbrec_port_binding *port_binding_rec;
- const struct vteprec_mcast_macs_remote *mmr;
- struct shash_node *node;
-
- /* Collects 'Physical_Switch's. */
- VTEPREC_PHYSICAL_SWITCH_FOR_EACH (vtep_ps, ctx->vtep_idl) {
- sset_add(&vtep_pswitches, vtep_ps->name);
- }
-
- /* Collects 'Logical_Switch's. */
- VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, ctx->vtep_idl) {
- shash_add(&vtep_lswitches, vtep_ls->name, vtep_ls);
- }
-
- /* Collects 'Ucast_Macs_Remote's. */
- VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, ctx->vtep_idl) {
- char *mac_ip_tnlkey =
- xasprintf("%s_%s_%"PRId64, umr->MAC,
- umr->locator ? umr->locator->dst_ip : "",
- umr->logical_switch && umr->logical_switch->n_tunnel_key
- ? umr->logical_switch->tunnel_key[0] : INT64_MAX);
-
- shash_add(&ucast_macs_rmts, mac_ip_tnlkey, umr);
- free(mac_ip_tnlkey);
- }
-
- /* Collects 'Mcast_Macs_Remote's. */
- VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mmr, ctx->vtep_idl) {
- struct mmr_hash_node_data *mmr_ext = xmalloc(sizeof *mmr_ext);;
- char *mac_tnlkey =
- xasprintf("%s_%"PRId64, mmr->MAC,
- mmr->logical_switch && mmr->logical_switch->n_tunnel_key
- ? mmr->logical_switch->tunnel_key[0] : INT64_MAX);
-
- shash_add(&mcast_macs_rmts, mac_tnlkey, mmr_ext);
- mmr_ext->mmr = mmr;
-
- shash_init(&mmr_ext->physical_locators);
- for (size_t i = 0; i < mmr->locator_set->n_locators; i++) {
- shash_add(&mmr_ext->physical_locators,
- mmr->locator_set->locators[i]->dst_ip,
- mmr->locator_set->locators[i]);
- }
-
- free(mac_tnlkey);
- }
-
- /* Collects 'Physical_Locator's. */
- const struct vteprec_physical_locator *pl;
- VTEPREC_PHYSICAL_LOCATOR_FOR_EACH (pl, ctx->vtep_idl) {
- shash_add(&physical_locators, pl->dst_ip, pl);
- }
-
- /* Collects and classifies 'Port_Binding's. */
- SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
- struct shash *target =
- !strcmp(port_binding_rec->type, "vtep") ? &vtep_pbs : &non_vtep_pbs;
-
- if (!port_binding_rec->chassis) {
- continue;
- }
- shash_add(target, port_binding_rec->logical_port, port_binding_rec);
- }
-
- ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn,
- "ovn-controller-vtep: update logical switch "
- "tunnel keys and 'ucast_macs_remote's");
-
- vtep_lswitch_run(&vtep_pbs, &vtep_pswitches, &vtep_lswitches);
- vtep_macs_run(ctx->vtep_idl_txn, &ucast_macs_rmts,
- &mcast_macs_rmts, &physical_locators,
- &vtep_lswitches, &non_vtep_pbs);
-
- sset_destroy(&vtep_pswitches);
- shash_destroy(&vtep_lswitches);
- shash_destroy(&ucast_macs_rmts);
- SHASH_FOR_EACH (node, &mcast_macs_rmts) {
- struct mmr_hash_node_data *mmr_ext = node->data;
- shash_destroy(&mmr_ext->physical_locators);
- free(mmr_ext);
- }
- shash_destroy(&mcast_macs_rmts);
- shash_destroy(&physical_locators);
- shash_destroy(&vtep_pbs);
- shash_destroy(&non_vtep_pbs);
-}
-
-/* Cleans up all related entries in vtep. Returns true when done (i.e. there
- * is no change made to 'ctx->vtep_idl'), otherwise returns false. */
-bool
-vtep_cleanup(struct controller_vtep_ctx *ctx)
-{
- if (!ctx->vtep_idl_txn) {
- return false;
- }
-
- bool all_done;
-
- ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn,
- "ovn-controller-vtep: cleaning up vtep "
- "configuration");
- all_done = vtep_lswitch_cleanup(ctx->vtep_idl);
- all_done = vtep_ucast_macs_cleanup(ctx->vtep_idl) && all_done;
- all_done = vtep_mcast_macs_cleanup(ctx->vtep_idl) && all_done;
-
- return all_done;
-}
diff --git a/ovn/controller-vtep/vtep.h b/ovn/controller-vtep/vtep.h
deleted file mode 100644
index 97c87b7a7..000000000
--- a/ovn/controller-vtep/vtep.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef OVN_VTEP_H
-#define OVN_VTEP_H 1
-
-#include <stdbool.h>
-
-struct controller_vtep_ctx;
-
-void vtep_run(struct controller_vtep_ctx *);
-bool vtep_cleanup(struct controller_vtep_ctx *);
-
-#endif /* ovn/controller-vtep/vtep.h */
diff --git a/ovn/controller/.gitignore b/ovn/controller/.gitignore
deleted file mode 100644
index 4199a3741..000000000
--- a/ovn/controller/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/ovn-controller
-/ovn-controller.8
diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
deleted file mode 100644
index 193ea690b..000000000
--- a/ovn/controller/automake.mk
+++ /dev/null
@@ -1,32 +0,0 @@
-bin_PROGRAMS += ovn/controller/ovn-controller
-ovn_controller_ovn_controller_SOURCES = \
- ovn/controller/bfd.c \
- ovn/controller/bfd.h \
- ovn/controller/binding.c \
- ovn/controller/binding.h \
- ovn/controller/chassis.c \
- ovn/controller/chassis.h \
- ovn/controller/encaps.c \
- ovn/controller/encaps.h \
- ovn/controller/ha-chassis.c \
- ovn/controller/ha-chassis.h \
- ovn/controller/ip-mcast.c \
- ovn/controller/ip-mcast.h \
- ovn/controller/lflow.c \
- ovn/controller/lflow.h \
- ovn/controller/lport.c \
- ovn/controller/lport.h \
- ovn/controller/ofctrl.c \
- ovn/controller/ofctrl.h \
- ovn/controller/pinctrl.c \
- ovn/controller/pinctrl.h \
- ovn/controller/patch.c \
- ovn/controller/patch.h \
- ovn/controller/ovn-controller.c \
- ovn/controller/ovn-controller.h \
- ovn/controller/physical.c \
- ovn/controller/physical.h
-ovn_controller_ovn_controller_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la
-man_MANS += ovn/controller/ovn-controller.8
-EXTRA_DIST += ovn/controller/ovn-controller.8.xml
-CLEANFILES += ovn/controller/ovn-controller.8
diff --git a/ovn/controller/bfd.c b/ovn/controller/bfd.c
deleted file mode 100644
index 22db00af7..000000000
--- a/ovn/controller/bfd.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/* Copyright (c) 2017 Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "bfd.h"
-#include "encaps.h"
-#include "lport.h"
-#include "ovn-controller.h"
-
-#include "lib/hash.h"
-#include "lib/sset.h"
-#include "lib/util.h"
-#include "lib/vswitch-idl.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn-controller.h"
-
-VLOG_DEFINE_THIS_MODULE(ovn_bfd);
-
-void
-bfd_register_ovs_idl(struct ovsdb_idl *ovs_idl)
-{
- /* NOTE: this assumes that binding.c has added the
- * ovsrec_interface table */
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
-}
-
-void
-bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
- struct sset *active_tunnels)
-{
- int i;
-
- if (!br_int) {
- /* Nothing to do if integration bridge doesn't exist. */
- return;
- }
-
- for (i = 0; i < br_int->n_ports; i++) {
- const struct ovsrec_port *port_rec = br_int->ports[i];
-
- if (!strcmp(port_rec->name, br_int->name)) {
- continue;
- }
-
- int j;
- for (j = 0; j < port_rec->n_interfaces; j++) {
- const struct ovsrec_interface *iface_rec;
- iface_rec = port_rec->interfaces[j];
-
- /* Check if this is a tunnel interface. */
- if (smap_get(&iface_rec->options, "remote_ip")) {
- /* Add ovn-chassis-id if the bfd_status of the tunnel
- * is active */
- const char *bfd = smap_get(&iface_rec->bfd, "enable");
- if (bfd && !strcmp(bfd, "true")) {
- const char *status = smap_get(&iface_rec->bfd_status,
- "state");
- if (status && !strcmp(status, "up")) {
- const char *id = smap_get(&port_rec->external_ids,
- "ovn-chassis-id");
- if (id) {
- char *chassis_name = NULL;
-
- if (encaps_tunnel_id_parse(id, &chassis_name,
- NULL)) {
- if (!sset_contains(active_tunnels,
- chassis_name)) {
- sset_add(active_tunnels, chassis_name);
- }
- free(chassis_name);
- }
- }
- }
- }
- }
- }
- }
-}
-
-/* Loops through the HA chassis groups in the SB DB and returns
- * the set of chassis which the call can establish the BFD sessions
- * with.
- * Eg.
- * If there are 2 HA chassis groups.
- * Group name - hapgrp1
- * - HA chassis - (HA1, HA2, HA3)
- * - ref chassis - (C1, C2)
- *
- * Group name - hapgrp2
- * - HA chassis - (HA1, HA4, HA5)
- * - ref chassis - (C1, C3, C4)
- *
- * If 'our_chassis' is HA1 then this function returns
- * bfd chassis set - (HA2, HA3, HA4 HA5, C1, C2, C3, C4)
- *
- * If 'our_chassis' is C1 then this function returns
- * bfd chassis set - (HA1, HA2, HA3, HA4, HA5)
- *
- * If 'our_chassis' is HA5 then this function returns
- * bfd chassis set - (HA1, HA4, C1, C3, C4)
- *
- * If 'our_chassis' is C2 then this function returns
- * bfd chassis set - (HA1, HA2, HA3)
- *
- * If 'our_chassis' is C5 then this function returns empty bfd set.
- */
-static void
-bfd_calculate_chassis(
- const struct sbrec_chassis *our_chassis,
- const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
- struct sset *bfd_chassis)
-{
- const struct sbrec_ha_chassis_group *ha_chassis_grp;
- SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_chassis_grp,
- ha_chassis_grp_table) {
- bool is_ha_chassis = false;
- struct sset grp_chassis = SSET_INITIALIZER(&grp_chassis);
- const struct sbrec_ha_chassis *ha_ch;
- bool bfd_setup_required = false;
- if (ha_chassis_grp->n_ha_chassis < 2) {
- /* No need to consider the chassis group for BFD if
- * there is 1 or no chassis in it. */
- continue;
- }
- for (size_t i = 0; i < ha_chassis_grp->n_ha_chassis; i++) {
- ha_ch = ha_chassis_grp->ha_chassis[i];
- if (!ha_ch->chassis) {
- continue;
- }
- sset_add(&grp_chassis, ha_ch->chassis->name);
- if (our_chassis == ha_ch->chassis) {
- is_ha_chassis = true;
- bfd_setup_required = true;
- }
- }
-
- if (is_ha_chassis) {
- /* It's an HA chassis. So add the ref_chassis to the bfd set. */
- for (size_t i = 0; i < ha_chassis_grp->n_ref_chassis; i++) {
- sset_add(&grp_chassis, ha_chassis_grp->ref_chassis[i]->name);
- }
- } else {
- /* This is not an HA chassis. Check if this chassis is present
- * in the ref_chassis list. If so add the ha_chassis to the
- * sset .*/
- for (size_t i = 0; i < ha_chassis_grp->n_ref_chassis; i++) {
- if (our_chassis == ha_chassis_grp->ref_chassis[i]) {
- bfd_setup_required = true;
- break;
- }
- }
- }
-
- if (bfd_setup_required) {
- const char *name;
- SSET_FOR_EACH (name, &grp_chassis) {
- sset_add(bfd_chassis, name);
- }
- }
- sset_destroy(&grp_chassis);
- }
-}
-
-void
-bfd_run(const struct ovsrec_interface_table *interface_table,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis_rec,
- const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
- const struct sbrec_sb_global_table *sb_global_table)
-{
- if (!chassis_rec) {
- return;
- }
- struct sset bfd_chassis = SSET_INITIALIZER(&bfd_chassis);
- bfd_calculate_chassis(chassis_rec, ha_chassis_grp_table,
- &bfd_chassis);
-
- /* Identify tunnels ports(connected to remote chassis id) to enable bfd */
- struct sset tunnels = SSET_INITIALIZER(&tunnels);
- struct sset bfd_ifaces = SSET_INITIALIZER(&bfd_ifaces);
- for (size_t k = 0; k < br_int->n_ports; k++) {
- const char *tunnel_id = smap_get(&br_int->ports[k]->external_ids,
- "ovn-chassis-id");
- if (tunnel_id) {
- char *chassis_name = NULL;
- char *port_name = br_int->ports[k]->name;
-
- sset_add(&tunnels, port_name);
-
- if (encaps_tunnel_id_parse(tunnel_id, &chassis_name, NULL)) {
- if (sset_contains(&bfd_chassis, chassis_name)) {
- sset_add(&bfd_ifaces, port_name);
- }
- free(chassis_name);
- }
- }
- }
-
- const struct sbrec_sb_global *sb
- = sbrec_sb_global_table_first(sb_global_table);
- struct smap bfd = SMAP_INITIALIZER(&bfd);
- smap_add(&bfd, "enable", "true");
-
- if (sb) {
- const char *min_rx = smap_get(&sb->options, "bfd-min-rx");
- const char *decay_min_rx = smap_get(&sb->options, "bfd-decay-min-rx");
- const char *min_tx = smap_get(&sb->options, "bfd-min-tx");
- const char *mult = smap_get(&sb->options, "bfd-mult");
- if (min_rx) {
- smap_add(&bfd, "min_rx", min_rx);
- }
- if (decay_min_rx) {
- smap_add(&bfd, "decay_min_rx", decay_min_rx);
- }
- if (min_tx) {
- smap_add(&bfd, "min_tx", min_tx);
- }
- if (mult) {
- smap_add(&bfd, "mult", mult);
- }
- }
-
- /* Enable or disable bfd */
- const struct ovsrec_interface *iface;
- OVSREC_INTERFACE_TABLE_FOR_EACH (iface, interface_table) {
- if (sset_contains(&tunnels, iface->name)) {
- if (sset_contains(&bfd_ifaces, iface->name)) {
- /* We need to enable BFD for this interface. Configure the
- * BFD params if
- * - If BFD was disabled earlier
- * - Or if CMS has updated BFD config options.
- */
- if (!smap_equal(&iface->bfd, &bfd)) {
- ovsrec_interface_verify_bfd(iface);
- ovsrec_interface_set_bfd(iface, &bfd);
- VLOG_INFO("Enabled BFD on interface %s", iface->name);
- }
- } else {
- /* We need to disable BFD for this interface if it was enabled
- * earlier. */
- if (smap_count(&iface->bfd)) {
- ovsrec_interface_verify_bfd(iface);
- ovsrec_interface_set_bfd(iface, NULL);
- VLOG_INFO("Disabled BFD on interface %s", iface->name);
- }
- }
- }
- }
-
- smap_destroy(&bfd);
- sset_destroy(&tunnels);
- sset_destroy(&bfd_ifaces);
- sset_destroy(&bfd_chassis);
-}
diff --git a/ovn/controller/bfd.h b/ovn/controller/bfd.h
deleted file mode 100644
index 17fab5323..000000000
--- a/ovn/controller/bfd.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Copyright (c) 2017 Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_BFD_H
-#define OVN_BFD_H 1
-
-struct hmap;
-struct ovsdb_idl;
-struct ovsdb_idl_index;
-struct ovsrec_bridge;
-struct ovsrec_interface_table;
-struct ovsrec_open_vswitch_table;
-struct sbrec_chassis;
-struct sbrec_sb_global_table;
-struct sbrec_ha_chassis_group_table;
-struct sset;
-
-void bfd_register_ovs_idl(struct ovsdb_idl *);
-
-void bfd_run(const struct ovsrec_interface_table *,
- const struct ovsrec_bridge *,
- const struct sbrec_chassis *,
- const struct sbrec_ha_chassis_group_table *,
- const struct sbrec_sb_global_table *);
-
-void bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
- struct sset *active_tunnels);
-
-#endif
diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
deleted file mode 100644
index ace0f811b..000000000
--- a/ovn/controller/binding.c
+++ /dev/null
@@ -1,764 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "binding.h"
-#include "ha-chassis.h"
-#include "lflow.h"
-#include "lport.h"
-
-#include "lib/bitmap.h"
-#include "openvswitch/poll-loop.h"
-#include "lib/sset.h"
-#include "lib/util.h"
-#include "lib/netdev.h"
-#include "lib/vswitch-idl.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn-controller.h"
-
-VLOG_DEFINE_THIS_MODULE(binding);
-
-#define OVN_QOS_TYPE "linux-htb"
-
-struct qos_queue {
- struct hmap_node node;
- uint32_t queue_id;
- uint32_t max_rate;
- uint32_t burst;
-};
-
-void
-binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
-{
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
-
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
-
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_qos);
-
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_status);
-
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
-}
-
-static void
-get_local_iface_ids(const struct ovsrec_bridge *br_int,
- struct shash *lport_to_iface,
- struct sset *local_lports,
- struct sset *egress_ifaces)
-{
- int i;
-
- for (i = 0; i < br_int->n_ports; i++) {
- const struct ovsrec_port *port_rec = br_int->ports[i];
- const char *iface_id;
- int j;
-
- if (!strcmp(port_rec->name, br_int->name)) {
- continue;
- }
-
- for (j = 0; j < port_rec->n_interfaces; j++) {
- const struct ovsrec_interface *iface_rec;
-
- iface_rec = port_rec->interfaces[j];
- iface_id = smap_get(&iface_rec->external_ids, "iface-id");
- int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0;
-
- if (iface_id && ofport > 0) {
- shash_add(lport_to_iface, iface_id, iface_rec);
- sset_add(local_lports, iface_id);
- }
-
- /* Check if this is a tunnel interface. */
- if (smap_get(&iface_rec->options, "remote_ip")) {
- const char *tunnel_iface
- = smap_get(&iface_rec->status, "tunnel_egress_iface");
- if (tunnel_iface) {
- sset_add(egress_ifaces, tunnel_iface);
- }
- }
- }
- }
-}
-
-static void
-add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_datapath_binding *datapath,
- bool has_local_l3gateway, int depth,
- struct hmap *local_datapaths)
-{
- uint32_t dp_key = datapath->tunnel_key;
- struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key);
- if (ld) {
- if (has_local_l3gateway) {
- ld->has_local_l3gateway = true;
- }
- return;
- }
-
- ld = xzalloc(sizeof *ld);
- hmap_insert(local_datapaths, &ld->hmap_node, dp_key);
- ld->datapath = datapath;
- ld->localnet_port = NULL;
- ld->has_local_l3gateway = has_local_l3gateway;
-
- if (depth >= 100) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "datapaths nested too deep");
- return;
- }
-
- struct sbrec_port_binding *target =
- sbrec_port_binding_index_init_row(sbrec_port_binding_by_datapath);
- sbrec_port_binding_index_set_datapath(target, datapath);
-
- const struct sbrec_port_binding *pb;
- SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
- sbrec_port_binding_by_datapath) {
- if (!strcmp(pb->type, "patch")) {
- const char *peer_name = smap_get(&pb->options, "peer");
- if (peer_name) {
- const struct sbrec_port_binding *peer;
-
- peer = lport_lookup_by_name(sbrec_port_binding_by_name,
- peer_name);
-
- if (peer && peer->datapath) {
- add_local_datapath__(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- peer->datapath, false,
- depth + 1, local_datapaths);
- ld->n_peer_ports++;
- ld->peer_ports = xrealloc(ld->peer_ports,
- ld->n_peer_ports *
- sizeof *ld->peer_ports);
- ld->peer_ports[ld->n_peer_ports - 1] = peer;
- }
- }
- }
- }
- sbrec_port_binding_index_destroy_row(target);
-}
-
-static void
-add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_datapath_binding *datapath,
- bool has_local_l3gateway, struct hmap *local_datapaths)
-{
- add_local_datapath__(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- datapath, has_local_l3gateway, 0, local_datapaths);
-}
-
-static void
-get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map)
-{
- uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0);
- uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0);
- uint32_t queue_id = smap_get_int(&pb->options, "qdisc_queue_id", 0);
-
- if ((!max_rate && !burst) || !queue_id) {
- /* Qos is not configured for this port. */
- return;
- }
-
- struct qos_queue *node = xzalloc(sizeof *node);
- hmap_insert(queue_map, &node->node, hash_int(queue_id, 0));
- node->max_rate = max_rate;
- node->burst = burst;
- node->queue_id = queue_id;
-}
-
-static const struct ovsrec_qos *
-get_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_qos_table *qos_table)
-{
- const struct ovsrec_qos *qos;
- OVSREC_QOS_TABLE_FOR_EACH (qos, qos_table) {
- if (!strcmp(qos->type, "linux-noop")) {
- return qos;
- }
- }
-
- if (!ovs_idl_txn) {
- return NULL;
- }
- qos = ovsrec_qos_insert(ovs_idl_txn);
- ovsrec_qos_set_type(qos, "linux-noop");
- return qos;
-}
-
-static bool
-set_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_port_table *port_table,
- const struct ovsrec_qos_table *qos_table,
- struct sset *egress_ifaces)
-{
- if (!ovs_idl_txn) {
- return false;
- }
-
- const struct ovsrec_qos *noop_qos = get_noop_qos(ovs_idl_txn, qos_table);
- if (!noop_qos) {
- return false;
- }
-
- const struct ovsrec_port *port;
- size_t count = 0;
-
- OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
- if (sset_contains(egress_ifaces, port->name)) {
- ovsrec_port_set_qos(port, noop_qos);
- count++;
- }
- if (sset_count(egress_ifaces) == count) {
- break;
- }
- }
- return true;
-}
-
-static void
-set_qos_type(struct netdev *netdev, const char *type)
-{
- int error = netdev_set_qos(netdev, type, NULL);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "%s: could not set qdisc type \"%s\" (%s)",
- netdev_get_name(netdev), type, ovs_strerror(error));
- }
-}
-
-static void
-setup_qos(const char *egress_iface, struct hmap *queue_map)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
- struct netdev *netdev_phy;
-
- if (!egress_iface) {
- /* Queues cannot be configured. */
- return;
- }
-
- int error = netdev_open(egress_iface, NULL, &netdev_phy);
- if (error) {
- VLOG_WARN_RL(&rl, "%s: could not open netdev (%s)",
- egress_iface, ovs_strerror(error));
- return;
- }
-
- /* Check current qdisc. */
- const char *qdisc_type;
- struct smap qdisc_details;
-
- smap_init(&qdisc_details);
- if (netdev_get_qos(netdev_phy, &qdisc_type, &qdisc_details) != 0 ||
- qdisc_type[0] == '\0') {
- smap_destroy(&qdisc_details);
- netdev_close(netdev_phy);
- /* Qos is not supported. */
- return;
- }
- smap_destroy(&qdisc_details);
-
- /* If we're not actually being requested to do any QoS:
- *
- * - If the current qdisc type is OVN_QOS_TYPE, then we clear the qdisc
- * type to "". Otherwise, it's possible that our own leftover qdisc
- * settings could cause strange behavior on egress. Also, QoS is
- * expensive and may waste CPU time even if it's not really in use.
- *
- * OVN isn't the only software that can configure qdiscs, and
- * physical interfaces are shared resources, so there is some risk in
- * this strategy: we could disrupt some other program's QoS.
- * Probably, to entirely avoid this possibility we would need to add
- * a configuration setting.
- *
- * - Otherwise leave the qdisc alone. */
- if (hmap_is_empty(queue_map)) {
- if (!strcmp(qdisc_type, OVN_QOS_TYPE)) {
- set_qos_type(netdev_phy, "");
- }
- netdev_close(netdev_phy);
- return;
- }
-
- /* Configure qdisc. */
- if (strcmp(qdisc_type, OVN_QOS_TYPE)) {
- set_qos_type(netdev_phy, OVN_QOS_TYPE);
- }
-
- /* Check and delete if needed. */
- struct netdev_queue_dump dump;
- unsigned int queue_id;
- struct smap queue_details;
- struct qos_queue *sb_info;
- struct hmap consistent_queues;
-
- smap_init(&queue_details);
- hmap_init(&consistent_queues);
- NETDEV_QUEUE_FOR_EACH (&queue_id, &queue_details, &dump, netdev_phy) {
- bool is_queue_needed = false;
-
- HMAP_FOR_EACH_WITH_HASH (sb_info, node, hash_int(queue_id, 0),
- queue_map) {
- is_queue_needed = true;
- if (sb_info->max_rate ==
- smap_get_int(&queue_details, "max-rate", 0)
- && sb_info->burst == smap_get_int(&queue_details, "burst", 0)) {
- /* This queue is consistent. */
- hmap_insert(&consistent_queues, &sb_info->node,
- hash_int(queue_id, 0));
- break;
- }
- }
-
- if (!is_queue_needed) {
- error = netdev_delete_queue(netdev_phy, queue_id);
- if (error) {
- VLOG_WARN_RL(&rl, "%s: could not delete queue %u (%s)",
- egress_iface, queue_id, ovs_strerror(error));
- }
- }
- }
-
- /* Create/Update queues. */
- HMAP_FOR_EACH (sb_info, node, queue_map) {
- if (hmap_contains(&consistent_queues, &sb_info->node)) {
- hmap_remove(&consistent_queues, &sb_info->node);
- continue;
- }
-
- smap_clear(&queue_details);
- smap_add_format(&queue_details, "max-rate", "%d", sb_info->max_rate);
- smap_add_format(&queue_details, "burst", "%d", sb_info->burst);
- error = netdev_set_queue(netdev_phy, sb_info->queue_id,
- &queue_details);
- if (error) {
- VLOG_WARN_RL(&rl, "%s: could not configure queue %u (%s)",
- egress_iface, sb_info->queue_id, ovs_strerror(error));
- }
- }
- smap_destroy(&queue_details);
- hmap_destroy(&consistent_queues);
- netdev_close(netdev_phy);
-}
-
-static void
-update_local_lport_ids(struct sset *local_lport_ids,
- const struct sbrec_port_binding *binding_rec)
-{
- char buf[16];
- snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
- binding_rec->datapath->tunnel_key,
- binding_rec->tunnel_key);
- sset_add(local_lport_ids, buf);
-}
-
-/*
- * Get the encap from the chassis for this port. The interface
- * may have an external_ids:encap-ip=<encap-ip> set; if so we
- * get the corresponding encap from the chassis.
- * If "encap-ip" external-ids is not set, we'll not bind the port
- * to any specific encap rec. and we'll pick up a tunnel port based on
- * the chassis name alone for the port.
- */
-static struct sbrec_encap *
-sbrec_get_port_encap(const struct sbrec_chassis *chassis_rec,
- const struct ovsrec_interface *iface_rec)
-{
-
- if (!iface_rec) {
- return NULL;
- }
-
- const char *encap_ip = smap_get(&iface_rec->external_ids, "encap-ip");
- if (!encap_ip) {
- return NULL;
- }
-
- struct sbrec_encap *best_encap = NULL;
- uint32_t best_type = 0;
- for (int i = 0; i < chassis_rec->n_encaps; i++) {
- if (!strcmp(chassis_rec->encaps[i]->ip, encap_ip)) {
- uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
- if (tun_type > best_type) {
- best_type = tun_type;
- best_encap = chassis_rec->encaps[i];
- }
- }
- }
- return best_encap;
-}
-
-static bool
-is_our_chassis(const struct sbrec_chassis *chassis_rec,
- const struct sbrec_port_binding *binding_rec,
- const struct sset *active_tunnels,
- const struct shash *lport_to_iface,
- const struct sset *local_lports)
-{
- const struct ovsrec_interface *iface_rec
- = shash_find_data(lport_to_iface, binding_rec->logical_port);
-
- bool our_chassis = false;
- if (iface_rec
- || (binding_rec->parent_port && binding_rec->parent_port[0] &&
- sset_contains(local_lports, binding_rec->parent_port))) {
- /* This port is in our chassis unless it is a localport. */
- our_chassis = strcmp(binding_rec->type, "localport");
- } else if (!strcmp(binding_rec->type, "l2gateway")) {
- const char *chassis_id = smap_get(&binding_rec->options,
- "l2gateway-chassis");
- our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
- } else if (!strcmp(binding_rec->type, "chassisredirect") ||
- !strcmp(binding_rec->type, "external")) {
- our_chassis = ha_chassis_group_contains(binding_rec->ha_chassis_group,
- chassis_rec) &&
- ha_chassis_group_is_active(binding_rec->ha_chassis_group,
- active_tunnels, chassis_rec);
- } else if (!strcmp(binding_rec->type, "l3gateway")) {
- const char *chassis_id = smap_get(&binding_rec->options,
- "l3gateway-chassis");
- our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
- }
-
- return our_chassis;
-}
-
-static void
-consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_txn *ovs_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sset *active_tunnels,
- const struct sbrec_chassis *chassis_rec,
- const struct sbrec_port_binding *binding_rec,
- struct hmap *qos_map,
- struct hmap *local_datapaths,
- struct shash *lport_to_iface,
- struct sset *local_lports,
- struct sset *local_lport_ids)
-{
- const struct ovsrec_interface *iface_rec
- = shash_find_data(lport_to_iface, binding_rec->logical_port);
-
- bool our_chassis = is_our_chassis(chassis_rec, binding_rec, active_tunnels,
- lport_to_iface, local_lports);
- if (iface_rec
- || (binding_rec->parent_port && binding_rec->parent_port[0] &&
- sset_contains(local_lports, binding_rec->parent_port))) {
- if (binding_rec->parent_port && binding_rec->parent_port[0]) {
- /* Add child logical port to the set of all local ports. */
- sset_add(local_lports, binding_rec->logical_port);
- }
- add_local_datapath(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- binding_rec->datapath, false, local_datapaths);
- if (iface_rec && qos_map && ovs_idl_txn) {
- get_qos_params(binding_rec, qos_map);
- }
- } else if (!strcmp(binding_rec->type, "l2gateway")) {
- if (our_chassis) {
- sset_add(local_lports, binding_rec->logical_port);
- add_local_datapath(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- binding_rec->datapath, false, local_datapaths);
- }
- } else if (!strcmp(binding_rec->type, "chassisredirect")) {
- if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
- chassis_rec)) {
- add_local_datapath(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- binding_rec->datapath, false, local_datapaths);
- }
- } else if (!strcmp(binding_rec->type, "l3gateway")) {
- if (our_chassis) {
- add_local_datapath(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- binding_rec->datapath, true, local_datapaths);
- }
- } else if (!strcmp(binding_rec->type, "localnet")) {
- /* Add all localnet ports to local_lports so that we allocate ct zones
- * for them. */
- sset_add(local_lports, binding_rec->logical_port);
- } else if (!strcmp(binding_rec->type, "external")) {
- if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
- chassis_rec)) {
- add_local_datapath(sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- binding_rec->datapath, false, local_datapaths);
- }
- }
-
- if (our_chassis
- || !strcmp(binding_rec->type, "patch")
- || !strcmp(binding_rec->type, "localport")
- || !strcmp(binding_rec->type, "vtep")
- || !strcmp(binding_rec->type, "localnet")) {
- update_local_lport_ids(local_lport_ids, binding_rec);
- }
-
- ovs_assert(ovnsb_idl_txn);
- if (ovnsb_idl_txn) {
- const char *vif_chassis = smap_get(&binding_rec->options,
- "requested-chassis");
- bool can_bind = !vif_chassis || !vif_chassis[0]
- || !strcmp(vif_chassis, chassis_rec->name)
- || !strcmp(vif_chassis, chassis_rec->hostname);
-
- if (can_bind && our_chassis) {
- if (binding_rec->chassis != chassis_rec) {
- if (binding_rec->chassis) {
- VLOG_INFO("Changing chassis for lport %s from %s to %s.",
- binding_rec->logical_port,
- binding_rec->chassis->name,
- chassis_rec->name);
- } else {
- VLOG_INFO("Claiming lport %s for this chassis.",
- binding_rec->logical_port);
- }
- for (int i = 0; i < binding_rec->n_mac; i++) {
- VLOG_INFO("%s: Claiming %s",
- binding_rec->logical_port, binding_rec->mac[i]);
- }
- sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
- }
- /* Check if the port encap binding, if any, has changed */
- struct sbrec_encap *encap_rec = sbrec_get_port_encap(
- chassis_rec, iface_rec);
- if (encap_rec && binding_rec->encap != encap_rec) {
- sbrec_port_binding_set_encap(binding_rec, encap_rec);
- }
- } else if (binding_rec->chassis == chassis_rec) {
- VLOG_INFO("Releasing lport %s from this chassis.",
- binding_rec->logical_port);
- if (binding_rec->encap)
- sbrec_port_binding_set_encap(binding_rec, NULL);
- sbrec_port_binding_set_chassis(binding_rec, NULL);
- } else if (our_chassis) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_INFO_RL(&rl,
- "Not claiming lport %s, chassis %s "
- "requested-chassis %s",
- binding_rec->logical_port,
- chassis_rec->name,
- vif_chassis);
- }
- }
-}
-
-static void
-consider_localnet_port(const struct sbrec_port_binding *binding_rec,
- struct hmap *local_datapaths)
-{
- struct local_datapath *ld
- = get_local_datapath(local_datapaths,
- binding_rec->datapath->tunnel_key);
- if (!ld) {
- return;
- }
-
- if (ld->localnet_port && strcmp(ld->localnet_port->logical_port,
- binding_rec->logical_port)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "localnet port '%s' already set for datapath "
- "'%"PRId64"', skipping the new port '%s'.",
- ld->localnet_port->logical_port,
- binding_rec->datapath->tunnel_key,
- binding_rec->logical_port);
- return;
- }
- ld->localnet_port = binding_rec;
-}
-
-void
-binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_txn *ovs_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct ovsrec_port_table *port_table,
- const struct ovsrec_qos_table *qos_table,
- const struct sbrec_port_binding_table *port_binding_table,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis_rec,
- const struct sset *active_tunnels,
- struct hmap *local_datapaths, struct sset *local_lports,
- struct sset *local_lport_ids)
-{
- if (!chassis_rec) {
- return;
- }
-
- const struct sbrec_port_binding *binding_rec;
- struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
- struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
- struct hmap qos_map;
-
- hmap_init(&qos_map);
- if (br_int) {
- get_local_iface_ids(br_int, &lport_to_iface, local_lports,
- &egress_ifaces);
- }
-
- /* Run through each binding record to see if it is resident on this
- * chassis and update the binding accordingly. This includes both
- * directly connected logical ports and children of those ports. */
- SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
- consider_local_datapath(ovnsb_idl_txn, ovs_idl_txn,
- sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- active_tunnels, chassis_rec, binding_rec,
- sset_is_empty(&egress_ifaces) ? NULL :
- &qos_map, local_datapaths, &lport_to_iface,
- local_lports, local_lport_ids);
-
- }
-
- /* Run through each binding record to see if it is a localnet port
- * on local datapaths discovered from above loop, and update the
- * corresponding local datapath accordingly. */
- SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
- if (!strcmp(binding_rec->type, "localnet")) {
- consider_localnet_port(binding_rec, local_datapaths);
- }
- }
-
- if (!sset_is_empty(&egress_ifaces)
- && set_noop_qos(ovs_idl_txn, port_table, qos_table, &egress_ifaces)) {
- const char *entry;
- SSET_FOR_EACH (entry, &egress_ifaces) {
- setup_qos(entry, &qos_map);
- }
- }
-
- shash_destroy(&lport_to_iface);
- sset_destroy(&egress_ifaces);
- hmap_destroy(&qos_map);
-}
-
-/* Returns true if port-binding changes potentially require flow changes on
- * the current chassis. Returns false if we are sure there is no impact. */
-bool
-binding_evaluate_port_binding_changes(
- const struct sbrec_port_binding_table *pb_table,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis_rec,
- struct sset *active_tunnels,
- struct sset *local_lports)
-{
- if (!chassis_rec) {
- return true;
- }
-
- bool changed = false;
-
- const struct sbrec_port_binding *binding_rec;
- struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
- struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
- if (br_int) {
- get_local_iface_ids(br_int, &lport_to_iface, local_lports,
- &egress_ifaces);
- }
- SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding_rec, pb_table) {
- /* XXX: currently OVSDB change tracking doesn't support getting old
- * data when the operation is update, so if a port-binding moved from
- * this chassis to another, there is no easy way to find out the
- * change. To workaround this problem, we just makes sure if
- * any port *related to* this chassis has any change, then trigger
- * recompute.
- *
- * - If a regular VIF is unbound from this chassis, the local ovsdb
- * interface table will be updated, which will trigger recompute.
- *
- * - If the port is not a regular VIF, always trigger recompute. */
- if (binding_rec->chassis == chassis_rec
- || is_our_chassis(chassis_rec, binding_rec,
- active_tunnels, &lport_to_iface, local_lports)
- || strcmp(binding_rec->type, "")) {
- changed = true;
- break;
- }
- }
-
- shash_destroy(&lport_to_iface);
- sset_destroy(&egress_ifaces);
- return changed;
-}
-
-/* Returns true if the database is all cleaned up, false if more work is
- * required. */
-bool
-binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_port_binding_table *port_binding_table,
- const struct sbrec_chassis *chassis_rec)
-{
- if (!ovnsb_idl_txn) {
- return false;
- }
- if (!chassis_rec) {
- return true;
- }
-
- const struct sbrec_port_binding *binding_rec;
- bool any_changes = false;
- SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
- if (binding_rec->chassis == chassis_rec) {
- if (binding_rec->encap)
- sbrec_port_binding_set_encap(binding_rec, NULL);
- sbrec_port_binding_set_chassis(binding_rec, NULL);
- any_changes = true;
- }
- }
-
- if (any_changes) {
- ovsdb_idl_txn_add_comment(
- ovnsb_idl_txn,
- "ovn-controller: removing all port bindings for '%s'",
- chassis_rec->name);
- }
-
- return !any_changes;
-}
diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
deleted file mode 100644
index 8d9492630..000000000
--- a/ovn/controller/binding.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef OVN_BINDING_H
-#define OVN_BINDING_H 1
-
-#include <stdbool.h>
-
-struct hmap;
-struct ovsdb_idl;
-struct ovsdb_idl_index;
-struct ovsdb_idl_txn;
-struct ovsrec_bridge;
-struct ovsrec_port_table;
-struct ovsrec_qos_table;
-struct sbrec_chassis;
-struct sbrec_port_binding_table;
-struct sset;
-
-void binding_register_ovs_idl(struct ovsdb_idl *);
-void binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_txn *ovs_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct ovsrec_port_table *,
- const struct ovsrec_qos_table *,
- const struct sbrec_port_binding_table *,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *,
- const struct sset *active_tunnels,
- struct hmap *local_datapaths,
- struct sset *local_lports, struct sset *local_lport_ids);
-bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_port_binding_table *,
- const struct sbrec_chassis *);
-bool binding_evaluate_port_binding_changes(
- const struct sbrec_port_binding_table *,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *,
- struct sset *active_tunnels,
- struct sset *local_lports);
-
-#endif /* ovn/binding.h */
diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
deleted file mode 100644
index 04b98d86c..000000000
--- a/ovn/controller/chassis.c
+++ /dev/null
@@ -1,671 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include <unistd.h>
-
-#include "chassis.h"
-
-#include "lib/smap.h"
-#include "lib/sset.h"
-#include "lib/vswitch-idl.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/vlog.h"
-#include "openvswitch/ofp-parse.h"
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn-controller.h"
-#include "lib/util.h"
-
-VLOG_DEFINE_THIS_MODULE(chassis);
-
-#ifndef HOST_NAME_MAX
-/* For windows. */
-#define HOST_NAME_MAX 255
-#endif /* HOST_NAME_MAX */
-
-/*
- * Structure to hold chassis specific state (currently just chassis-id)
- * to avoid database lookups when changes happen while the controller is
- * running.
- */
-struct chassis_info {
- /* Last ID we initialized the Chassis SB record with. */
- struct ds id;
-
- /* True if Chassis SB record is initialized, false otherwise. */
- uint32_t id_inited : 1;
-};
-
-static struct chassis_info chassis_state = {
- .id = DS_EMPTY_INITIALIZER,
- .id_inited = false,
-};
-
-static void
-chassis_info_set_id(struct chassis_info *info, const char *id)
-{
- ds_clear(&info->id);
- ds_put_cstr(&info->id, id);
- info->id_inited = true;
-}
-
-static bool
-chassis_info_id_inited(const struct chassis_info *info)
-{
- return info->id_inited;
-}
-
-static const char *
-chassis_info_id(const struct chassis_info *info)
-{
- return ds_cstr_ro(&info->id);
-}
-
-/*
- * Structure for storing the chassis config parsed from the ovs table.
- */
-struct ovs_chassis_cfg {
- /* Single string fields parsed from external-ids. */
- const char *hostname;
- const char *bridge_mappings;
- const char *datapath_type;
- const char *encap_csum;
- const char *cms_options;
- const char *chassis_macs;
-
- /* Set of encap types parsed from the 'ovn-encap-type' external-id. */
- struct sset encap_type_set;
- /* Set of encap IPs parsed from the 'ovn-encap-type' external-id. */
- struct sset encap_ip_set;
- /* Interface type list formatted in the OVN-SB Chassis required format. */
- struct ds iface_types;
-};
-
-static void
-ovs_chassis_cfg_init(struct ovs_chassis_cfg *cfg)
-{
- sset_init(&cfg->encap_type_set);
- sset_init(&cfg->encap_ip_set);
- ds_init(&cfg->iface_types);
-}
-
-static void
-ovs_chassis_cfg_destroy(struct ovs_chassis_cfg *cfg)
-{
- sset_destroy(&cfg->encap_type_set);
- sset_destroy(&cfg->encap_ip_set);
- ds_destroy(&cfg->iface_types);
-}
-
-void
-chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl)
-{
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_iface_types);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_datapath_type);
-}
-
-static const char *
-get_hostname(const struct smap *ext_ids)
-{
- const char *hostname = smap_get_def(ext_ids, "hostname", "");
-
- if (strlen(hostname) == 0) {
- static char hostname_[HOST_NAME_MAX + 1];
-
- if (gethostname(hostname_, sizeof(hostname_))) {
- hostname_[0] = 0;
- }
-
- return &hostname_[0];
- }
-
- return hostname;
-}
-
-static const char *
-get_bridge_mappings(const struct smap *ext_ids)
-{
- return smap_get_def(ext_ids, "ovn-bridge-mappings", "");
-}
-
-static const char *
-get_chassis_mac_mappings(const struct smap *ext_ids)
-{
- return smap_get_def(ext_ids, "ovn-chassis-mac-mappings", "");
-}
-
-static const char *
-get_cms_options(const struct smap *ext_ids)
-{
- return smap_get_def(ext_ids, "ovn-cms-options", "");
-}
-
-static const char *
-get_encap_csum(const struct smap *ext_ids)
-{
- return smap_get_def(ext_ids, "ovn-encap-csum", "true");
-}
-
-static const char *
-get_datapath_type(const struct ovsrec_bridge *br_int)
-{
- if (br_int && br_int->datapath_type) {
- return br_int->datapath_type;
- }
-
- return "";
-}
-
-static void
-update_chassis_transport_zones(const struct sset *transport_zones,
- const struct sbrec_chassis *chassis_rec)
-{
- struct sset chassis_tzones_set = SSET_INITIALIZER(&chassis_tzones_set);
- for (int i = 0; i < chassis_rec->n_transport_zones; i++) {
- sset_add(&chassis_tzones_set, chassis_rec->transport_zones[i]);
- }
-
- /* Only update the transport zones if something changed */
- if (!sset_equals(transport_zones, &chassis_tzones_set)) {
- const char **ls_arr = sset_array(transport_zones);
- sbrec_chassis_set_transport_zones(chassis_rec, ls_arr,
- sset_count(transport_zones));
- free(ls_arr);
- }
-
- sset_destroy(&chassis_tzones_set);
-}
-
-/*
- * Parse an ovs 'encap_type' string and stores the resulting types in the
- * 'encap_type_set' string set.
- */
-static bool
-chassis_parse_ovs_encap_type(const char *encap_type,
- struct sset *encap_type_set)
-{
- sset_from_delimited_string(encap_type_set, encap_type, ",");
-
- const char *type;
-
- SSET_FOR_EACH (type, encap_type_set) {
- if (!get_tunnel_type(type)) {
- VLOG_INFO("Unknown tunnel type: %s", type);
- }
- }
-
- return true;
-}
-
-/*
- * Parse an ovs 'encap_ip' string and stores the resulting IP representations
- * in the 'encap_ip_set' string set.
- */
-static bool
-chassis_parse_ovs_encap_ip(const char *encap_ip, struct sset *encap_ip_set)
-{
- sset_from_delimited_string(encap_ip_set, encap_ip, ",");
- return true;
-}
-
-/*
- * Parse the ovs 'iface_types' and store them in the format required by the
- * Chassis record.
- */
-static bool
-chassis_parse_ovs_iface_types(char **iface_types, size_t n_iface_types,
- struct ds *iface_types_str)
-{
- for (size_t i = 0; i < n_iface_types; i++) {
- ds_put_format(iface_types_str, "%s,", iface_types[i]);
- }
- ds_chomp(iface_types_str, ',');
- return true;
-}
-
-/*
- * Parse the 'ovs_table' entry and populate 'ovs_cfg'.
- */
-static bool
-chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table *ovs_table,
- const struct ovsrec_bridge *br_int,
- struct ovs_chassis_cfg *ovs_cfg)
-{
- const struct ovsrec_open_vswitch *cfg =
- ovsrec_open_vswitch_table_first(ovs_table);
-
- if (!cfg) {
- VLOG_INFO("No Open_vSwitch row defined.");
- return false;
- }
-
- const char *encap_type = smap_get(&cfg->external_ids, "ovn-encap-type");
- const char *encap_ips = smap_get(&cfg->external_ids, "ovn-encap-ip");
- if (!encap_type || !encap_ips) {
- VLOG_INFO("Need to specify an encap type and ip");
- return false;
- }
-
- ovs_cfg->hostname = get_hostname(&cfg->external_ids);
- ovs_cfg->bridge_mappings = get_bridge_mappings(&cfg->external_ids);
- ovs_cfg->datapath_type = get_datapath_type(br_int);
- ovs_cfg->encap_csum = get_encap_csum(&cfg->external_ids);
- ovs_cfg->cms_options = get_cms_options(&cfg->external_ids);
- ovs_cfg->chassis_macs = get_chassis_mac_mappings(&cfg->external_ids);
-
- if (!chassis_parse_ovs_encap_type(encap_type, &ovs_cfg->encap_type_set)) {
- return false;
- }
-
- if (!chassis_parse_ovs_encap_ip(encap_ips, &ovs_cfg->encap_ip_set)) {
- sset_destroy(&ovs_cfg->encap_type_set);
- return false;
- }
-
- if (!chassis_parse_ovs_iface_types(cfg->iface_types,
- cfg->n_iface_types,
- &ovs_cfg->iface_types)) {
- sset_destroy(&ovs_cfg->encap_type_set);
- sset_destroy(&ovs_cfg->encap_ip_set);
- }
-
- return true;
-}
-
-static void
-chassis_build_external_ids(struct smap *ext_ids, const char *bridge_mappings,
- const char *datapath_type, const char *cms_options,
- const char *chassis_macs, const char *iface_types)
-{
- smap_replace(ext_ids, "ovn-bridge-mappings", bridge_mappings);
- smap_replace(ext_ids, "datapath-type", datapath_type);
- smap_replace(ext_ids, "ovn-cms-options", cms_options);
- smap_replace(ext_ids, "iface-types", iface_types);
- smap_replace(ext_ids, "ovn-chassis-mac-mappings", chassis_macs);
-}
-
-/*
- * Returns true if any external-id doesn't match the values in 'chassis-rec'.
- */
-static bool
-chassis_external_ids_changed(const char *bridge_mappings,
- const char *datapath_type,
- const char *cms_options,
- const char *chassis_macs,
- const struct ds *iface_types,
- const struct sbrec_chassis *chassis_rec)
-{
- const char *chassis_bridge_mappings =
- get_bridge_mappings(&chassis_rec->external_ids);
-
- if (strcmp(bridge_mappings, chassis_bridge_mappings)) {
- return true;
- }
-
- const char *chassis_datapath_type =
- smap_get_def(&chassis_rec->external_ids, "datapath-type", "");
-
- if (strcmp(datapath_type, chassis_datapath_type)) {
- return true;
- }
-
- const char *chassis_cms_options =
- get_cms_options(&chassis_rec->external_ids);
-
- if (strcmp(cms_options, chassis_cms_options)) {
- return true;
- }
-
- const char *chassis_mac_mappings =
- get_chassis_mac_mappings(&chassis_rec->external_ids);
- if (strcmp(chassis_macs, chassis_mac_mappings)) {
- return true;
- }
-
- const char *chassis_iface_types =
- smap_get_def(&chassis_rec->external_ids, "iface-types", "");
-
- if (strcmp(ds_cstr_ro(iface_types), chassis_iface_types)) {
- return true;
- }
-
- return false;
-}
-
-/*
- * Returns true if the tunnel config obtained by combining 'encap_type_set'
- * with 'encap_ip_set' and 'encap_csum' doesn't match the values in
- * 'chassis-rec'.
- */
-static bool
-chassis_tunnels_changed(const struct sset *encap_type_set,
- const struct sset *encap_ip_set,
- const char *encap_csum,
- const struct sbrec_chassis *chassis_rec)
-{
- size_t encap_type_count = 0;
-
- for (int i = 0; i < chassis_rec->n_encaps; i++) {
- if (strcmp(chassis_rec->name, chassis_rec->encaps[i]->chassis_name)) {
- return true;
- }
-
- if (!sset_contains(encap_type_set, chassis_rec->encaps[i]->type)) {
- return true;
- }
- encap_type_count++;
-
- if (!sset_contains(encap_ip_set, chassis_rec->encaps[i]->ip)) {
- return true;
- }
-
- if (strcmp(smap_get_def(&chassis_rec->encaps[i]->options, "csum", ""),
- encap_csum)) {
- return true;
- }
- }
-
- size_t tunnel_count =
- sset_count(encap_type_set) * sset_count(encap_ip_set);
-
- if (tunnel_count != chassis_rec->n_encaps) {
- return true;
- }
-
- if (sset_count(encap_type_set) != encap_type_count) {
- return true;
- }
-
- return false;
-}
-
-/*
- * Build the new encaps config (full mesh of 'encap_type_set' and
- * 'encap_ip_set'). Allocates and stores the new 'n_encap' Encap records in
- * 'encaps'.
- */
-static struct sbrec_encap **
-chassis_build_encaps(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sset *encap_type_set,
- const struct sset *encap_ip_set,
- const char *chassis_id,
- const char *encap_csum,
- size_t *n_encap)
-{
- size_t tunnel_count = 0;
-
- struct sbrec_encap **encaps =
- xmalloc(sset_count(encap_type_set) * sset_count(encap_ip_set) *
- sizeof(*encaps));
- const struct smap options = SMAP_CONST1(&options, "csum", encap_csum);
-
- const char *encap_ip;
- const char *encap_type;
-
- SSET_FOR_EACH (encap_ip, encap_ip_set) {
- SSET_FOR_EACH (encap_type, encap_type_set) {
- struct sbrec_encap *encap = sbrec_encap_insert(ovnsb_idl_txn);
-
- sbrec_encap_set_type(encap, encap_type);
- sbrec_encap_set_ip(encap, encap_ip);
- sbrec_encap_set_options(encap, &options);
- sbrec_encap_set_chassis_name(encap, chassis_id);
-
- encaps[tunnel_count] = encap;
- tunnel_count++;
- }
- }
-
- *n_encap = tunnel_count;
- return encaps;
-}
-
-/*
- * Returns a pointer to a chassis record from 'chassis_table' that
- * matches at least one tunnel config.
- */
-static const struct sbrec_chassis *
-chassis_get_stale_record(const struct sbrec_chassis_table *chassis_table,
- const struct ovs_chassis_cfg *ovs_cfg,
- const char *chassis_id)
-{
- const struct sbrec_chassis *chassis_rec;
-
- SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
- for (size_t i = 0; i < chassis_rec->n_encaps; i++) {
- if (sset_contains(&ovs_cfg->encap_type_set,
- chassis_rec->encaps[i]->type) &&
- sset_contains(&ovs_cfg->encap_ip_set,
- chassis_rec->encaps[i]->ip)) {
- return chassis_rec;
- }
- if (strcmp(chassis_rec->name, chassis_id) == 0) {
- return chassis_rec;
- }
- }
- }
-
- return NULL;
-}
-
-/* If this is a chassis config update after we initialized the record once
- * then we should always be able to find it with the ID we saved in
- * chassis_state.
- * Otherwise (i.e., first time we create the record) then we check if there's
- * a stale record from a previous controller run that didn't end gracefully
- * and reuse it. If not then we create a new record.
- */
-static const struct sbrec_chassis *
-chassis_get_record(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- const struct sbrec_chassis_table *chassis_table,
- const struct ovs_chassis_cfg *ovs_cfg,
- const char *chassis_id)
-{
- const struct sbrec_chassis *chassis_rec;
-
- if (chassis_info_id_inited(&chassis_state)) {
- chassis_rec = chassis_lookup_by_name(sbrec_chassis_by_name,
- chassis_info_id(&chassis_state));
- if (!chassis_rec) {
- VLOG_WARN("Could not find Chassis : stored (%s) ovs (%s)",
- chassis_info_id(&chassis_state), chassis_id);
- }
- } else {
- chassis_rec =
- chassis_get_stale_record(chassis_table, ovs_cfg, chassis_id);
-
- if (!chassis_rec && ovnsb_idl_txn) {
- chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn);
- }
- }
- return chassis_rec;
-}
-
-/* Update a Chassis record based on the config in the ovs config. */
-static void
-chassis_update(const struct sbrec_chassis *chassis_rec,
- struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct ovs_chassis_cfg *ovs_cfg,
- const char *chassis_id,
- const struct sset *transport_zones)
-{
- if (strcmp(chassis_id, chassis_rec->name)) {
- sbrec_chassis_set_name(chassis_rec, chassis_id);
- }
-
- if (strcmp(ovs_cfg->hostname, chassis_rec->hostname)) {
- sbrec_chassis_set_hostname(chassis_rec, ovs_cfg->hostname);
- }
-
- if (chassis_external_ids_changed(ovs_cfg->bridge_mappings,
- ovs_cfg->datapath_type,
- ovs_cfg->cms_options,
- ovs_cfg->chassis_macs,
- &ovs_cfg->iface_types,
- chassis_rec)) {
- struct smap ext_ids;
-
- smap_clone(&ext_ids, &chassis_rec->external_ids);
- chassis_build_external_ids(&ext_ids, ovs_cfg->bridge_mappings,
- ovs_cfg->datapath_type,
- ovs_cfg->cms_options,
- ovs_cfg->chassis_macs,
- ds_cstr_ro(&ovs_cfg->iface_types));
- sbrec_chassis_verify_external_ids(chassis_rec);
- sbrec_chassis_set_external_ids(chassis_rec, &ext_ids);
- smap_destroy(&ext_ids);
- }
-
- update_chassis_transport_zones(transport_zones, chassis_rec);
-
- /* If any of the encaps should change, update them. */
- bool tunnels_changed =
- chassis_tunnels_changed(&ovs_cfg->encap_type_set,
- &ovs_cfg->encap_ip_set, ovs_cfg->encap_csum,
- chassis_rec);
- if (!tunnels_changed) {
- return;
- }
-
- struct sbrec_encap **encaps;
- size_t n_encap;
-
- encaps =
- chassis_build_encaps(ovnsb_idl_txn, &ovs_cfg->encap_type_set,
- &ovs_cfg->encap_ip_set, chassis_id,
- ovs_cfg->encap_csum, &n_encap);
- sbrec_chassis_set_encaps(chassis_rec, encaps, n_encap);
- free(encaps);
-}
-
-/* Returns this chassis's Chassis record, if it is available. */
-const struct sbrec_chassis *
-chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- const struct ovsrec_open_vswitch_table *ovs_table,
- const struct sbrec_chassis_table *chassis_table,
- const char *chassis_id,
- const struct ovsrec_bridge *br_int,
- const struct sset *transport_zones)
-{
- struct ovs_chassis_cfg ovs_cfg;
-
- /* Get the chassis config from the ovs table. */
- ovs_chassis_cfg_init(&ovs_cfg);
- if (!chassis_parse_ovs_config(ovs_table, br_int, &ovs_cfg)) {
- return NULL;
- }
-
- const struct sbrec_chassis *chassis_rec =
- chassis_get_record(ovnsb_idl_txn, sbrec_chassis_by_name,
- chassis_table, &ovs_cfg, chassis_id);
-
- /* If we found (or created) a record, update it with the correct config
- * and store the current chassis_id for fast lookup in case it gets
- * modified in the ovs table.
- */
- if (chassis_rec && ovnsb_idl_txn) {
- chassis_update(chassis_rec, ovnsb_idl_txn, &ovs_cfg, chassis_id,
- transport_zones);
- chassis_info_set_id(&chassis_state, chassis_id);
- ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
- "ovn-controller: registering chassis '%s'",
- chassis_id);
- }
-
- ovs_chassis_cfg_destroy(&ovs_cfg);
- return chassis_rec;
-}
-
-bool
-chassis_get_mac(const struct sbrec_chassis *chassis_rec,
- const char *bridge_mapping,
- struct eth_addr *chassis_mac)
-{
- const char *tokens
- = get_chassis_mac_mappings(&chassis_rec->external_ids);
- if (!tokens[0]) {
- return false;
- }
-
- char *save_ptr = NULL;
- bool ret = false;
- char *tokstr = xstrdup(tokens);
-
- /* Format for a chassis mac configuration is:
- * ovn-chassis-mac-mappings="bridge-name1:MAC1,bridge-name2:MAC2"
- */
- for (char *token = strtok_r(tokstr, ",", &save_ptr);
- token != NULL;
- token = strtok_r(NULL, ",", &save_ptr)) {
- char *save_ptr2 = NULL;
- char *chassis_mac_bridge = strtok_r(token, ":", &save_ptr2);
- char *chassis_mac_str = strtok_r(NULL, "", &save_ptr2);
-
- if (!strcmp(chassis_mac_bridge, bridge_mapping)) {
- struct eth_addr temp_mac;
-
- /* Return the first chassis mac. */
- char *err_str = str_to_mac(chassis_mac_str, &temp_mac);
- if (err_str) {
- free(err_str);
- continue;
- }
-
- ret = true;
- *chassis_mac = temp_mac;
- break;
- }
- }
-
- free(tokstr);
- return ret;
-}
-
-/* Returns true if the database is all cleaned up, false if more work is
- * required. */
-bool
-chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_chassis *chassis_rec)
-{
- if (!chassis_rec) {
- return true;
- }
- if (ovnsb_idl_txn) {
- ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
- "ovn-controller: unregistering chassis '%s'",
- chassis_rec->name);
- sbrec_chassis_delete(chassis_rec);
- }
- return false;
-}
-
-/*
- * Returns the last initialized chassis-id.
- */
-const char *
-chassis_get_id(void)
-{
- if (chassis_info_id_inited(&chassis_state)) {
- return chassis_info_id(&chassis_state);
- }
-
- return NULL;
-}
diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h
deleted file mode 100644
index 16a131a3b..000000000
--- a/ovn/controller/chassis.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_CHASSIS_H
-#define OVN_CHASSIS_H 1
-
-#include <stdbool.h>
-
-struct ovsdb_idl;
-struct ovsdb_idl_index;
-struct ovsdb_idl_txn;
-struct ovsrec_bridge;
-struct ovsrec_open_vswitch_table;
-struct sbrec_chassis;
-struct sbrec_chassis_table;
-struct sset;
-struct eth_addr;
-
-void chassis_register_ovs_idl(struct ovsdb_idl *);
-const struct sbrec_chassis *chassis_run(
- struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- const struct ovsrec_open_vswitch_table *,
- const struct sbrec_chassis_table *,
- const char *chassis_id, const struct ovsrec_bridge *br_int,
- const struct sset *transport_zones);
-bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_chassis *);
-bool chassis_get_mac(const struct sbrec_chassis *chassis,
- const char *bridge_mapping,
- struct eth_addr *chassis_mac);
-const char *chassis_get_id(void);
-
-#endif /* ovn/chassis.h */
diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
deleted file mode 100644
index d4a436df3..000000000
--- a/ovn/controller/encaps.c
+++ /dev/null
@@ -1,409 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "encaps.h"
-
-#include "lib/hash.h"
-#include "lib/sset.h"
-#include "lib/util.h"
-#include "lib/vswitch-idl.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn-controller.h"
-
-VLOG_DEFINE_THIS_MODULE(encaps);
-
-/*
- * Given there could be multiple tunnels with different IPs to the same
- * chassis we annotate the ovn-chassis-id with
- * <chassis_name>OVN_MVTEP_CHASSISID_DELIM<IP>.
- */
-#define OVN_MVTEP_CHASSISID_DELIM '@'
-
-void
-encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
-{
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options);
-}
-
-/* Enough context to create a new tunnel, using tunnel_add(). */
-struct tunnel_ctx {
- /* Maps from a chassis name to "struct chassis_node *". */
- struct shash chassis;
-
- /* Names of all ports in the bridge, to allow checking uniqueness when
- * adding a new tunnel. */
- struct sset port_names;
-
- struct ovsdb_idl_txn *ovs_txn;
- const struct ovsrec_bridge *br_int;
-};
-
-struct chassis_node {
- const struct ovsrec_port *port;
- const struct ovsrec_bridge *bridge;
-};
-
-static char *
-tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
-{
- int i;
-
- for (i = 0; i < UINT16_MAX; i++) {
- char *port_name;
- port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
-
- if (!sset_contains(&tc->port_names, port_name)) {
- return port_name;
- }
-
- free(port_name);
- }
-
- return NULL;
-}
-
-/*
- * Returns a tunnel-id of the form 'chassis_id'-delimiter-'encap_ip'.
- */
-char *
-encaps_tunnel_id_create(const char *chassis_id, const char *encap_ip)
-{
- return xasprintf("%s%c%s", chassis_id, OVN_MVTEP_CHASSISID_DELIM,
- encap_ip);
-}
-
-/*
- * Parses a 'tunnel_id' of the form <chassis_name><delimiter><IP>.
- * If the 'chassis_id' argument is not NULL the function will allocate memory
- * and store the chassis-id part of the tunnel-id at '*chassis_id'.
- * If the 'encap_ip' argument is not NULL the function will allocate memory
- * and store the encapsulation IP part of the tunnel-id at '*encap_ip'.
- */
-bool
-encaps_tunnel_id_parse(const char *tunnel_id, char **chassis_id,
- char **encap_ip)
-{
- /* Find the delimiter. Fail if there is no delimiter or if <chassis_name>
- * or <IP> is the empty string.*/
- const char *d = strchr(tunnel_id, OVN_MVTEP_CHASSISID_DELIM);
- if (d == tunnel_id || !d || !d[1]) {
- return false;
- }
-
- if (chassis_id) {
- *chassis_id = xmemdup0(tunnel_id, d - tunnel_id);
- }
- if (encap_ip) {
- *encap_ip = xstrdup(d + 1);
- }
- return true;
-}
-
-/*
- * Returns true if 'tunnel_id' contains 'chassis_id' and, if specified, the
- * given 'encap_ip'. Returns false otherwise.
- */
-bool
-encaps_tunnel_id_match(const char *tunnel_id, const char *chassis_id,
- const char *encap_ip)
-{
- while (*tunnel_id == *chassis_id) {
- if (!*tunnel_id) {
- /* 'tunnel_id' and 'chassis_id' are equal strings. This is a
- * mismatch because 'tunnel_id' is missing the delimiter and IP. */
- return false;
- }
- tunnel_id++;
- chassis_id++;
- }
-
- /* We found the first byte that disagrees between 'tunnel_id' and
- * 'chassis_id'. If we consumed all of 'chassis_id' and arrived at the
- * delimiter in 'tunnel_id' (and if 'encap_ip' is correct, if it was
- * supplied), it's a match. */
- return (*tunnel_id == OVN_MVTEP_CHASSISID_DELIM
- && *chassis_id == '\0'
- && (!encap_ip || !strcmp(tunnel_id + 1, encap_ip)));
-}
-
-static void
-tunnel_add(struct tunnel_ctx *tc, const struct sbrec_sb_global *sbg,
- const char *new_chassis_id, const struct sbrec_encap *encap)
-{
- struct smap options = SMAP_INITIALIZER(&options);
- smap_add(&options, "remote_ip", encap->ip);
- smap_add(&options, "key", "flow");
- const char *dst_port = smap_get(&encap->options, "dst_port");
- const char *csum = smap_get(&encap->options, "csum");
- char *tunnel_entry_id = NULL;
-
- /*
- * Since a chassis may have multiple encap-ip, we can't just add the
- * chassis name as as the "ovn-chassis-id" for the port; we use the
- * combination of the chassis_name and the encap-ip to identify
- * a specific tunnel to the chassis.
- */
- tunnel_entry_id = encaps_tunnel_id_create(new_chassis_id, encap->ip);
- if (csum && (!strcmp(csum, "true") || !strcmp(csum, "false"))) {
- smap_add(&options, "csum", csum);
- }
- if (dst_port) {
- smap_add(&options, "dst_port", dst_port);
- }
-
- /* Add auth info if ipsec is enabled. */
- if (sbg->ipsec) {
- smap_add(&options, "remote_name", new_chassis_id);
- }
-
- /* If there's an existing chassis record that does not need any change,
- * keep it. Otherwise, create a new record (if there was an existing
- * record, the new record will supplant it and encaps_run() will delete
- * it). */
- struct chassis_node *chassis = shash_find_data(&tc->chassis,
- tunnel_entry_id);
- if (chassis
- && chassis->port->n_interfaces == 1
- && !strcmp(chassis->port->interfaces[0]->type, encap->type)
- && smap_equal(&chassis->port->interfaces[0]->options, &options)) {
- shash_find_and_delete(&tc->chassis, tunnel_entry_id);
- free(chassis);
- goto exit;
- }
-
- /* Choose a name for the new port. If we're replacing an old port, reuse
- * its name, otherwise generate a new, unique name. */
- char *port_name = (chassis
- ? xstrdup(chassis->port->name)
- : tunnel_create_name(tc, new_chassis_id));
- if (!port_name) {
- VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
- new_chassis_id);
- goto exit;
- }
-
- struct ovsrec_interface *iface = ovsrec_interface_insert(tc->ovs_txn);
- ovsrec_interface_set_name(iface, port_name);
- ovsrec_interface_set_type(iface, encap->type);
- ovsrec_interface_set_options(iface, &options);
-
- struct ovsrec_port *port = ovsrec_port_insert(tc->ovs_txn);
- ovsrec_port_set_name(port, port_name);
- ovsrec_port_set_interfaces(port, &iface, 1);
- const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", tunnel_entry_id);
- ovsrec_port_set_external_ids(port, &id);
-
- ovsrec_bridge_update_ports_addvalue(tc->br_int, port);
-
- sset_add_and_free(&tc->port_names, port_name);
-
-exit:
- free(tunnel_entry_id);
- smap_destroy(&options);
-}
-
-struct sbrec_encap *
-preferred_encap(const struct sbrec_chassis *chassis_rec)
-{
- struct sbrec_encap *best_encap = NULL;
- uint32_t best_type = 0;
-
- for (int i = 0; i < chassis_rec->n_encaps; i++) {
- uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
- if (tun_type > best_type) {
- best_type = tun_type;
- best_encap = chassis_rec->encaps[i];
- }
- }
-
- return best_encap;
-}
-
-/*
- * For each peer chassis, get a preferred tunnel type and create as many tunnels
- * as there are VTEP of that type (differentiated by remote_ip) on that chassis.
- */
-static int
-chassis_tunnel_add(const struct sbrec_chassis *chassis_rec, const struct sbrec_sb_global *sbg, struct tunnel_ctx *tc)
-{
- struct sbrec_encap *encap = preferred_encap(chassis_rec);
- int tuncnt = 0;
-
- if (!encap) {
- VLOG_INFO("chassis_tunnel_add: No supported encaps for '%s'", chassis_rec->name);
- return tuncnt;
- }
-
- uint32_t pref_type = get_tunnel_type(encap->type);
- for (int i = 0; i < chassis_rec->n_encaps; i++) {
- uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
- if (tun_type != pref_type) {
- continue;
- }
- tunnel_add(tc, sbg, chassis_rec->name, chassis_rec->encaps[i]);
- tuncnt++;
- }
- return tuncnt;
-}
-
-/*
-* Returns true if transport_zones and chassis_rec->transport_zones
-* have at least one common transport zone.
-*/
-static bool
-chassis_tzones_overlap(const struct sset *transport_zones,
- const struct sbrec_chassis *chassis_rec)
-{
- /* If neither Chassis belongs to any transport zones, return true to
- * form a tunnel between them */
- if (!chassis_rec->n_transport_zones && sset_is_empty(transport_zones)) {
- return true;
- }
-
- for (int i = 0; i < chassis_rec->n_transport_zones; i++) {
- if (sset_contains(transport_zones, chassis_rec->transport_zones[i])) {
- return true;
- }
- }
- return false;
-}
-
-void
-encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis_table *chassis_table,
- const char *chassis_id,
- const struct sbrec_sb_global *sbg,
- const struct sset *transport_zones)
-{
- if (!ovs_idl_txn || !br_int) {
- return;
- }
-
- const struct sbrec_chassis *chassis_rec;
- const struct ovsrec_bridge *br;
-
- struct tunnel_ctx tc = {
- .chassis = SHASH_INITIALIZER(&tc.chassis),
- .port_names = SSET_INITIALIZER(&tc.port_names),
- .br_int = br_int
- };
-
- tc.ovs_txn = ovs_idl_txn;
- ovsdb_idl_txn_add_comment(tc.ovs_txn,
- "ovn-controller: modifying OVS tunnels '%s'",
- chassis_id);
-
- /* Collect all port names into tc.port_names.
- *
- * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
- OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
- for (size_t i = 0; i < br->n_ports; i++) {
- const struct ovsrec_port *port = br->ports[i];
- sset_add(&tc.port_names, port->name);
-
- /*
- * note that the id here is not just the chassis name, but the
- * combination of <chassis_name><delim><encap_ip>
- */
- const char *id = smap_get(&port->external_ids, "ovn-chassis-id");
- if (id) {
- if (!shash_find(&tc.chassis, id)) {
- struct chassis_node *chassis = xzalloc(sizeof *chassis);
- chassis->bridge = br;
- chassis->port = port;
- shash_add_assert(&tc.chassis, id, chassis);
- } else {
- /* Duplicate port for ovn-chassis-id. Arbitrarily choose
- * to delete this one. */
- ovsrec_bridge_update_ports_delvalue(br, port);
- }
- }
- }
- }
-
- SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
- if (strcmp(chassis_rec->name, chassis_id)) {
- /* Create tunnels to the other Chassis belonging to the
- * same transport zone */
- if (!chassis_tzones_overlap(transport_zones, chassis_rec)) {
- VLOG_DBG("Skipping encap creation for Chassis '%s' because "
- "it belongs to different transport zones",
- chassis_rec->name);
- continue;
- }
-
- if (chassis_tunnel_add(chassis_rec, sbg, &tc) == 0) {
- VLOG_INFO("Creating encap for '%s' failed", chassis_rec->name);
- continue;
- }
- }
- }
-
- /* Delete any existing OVN tunnels that were not still around. */
- struct shash_node *node, *next_node;
- SHASH_FOR_EACH_SAFE (node, next_node, &tc.chassis) {
- struct chassis_node *chassis = node->data;
- ovsrec_bridge_update_ports_delvalue(chassis->bridge, chassis->port);
- shash_delete(&tc.chassis, node);
- free(chassis);
- }
- shash_destroy(&tc.chassis);
- sset_destroy(&tc.port_names);
-}
-
-/* Returns true if the database is all cleaned up, false if more work is
- * required. */
-bool
-encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge *br_int)
-{
- if (!br_int) {
- return true;
- }
-
- /* Delete all the OVS-created tunnels from the integration bridge. */
- struct ovsrec_port **ports
- = xmalloc(sizeof *br_int->ports * br_int->n_ports);
- size_t n = 0;
- for (size_t i = 0; i < br_int->n_ports; i++) {
- if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) {
- ports[n++] = br_int->ports[i];
- }
- }
-
- bool any_changes = n != br_int->n_ports;
- if (any_changes && ovs_idl_txn) {
- ovsdb_idl_txn_add_comment(ovs_idl_txn,
- "ovn-controller: destroying tunnels");
- ovsrec_bridge_verify_ports(br_int);
- ovsrec_bridge_set_ports(br_int, ports, n);
- }
- free(ports);
-
- return !any_changes;
-}
diff --git a/ovn/controller/encaps.h b/ovn/controller/encaps.h
deleted file mode 100644
index afa41830a..000000000
--- a/ovn/controller/encaps.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* Copyright (c) 2015 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_ENCAPS_H
-#define OVN_ENCAPS_H 1
-
-#include <stdbool.h>
-
-struct ovsdb_idl;
-struct ovsdb_idl_txn;
-struct ovsrec_bridge;
-struct ovsrec_bridge_table;
-struct sbrec_chassis_table;
-struct sbrec_sb_global;
-struct ovsrec_open_vswitch_table;
-struct sset;
-
-void encaps_register_ovs_idl(struct ovsdb_idl *);
-void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge_table *,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis_table *,
- const char *chassis_id,
- const struct sbrec_sb_global *,
- const struct sset *transport_zones);
-
-bool encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge *br_int);
-
-char *encaps_tunnel_id_create(const char *chassis_id, const char *encap_ip);
-bool encaps_tunnel_id_parse(const char *tunnel_id, char **chassis_id,
- char **encap_ip);
-bool encaps_tunnel_id_match(const char *tunnel_id, const char *chassis_id,
- const char *encap_ip);
-
-#endif /* ovn/encaps.h */
diff --git a/ovn/controller/ha-chassis.c b/ovn/controller/ha-chassis.c
deleted file mode 100644
index 498e5ce5a..000000000
--- a/ovn/controller/ha-chassis.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/* Copyright (c) 2019 Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "ha-chassis.h"
-#include "lib/sset.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-VLOG_DEFINE_THIS_MODULE(ha_chassis);
-
-static int
-compare_chassis_prio_(const void *a_, const void *b_)
-{
- const struct sbrec_ha_chassis *ch_a = a_;
- const struct sbrec_ha_chassis *ch_b = b_;
- int prio_diff = ch_b->priority - ch_a->priority;
- if (!prio_diff) {
- return strcmp(ch_b->chassis->name, ch_a->chassis->name);
- }
- return prio_diff;
-}
-
-/* Returns the ordered HA chassis list in the HA chassis group.
- * Eg. If an HA chassis group has 3 HA chassis
- * - HA1 - pri 30
- * - HA2 - pri 40 and
- * - HA3 - pri 20
- * and the ref_chassis of HA chassis group is set to - C1 and C2.
- *
- * If active_tunnels is NULL, then it returns the ordered list
- * - (HA2, HA1, HA3)
- *
- * If active_tunnels is set to - (HA1, HA2, C1, C2) and
- * local_chassis is HA3, then it returns the ordered list
- * - (HA2, HA1, HA3)
- *
- * If active_tunnels is set to - (HA1, C1, C2) and
- * local_chassis is HA3, then it returns the ordered list
- * - (HA1, HA3)
- *
- * If active_tunnels is set to - (C1, C2) and
- * local_chassis is HA3, then it returns the ordered list
- * - (HA3)
- *
- * If active_tunnels is set is empty and local_chassis is HA3,
- * then it returns NULL.
- */
-static struct ha_chassis_ordered *
-get_ordered_ha_chassis_list(const struct sbrec_ha_chassis_group *ha_ch_grp,
- const struct sset *active_tunnels,
- const struct sbrec_chassis *local_chassis)
-{
- struct sbrec_ha_chassis *ha_ch_order =
- xzalloc(sizeof *ha_ch_order * ha_ch_grp->n_ha_chassis);
-
- size_t n_ha_ch = 0;
-
- for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
- if (!ha_ch_grp->ha_chassis[i]->chassis) {
- continue;
- }
-
- /* Don't add it to the list for ordering if it is not active. */
- if (ha_ch_grp->ha_chassis[i]->chassis != local_chassis &&
- active_tunnels &&
- !sset_contains(active_tunnels,
- ha_ch_grp->ha_chassis[i]->chassis->name)) {
- continue;
- }
-
- ha_ch_order[n_ha_ch].chassis = ha_ch_grp->ha_chassis[i]->chassis;
- ha_ch_order[n_ha_ch].priority = ha_ch_grp->ha_chassis[i]->priority;
- n_ha_ch++;
- }
-
- if (!n_ha_ch) {
- free(ha_ch_order);
- return NULL;
- }
-
- struct ha_chassis_ordered *ordered_ha_ch;
- if (n_ha_ch == 1) {
- if (active_tunnels) {
- /* If n_ha_ch is 1, it means only the local chassis is in the
- * ha_ch_order list. Check if this local chassis has active
- * bfd session with any of the referenced chassis. If so,
- * then the local chassis can be active. Otherwise it can't.
- * This can happen in the following scenario.
- * Lets say we have chassis HA1 (prioirty 20) and HA2 (priority 10)
- * in the ha_chasis_group and compute chassis C1 and C2 are in the
- * reference chassis list. If HA1 chassis has lost the link and
- * when this function is called for HA2 we need to consider
- * HA2 as active since it has active BFD sessions with C1 and C2.
- * On HA1 chassis, this function won't be called since
- * active_tunnels set will be empty.
- * */
- bool can_local_chassis_be_active = false;
- for (size_t i = 0; i < ha_ch_grp->n_ref_chassis; i++) {
- if (sset_contains(active_tunnels,
- ha_ch_grp->ref_chassis[i]->name)) {
- can_local_chassis_be_active = true;
- break;
- }
- }
- if (!can_local_chassis_be_active) {
- free(ha_ch_order);
- return NULL;
- }
- }
- } else {
- qsort(ha_ch_order, n_ha_ch, sizeof *ha_ch_order,
- compare_chassis_prio_);
- }
-
- ordered_ha_ch = xmalloc(sizeof *ordered_ha_ch);
- ordered_ha_ch->ha_ch = ha_ch_order;
- ordered_ha_ch->n_ha_ch = n_ha_ch;
-
- return ordered_ha_ch;
-}
-
-void
-ha_chassis_destroy_ordered(struct ha_chassis_ordered *ordered_ha_ch)
-{
- if (ordered_ha_ch) {
- free(ordered_ha_ch->ha_ch);
- free(ordered_ha_ch);
- }
-}
-
-
-/* Returns true if the local_chassis is the master of
- * the HA chassis group, false otherwise. */
-bool
-ha_chassis_group_is_active(
- const struct sbrec_ha_chassis_group *ha_ch_grp,
- const struct sset *active_tunnels,
- const struct sbrec_chassis *local_chassis)
-{
- if (!ha_ch_grp || !ha_ch_grp->n_ha_chassis) {
- return false;
- }
-
- if (ha_ch_grp->n_ha_chassis == 1) {
- return (ha_ch_grp->ha_chassis[0]->chassis == local_chassis);
- }
-
- if (sset_is_empty(active_tunnels)) {
- /* If active tunnel sset is empty, it means it has lost
- * connectivity with other chassis. */
- return false;
- }
-
- struct ha_chassis_ordered *ordered_ha_ch =
- get_ordered_ha_chassis_list(ha_ch_grp, active_tunnels, local_chassis);
- if (!ordered_ha_ch) {
- return false;
- }
-
- struct sbrec_chassis *active_ch = ordered_ha_ch->ha_ch[0].chassis;
- ha_chassis_destroy_ordered(ordered_ha_ch);
-
- return (active_ch == local_chassis);
-}
-
-bool
-ha_chassis_group_contains(
- const struct sbrec_ha_chassis_group *ha_chassis_grp,
- const struct sbrec_chassis *chassis)
-{
- if (ha_chassis_grp && chassis) {
- for (size_t i = 0; i < ha_chassis_grp->n_ha_chassis; i++) {
- if (ha_chassis_grp->ha_chassis[i]->chassis == chassis) {
- return true;
- }
- }
- }
- return false;
-}
-
-struct ha_chassis_ordered *
-ha_chassis_get_ordered(const struct sbrec_ha_chassis_group *ha_chassis_grp)
-{
- if (!ha_chassis_grp || !ha_chassis_grp->n_ha_chassis) {
- return NULL;
- }
-
- return get_ordered_ha_chassis_list(ha_chassis_grp, NULL, NULL);
-}
diff --git a/ovn/controller/ha-chassis.h b/ovn/controller/ha-chassis.h
deleted file mode 100644
index 3768c2a5c..000000000
--- a/ovn/controller/ha-chassis.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Copyright (c) 2019 Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_HA_CHASSIS_H
-#define OVN_HA_CHASSIS_H 1
-
-#include <stdint.h>
-#include "openvswitch/hmap.h"
-#include "openvswitch/list.h"
-
-struct sbrec_chassis;
-struct sbrec_ha_chassis_group;
-struct sset;
-
-struct ha_chassis_ordered {
- struct sbrec_ha_chassis *ha_ch;
- size_t n_ha_ch;
-};
-
-/* Returns true if the local chassis is the active gateway among a set
- * of gateway_chassis. Return false if the local chassis is currently a
- * backup in a set of multiple gateway_chassis. */
-bool ha_chassis_group_is_active(
- const struct sbrec_ha_chassis_group *ha_chassis_grp,
- const struct sset *active_tunnels,
- const struct sbrec_chassis *local_chassis);
-
-bool ha_chassis_group_contains(
- const struct sbrec_ha_chassis_group *ha_chassis_grp,
- const struct sbrec_chassis *chassis);
-
-struct ha_chassis_ordered *ha_chassis_get_ordered(
- const struct sbrec_ha_chassis_group *ha_chassis_grp);
-
-void ha_chassis_destroy_ordered(
- struct ha_chassis_ordered *ordered_ha_ch);
-
-#endif /* OVN_HA_CHASSIS_H */
diff --git a/ovn/controller/ip-mcast.c b/ovn/controller/ip-mcast.c
deleted file mode 100644
index ef36be2ca..000000000
--- a/ovn/controller/ip-mcast.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/* Copyright (c) 2019, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "ip-mcast.h"
-#include "lport.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-/*
- * Used for (faster) updating of IGMP_Group ports.
- */
-struct igmp_group_port {
- struct hmap_node hmap_node;
- const struct sbrec_port_binding *port;
-};
-
-struct ovsdb_idl_index *
-igmp_group_index_create(struct ovsdb_idl *idl)
-{
- const struct ovsdb_idl_index_column cols[] = {
- { .column = &sbrec_igmp_group_col_address },
- { .column = &sbrec_igmp_group_col_datapath },
- { .column = &sbrec_igmp_group_col_chassis },
- };
-
- return ovsdb_idl_index_create(idl, cols, ARRAY_SIZE(cols));
-}
-
-/* Looks up an IGMP group based on an IPv4 (mapped in IPv6) or IPv6 'address'
- * and 'datapath'.
- */
-const struct sbrec_igmp_group *
-igmp_group_lookup(struct ovsdb_idl_index *igmp_groups,
- const struct in6_addr *address,
- const struct sbrec_datapath_binding *datapath,
- const struct sbrec_chassis *chassis)
-{
- char addr_str[INET6_ADDRSTRLEN];
-
- if (!ipv6_string_mapped(addr_str, address)) {
- return NULL;
- }
-
- struct sbrec_igmp_group *target =
- sbrec_igmp_group_index_init_row(igmp_groups);
-
- sbrec_igmp_group_index_set_address(target, addr_str);
- sbrec_igmp_group_index_set_datapath(target, datapath);
- sbrec_igmp_group_index_set_chassis(target, chassis);
-
- const struct sbrec_igmp_group *g =
- sbrec_igmp_group_index_find(igmp_groups, target);
- sbrec_igmp_group_index_destroy_row(target);
- return g;
-}
-
-/* Creates and returns a new IGMP group based on an IPv4 (mapped in IPv6) or
- * IPv6 'address', 'datapath' and 'chassis'.
- */
-struct sbrec_igmp_group *
-igmp_group_create(struct ovsdb_idl_txn *idl_txn,
- const struct in6_addr *address,
- const struct sbrec_datapath_binding *datapath,
- const struct sbrec_chassis *chassis)
-{
- char addr_str[INET6_ADDRSTRLEN];
-
- if (!ipv6_string_mapped(addr_str, address)) {
- return NULL;
- }
-
- struct sbrec_igmp_group *g = sbrec_igmp_group_insert(idl_txn);
-
- sbrec_igmp_group_set_address(g, addr_str);
- sbrec_igmp_group_set_datapath(g, datapath);
- sbrec_igmp_group_set_chassis(g, chassis);
-
- return g;
-}
-
-void
-igmp_group_update_ports(const struct sbrec_igmp_group *g,
- struct ovsdb_idl_index *datapaths,
- struct ovsdb_idl_index *port_bindings,
- const struct mcast_snooping *ms OVS_UNUSED,
- const struct mcast_group *mc_group)
- OVS_REQ_RDLOCK(ms->rwlock)
-{
- struct igmp_group_port *old_ports_storage =
- (g->n_ports ? xmalloc(g->n_ports * sizeof *old_ports_storage) : NULL);
-
- struct hmap old_ports = HMAP_INITIALIZER(&old_ports);
-
- for (size_t i = 0; i < g->n_ports; i++) {
- struct igmp_group_port *old_port = &old_ports_storage[i];
-
- old_port->port = g->ports[i];
- hmap_insert(&old_ports, &old_port->hmap_node,
- old_port->port->tunnel_key);
- }
-
- struct mcast_group_bundle *bundle;
- uint64_t dp_key = g->datapath->tunnel_key;
-
- LIST_FOR_EACH (bundle, bundle_node, &mc_group->bundle_lru) {
- uint32_t port_key = (uintptr_t)bundle->port;
- const struct sbrec_port_binding *sbrec_port =
- lport_lookup_by_key(datapaths, port_bindings, dp_key, port_key);
- if (!sbrec_port) {
- continue;
- }
-
- struct hmap_node *node = hmap_first_with_hash(&old_ports, port_key);
- if (!node) {
- sbrec_igmp_group_update_ports_addvalue(g, sbrec_port);
- } else {
- hmap_remove(&old_ports, node);
- }
- }
-
- struct igmp_group_port *igmp_port;
- HMAP_FOR_EACH_POP (igmp_port, hmap_node, &old_ports) {
- sbrec_igmp_group_update_ports_delvalue(g, igmp_port->port);
- }
-
- free(old_ports_storage);
- hmap_destroy(&old_ports);
-}
-
-void
-igmp_group_delete(const struct sbrec_igmp_group *g)
-{
- sbrec_igmp_group_delete(g);
-}
-
-bool
-igmp_group_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *igmp_groups)
-{
- const struct sbrec_igmp_group *g;
-
- if (!ovnsb_idl_txn) {
- return true;
- }
-
- SBREC_IGMP_GROUP_FOR_EACH_BYINDEX (g, igmp_groups) {
- igmp_group_delete(g);
- }
-
- return true;
-}
diff --git a/ovn/controller/ip-mcast.h b/ovn/controller/ip-mcast.h
deleted file mode 100644
index 6014f43d5..000000000
--- a/ovn/controller/ip-mcast.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2019, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_IP_MCAST_H
-#define OVN_IP_MCAST_H 1
-
-#include "mcast-snooping.h"
-
-struct ovsdb_idl;
-struct ovsdb_idl_txn;
-
-struct sbrec_chassis;
-struct sbrec_datapath_binding;
-
-struct ovsdb_idl_index *igmp_group_index_create(struct ovsdb_idl *);
-const struct sbrec_igmp_group *igmp_group_lookup(
- struct ovsdb_idl_index *igmp_groups,
- const struct in6_addr *address,
- const struct sbrec_datapath_binding *datapath,
- const struct sbrec_chassis *chassis);
-
-struct sbrec_igmp_group *igmp_group_create(
- struct ovsdb_idl_txn *idl_txn,
- const struct in6_addr *address,
- const struct sbrec_datapath_binding *datapath,
- const struct sbrec_chassis *chassis);
-
-void igmp_group_update_ports(const struct sbrec_igmp_group *g,
- struct ovsdb_idl_index *datapaths,
- struct ovsdb_idl_index *port_bindings,
- const struct mcast_snooping *ms,
- const struct mcast_group *mc_group)
- OVS_REQ_RDLOCK(ms->rwlock);
-
-void igmp_group_delete(const struct sbrec_igmp_group *g);
-
-bool igmp_group_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *igmp_groups);
-
-#endif /* ovn/controller/ip-mcast.h */
diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
deleted file mode 100644
index 1aafafb33..000000000
--- a/ovn/controller/lflow.c
+++ /dev/null
@@ -1,898 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "lflow.h"
-#include "coverage.h"
-#include "ha-chassis.h"
-#include "lport.h"
-#include "ofctrl.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofpbuf.h"
-#include "openvswitch/vlog.h"
-#include "ovn-controller.h"
-#include "ovn/actions.h"
-#include "ovn/expr.h"
-#include "ovn/lib/ovn-l7.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn/lib/extend-table.h"
-#include "packets.h"
-#include "physical.h"
-#include "simap.h"
-#include "sset.h"
-
-VLOG_DEFINE_THIS_MODULE(lflow);
-
-COVERAGE_DEFINE(lflow_run);
-
-/* Symbol table. */
-
-/* Contains "struct expr_symbol"s for fields supported by OVN lflows. */
-static struct shash symtab;
-
-void
-lflow_init(void)
-{
- ovn_init_symtab(&symtab);
-}
-
-struct lookup_port_aux {
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
- struct ovsdb_idl_index *sbrec_port_binding_by_name;
- const struct sbrec_datapath_binding *dp;
-};
-
-struct condition_aux {
- struct ovsdb_idl_index *sbrec_port_binding_by_name;
- const struct sbrec_chassis *chassis;
- const struct sset *active_tunnels;
-};
-
-static bool consider_logical_flow(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_logical_flow *,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *,
- struct hmap *dhcp_opts,
- struct hmap *dhcpv6_opts,
- struct hmap *nd_ra_opts,
- struct controller_event_options *controller_event_opts,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *lfrr,
- uint32_t *conj_id_ofs);
-
-static bool
-lookup_port_cb(const void *aux_, const char *port_name, unsigned int *portp)
-{
- const struct lookup_port_aux *aux = aux_;
-
- const struct sbrec_port_binding *pb
- = lport_lookup_by_name(aux->sbrec_port_binding_by_name, port_name);
- if (pb && pb->datapath == aux->dp) {
- *portp = pb->tunnel_key;
- return true;
- }
-
- const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
- aux->sbrec_multicast_group_by_name_datapath, aux->dp, port_name);
- if (mg) {
- *portp = mg->tunnel_key;
- return true;
- }
-
- return false;
-}
-
-static bool
-is_chassis_resident_cb(const void *c_aux_, const char *port_name)
-{
- const struct condition_aux *c_aux = c_aux_;
-
- const struct sbrec_port_binding *pb
- = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name, port_name);
- if (!pb) {
- return false;
- }
- if (strcmp(pb->type, "chassisredirect")) {
- /* for non-chassisredirect ports */
- return pb->chassis && pb->chassis == c_aux->chassis;
- } else {
- if (ha_chassis_group_contains(pb->ha_chassis_group,
- c_aux->chassis)) {
- bool active = ha_chassis_group_is_active(pb->ha_chassis_group,
- c_aux->active_tunnels,
- c_aux->chassis);
- return active;
- }
- return false;
- }
-}
-
-static bool
-is_switch(const struct sbrec_datapath_binding *ldp)
-{
- return smap_get(&ldp->external_ids, "logical-switch") != NULL;
-
-}
-
-void
-lflow_resource_init(struct lflow_resource_ref *lfrr)
-{
- hmap_init(&lfrr->ref_lflow_table);
- hmap_init(&lfrr->lflow_ref_table);
-}
-
-void
-lflow_resource_destroy(struct lflow_resource_ref *lfrr)
-{
- struct ref_lflow_node *rlfn, *rlfn_next;
- HMAP_FOR_EACH_SAFE (rlfn, rlfn_next, node, &lfrr->ref_lflow_table) {
- free(rlfn->ref_name);
- struct lflow_ref_list_node *lrln, *next;
- LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) {
- ovs_list_remove(&lrln->ref_list);
- ovs_list_remove(&lrln->lflow_list);
- free(lrln);
- }
- hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
- free(rlfn);
- }
- hmap_destroy(&lfrr->ref_lflow_table);
-
- struct lflow_ref_node *lfrn, *lfrn_next;
- HMAP_FOR_EACH_SAFE (lfrn, lfrn_next, node, &lfrr->lflow_ref_table) {
- hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
- free(lfrn);
- }
- hmap_destroy(&lfrr->lflow_ref_table);
-}
-
-void
-lflow_resource_clear(struct lflow_resource_ref *lfrr)
-{
- lflow_resource_destroy(lfrr);
- lflow_resource_init(lfrr);
-}
-
-static struct ref_lflow_node*
-ref_lflow_lookup(struct hmap *ref_lflow_table,
- enum ref_type type, const char *ref_name)
-{
- struct ref_lflow_node *rlfn;
-
- HMAP_FOR_EACH_WITH_HASH (rlfn, node, hash_string(ref_name, type),
- ref_lflow_table) {
- if (rlfn->type == type && !strcmp(rlfn->ref_name, ref_name)) {
- return rlfn;
- }
- }
- return NULL;
-}
-
-static struct lflow_ref_node*
-lflow_ref_lookup(struct hmap *lflow_ref_table,
- const struct uuid *lflow_uuid)
-{
- struct lflow_ref_node *lfrn;
-
- HMAP_FOR_EACH_WITH_HASH (lfrn, node, uuid_hash(lflow_uuid),
- lflow_ref_table) {
- if (uuid_equals(&lfrn->lflow_uuid, lflow_uuid)) {
- return lfrn;
- }
- }
- return NULL;
-}
-
-static void
-lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type,
- const char *ref_name, const struct uuid *lflow_uuid)
-{
- struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
- type, ref_name);
- if (!rlfn) {
- rlfn = xzalloc(sizeof *rlfn);
- rlfn->node.hash = hash_string(ref_name, type);
- rlfn->type = type;
- rlfn->ref_name = xstrdup(ref_name);
- ovs_list_init(&rlfn->ref_lflow_head);
- hmap_insert(&lfrr->ref_lflow_table, &rlfn->node, rlfn->node.hash);
- }
-
- struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
- lflow_uuid);
- if (!lfrn) {
- lfrn = xzalloc(sizeof *lfrn);
- lfrn->node.hash = uuid_hash(lflow_uuid);
- lfrn->lflow_uuid = *lflow_uuid;
- ovs_list_init(&lfrn->lflow_ref_head);
- hmap_insert(&lfrr->lflow_ref_table, &lfrn->node, lfrn->node.hash);
- }
-
- struct lflow_ref_list_node *lrln = xzalloc(sizeof *lrln);
- lrln->type = type;
- lrln->ref_name = xstrdup(ref_name);
- lrln->lflow_uuid = *lflow_uuid;
- ovs_list_push_back(&rlfn->ref_lflow_head, &lrln->ref_list);
- ovs_list_push_back(&lfrn->lflow_ref_head, &lrln->lflow_list);
-}
-
-static void
-lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr,
- const struct uuid *lflow_uuid)
-{
- struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
- lflow_uuid);
- if (!lfrn) {
- return;
- }
-
- hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
- struct lflow_ref_list_node *lrln, *next;
- LIST_FOR_EACH_SAFE (lrln, next, lflow_list, &lfrn->lflow_ref_head) {
- ovs_list_remove(&lrln->ref_list);
- ovs_list_remove(&lrln->lflow_list);
- free(lrln);
- }
- free(lfrn);
-}
-
-/* Adds the logical flows from the Logical_Flow table to flow tables. */
-static void
-add_logical_flows(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *dhcp_options_table,
- const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
- const struct sbrec_logical_flow_table *logical_flow_table,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *chassis,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *flow_table,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *lfrr,
- uint32_t *conj_id_ofs)
-{
- const struct sbrec_logical_flow *lflow;
-
- struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
- struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
- const struct sbrec_dhcp_options *dhcp_opt_row;
- SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
- dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
- dhcp_opt_row->type);
- }
-
-
- const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
- SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
- dhcpv6_options_table) {
- dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
- dhcpv6_opt_row->type);
- }
-
- struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
- nd_ra_opts_init(&nd_ra_opts);
-
- struct controller_event_options controller_event_opts;
- controller_event_opts_init(&controller_event_opts);
-
- SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
- if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,
- lflow, local_datapaths,
- chassis, &dhcp_opts, &dhcpv6_opts,
- &nd_ra_opts, &controller_event_opts,
- addr_sets, port_groups,
- active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table,
- lfrr, conj_id_ofs)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
- VLOG_ERR_RL(&rl, "Conjunction id overflow when processing lflow "
- UUID_FMT, UUID_ARGS(&lflow->header_.uuid));
- }
- }
-
- dhcp_opts_destroy(&dhcp_opts);
- dhcp_opts_destroy(&dhcpv6_opts);
- nd_ra_opts_destroy(&nd_ra_opts);
- controller_event_opts_destroy(&controller_event_opts);
-}
-
-bool
-lflow_handle_changed_flows(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *dhcp_options_table,
- const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
- const struct sbrec_logical_flow_table *logical_flow_table,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *chassis,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *flow_table,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *lfrr,
- uint32_t *conj_id_ofs)
-{
- bool ret = true;
- const struct sbrec_logical_flow *lflow;
-
- struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
- struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
- const struct sbrec_dhcp_options *dhcp_opt_row;
- SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
- dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
- dhcp_opt_row->type);
- }
-
-
- const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
- SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
- dhcpv6_options_table) {
- dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
- dhcpv6_opt_row->type);
- }
-
- struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
- nd_ra_opts_init(&nd_ra_opts);
-
- /* Handle removed flows first, and then other flows, so that when
- * the flows being added and removed have same match conditions
- * can be processed in the proper order */
- SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table) {
- /* Remove any flows that should be removed. */
- if (sbrec_logical_flow_is_deleted(lflow)) {
- VLOG_DBG("handle deleted lflow "UUID_FMT,
- UUID_ARGS(&lflow->header_.uuid));
- ofctrl_remove_flows(flow_table, &lflow->header_.uuid);
- /* Delete entries from lflow resource reference. */
- lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
- }
- }
-
- struct controller_event_options controller_event_opts;
- controller_event_opts_init(&controller_event_opts);
-
- SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table) {
- if (!sbrec_logical_flow_is_deleted(lflow)) {
- /* Now, add/modify existing flows. If the logical
- * flow is a modification, just remove the flows
- * for this row, and then add new flows. */
- if (!sbrec_logical_flow_is_new(lflow)) {
- VLOG_DBG("handle updated lflow "UUID_FMT,
- UUID_ARGS(&lflow->header_.uuid));
- ofctrl_remove_flows(flow_table, &lflow->header_.uuid);
- /* Delete entries from lflow resource reference. */
- lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
- }
- VLOG_DBG("handle new lflow "UUID_FMT,
- UUID_ARGS(&lflow->header_.uuid));
- if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,
- lflow, local_datapaths,
- chassis, &dhcp_opts, &dhcpv6_opts,
- &nd_ra_opts, &controller_event_opts,
- addr_sets, port_groups,
- active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table,
- lfrr, conj_id_ofs)) {
- ret = false;
- break;
- }
- }
- }
- dhcp_opts_destroy(&dhcp_opts);
- dhcp_opts_destroy(&dhcpv6_opts);
- nd_ra_opts_destroy(&nd_ra_opts);
- controller_event_opts_destroy(&controller_event_opts);
- return ret;
-}
-
-bool
-lflow_handle_changed_ref(
- enum ref_type ref_type,
- const char *ref_name,
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *dhcp_options_table,
- const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
- const struct sbrec_logical_flow_table *logical_flow_table,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *chassis,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *flow_table,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *lfrr,
- uint32_t *conj_id_ofs,
- bool *changed)
-{
- struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
- ref_type, ref_name);
- if (!rlfn) {
- *changed = false;
- return true;
- }
- VLOG_DBG("Handle changed lflow reference for resource type: %d,"
- " name: %s.", ref_type, ref_name);
- *changed = false;
- bool ret = true;
-
- hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
-
- struct lflow_ref_list_node *lrln, *next;
- /* Detach the rlfn->ref_lflow_head nodes from the lfrr table and clean
- * up all other nodes related to the lflows that uses the resource,
- * so that the old nodes won't interfere with updating the lfrr table
- * when reparsing the lflows. */
- LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
- ovs_list_remove(&lrln->lflow_list);
- lflow_resource_destroy_lflow(lfrr, &lrln->lflow_uuid);
- }
-
- struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
- struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
- const struct sbrec_dhcp_options *dhcp_opt_row;
- SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
- dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
- dhcp_opt_row->type);
- }
-
- const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
- SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH(dhcpv6_opt_row, dhcpv6_options_table) {
- dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name, dhcpv6_opt_row->code,
- dhcpv6_opt_row->type);
- }
-
- struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
- nd_ra_opts_init(&nd_ra_opts);
-
- struct controller_event_options controller_event_opts;
- controller_event_opts_init(&controller_event_opts);
-
- /* Re-parse the related lflows. */
- LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
- const struct sbrec_logical_flow *lflow =
- sbrec_logical_flow_table_get_for_uuid(logical_flow_table,
- &lrln->lflow_uuid);
- if (!lflow) {
- VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
- " name: %s - not found.",
- UUID_ARGS(&lrln->lflow_uuid),
- ref_type, ref_name);
- continue;
- }
- VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
- " name: %s.",
- UUID_ARGS(&lrln->lflow_uuid),
- ref_type, ref_name);
- ofctrl_remove_flows(flow_table, &lrln->lflow_uuid);
-
- if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,
- lflow, local_datapaths,
- chassis, &dhcp_opts, &dhcpv6_opts,
- &nd_ra_opts, &controller_event_opts,
- addr_sets, port_groups,
- active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table,
- lfrr, conj_id_ofs)) {
- ret = false;
- break;
- }
- *changed = true;
- }
-
- LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) {
- ovs_list_remove(&lrln->ref_list);
- free(lrln);
- }
- free(rlfn);
-
- dhcp_opts_destroy(&dhcp_opts);
- dhcp_opts_destroy(&dhcpv6_opts);
- nd_ra_opts_destroy(&nd_ra_opts);
- controller_event_opts_destroy(&controller_event_opts);
- return ret;
-}
-
-static bool
-update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs)
-{
- if (*conj_id_ofs + n_conjs < *conj_id_ofs) {
- /* overflow */
- return false;
- }
- *conj_id_ofs += n_conjs;
- return true;
-}
-
-static bool
-consider_logical_flow(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_logical_flow *lflow,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *chassis,
- struct hmap *dhcp_opts,
- struct hmap *dhcpv6_opts,
- struct hmap *nd_ra_opts,
- struct controller_event_options *controller_event_opts,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *flow_table,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *lfrr,
- uint32_t *conj_id_ofs)
-{
- /* Determine translation of logical table IDs to physical table IDs. */
- bool ingress = !strcmp(lflow->pipeline, "ingress");
-
- const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
- if (!ldp) {
- VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip",
- UUID_ARGS(&lflow->header_.uuid));
- return true;
- }
- if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
- VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip",
- UUID_ARGS(&lflow->header_.uuid));
- return true;
- }
-
- /* Determine translation of logical table IDs to physical table IDs. */
- uint8_t first_ptable = (ingress
- ? OFTABLE_LOG_INGRESS_PIPELINE
- : OFTABLE_LOG_EGRESS_PIPELINE);
- uint8_t ptable = first_ptable + lflow->table_id;
- uint8_t output_ptable = (ingress
- ? OFTABLE_REMOTE_OUTPUT
- : OFTABLE_SAVE_INPORT);
-
- /* Parse OVN logical actions.
- *
- * XXX Deny changes to 'outport' in egress pipeline. */
- uint64_t ovnacts_stub[1024 / 8];
- struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub);
- struct ovnact_parse_params pp = {
- .symtab = &symtab,
- .dhcp_opts = dhcp_opts,
- .dhcpv6_opts = dhcpv6_opts,
- .nd_ra_opts = nd_ra_opts,
- .controller_event_opts = controller_event_opts,
-
- .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
- .n_tables = LOG_PIPELINE_LEN,
- .cur_ltable = lflow->table_id,
- };
- struct expr *prereqs;
- char *error;
-
- error = ovnacts_parse_string(lflow->actions, &pp, &ovnacts, &prereqs);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
- lflow->actions, error);
- free(error);
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
- return true;
- }
-
- /* Translate OVN match into table of OpenFlow matches. */
- struct hmap matches;
- struct expr *expr;
-
- struct sset addr_sets_ref = SSET_INITIALIZER(&addr_sets_ref);
- expr = expr_parse_string(lflow->match, &symtab, addr_sets, port_groups,
- &addr_sets_ref, &error);
- const char *addr_set_name;
- SSET_FOR_EACH (addr_set_name, &addr_sets_ref) {
- lflow_resource_add(lfrr, REF_TYPE_ADDRSET, addr_set_name,
- &lflow->header_.uuid);
- }
- sset_destroy(&addr_sets_ref);
-
- if (!error) {
- if (prereqs) {
- expr = expr_combine(EXPR_T_AND, expr, prereqs);
- prereqs = NULL;
- }
- expr = expr_annotate(expr, &symtab, &error);
- }
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
- lflow->match, error);
- expr_destroy(prereqs);
- free(error);
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
- return true;
- }
-
- struct lookup_port_aux aux = {
- .sbrec_multicast_group_by_name_datapath
- = sbrec_multicast_group_by_name_datapath,
- .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
- .dp = lflow->logical_datapath
- };
- struct condition_aux cond_aux = {
- .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
- .chassis = chassis,
- .active_tunnels = active_tunnels,
- };
- expr = expr_simplify(expr, is_chassis_resident_cb, &cond_aux);
- expr = expr_normalize(expr);
- uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
- &matches);
- expr_destroy(expr);
-
- if (hmap_is_empty(&matches)) {
- VLOG_DBG("lflow "UUID_FMT" matches are empty, skip",
- UUID_ARGS(&lflow->header_.uuid));
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
- expr_matches_destroy(&matches);
- return true;
- }
-
- /* Encode OVN logical actions into OpenFlow. */
- uint64_t ofpacts_stub[1024 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- struct ovnact_encode_params ep = {
- .lookup_port = lookup_port_cb,
- .aux = &aux,
- .is_switch = is_switch(ldp),
- .group_table = group_table,
- .meter_table = meter_table,
- .lflow_uuid = lflow->header_.uuid,
-
- .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
- .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE,
- .egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE,
- .output_ptable = output_ptable,
- .mac_bind_ptable = OFTABLE_MAC_BINDING,
- };
- ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
-
- /* Prepare the OpenFlow matches for adding to the flow table. */
- struct expr_match *m;
- HMAP_FOR_EACH (m, hmap_node, &matches) {
- match_set_metadata(&m->match,
- htonll(lflow->logical_datapath->tunnel_key));
- if (m->match.wc.masks.conj_id) {
- m->match.flow.conj_id += *conj_id_ofs;
- }
- if (is_switch(ldp)) {
- unsigned int reg_index
- = (ingress ? MFF_LOG_INPORT : MFF_LOG_OUTPORT) - MFF_REG0;
- int64_t port_id = m->match.flow.regs[reg_index];
- if (port_id) {
- int64_t dp_id = lflow->logical_datapath->tunnel_key;
- char buf[16];
- snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64, dp_id, port_id);
- if (!sset_contains(local_lport_ids, buf)) {
- VLOG_DBG("lflow "UUID_FMT
- " port %s in match is not local, skip",
- UUID_ARGS(&lflow->header_.uuid),
- buf);
- continue;
- }
- }
- }
- if (!m->n) {
- ofctrl_add_flow(flow_table, ptable, lflow->priority,
- lflow->header_.uuid.parts[0], &m->match, &ofpacts,
- &lflow->header_.uuid);
- } else {
- uint64_t conj_stubs[64 / 8];
- struct ofpbuf conj;
-
- ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
- for (int i = 0; i < m->n; i++) {
- const struct cls_conjunction *src = &m->conjunctions[i];
- struct ofpact_conjunction *dst;
-
- dst = ofpact_put_CONJUNCTION(&conj);
- dst->id = src->id + *conj_id_ofs;
- dst->clause = src->clause;
- dst->n_clauses = src->n_clauses;
- }
- ofctrl_add_flow(flow_table, ptable, lflow->priority, 0, &m->match,
- &conj, &lflow->header_.uuid);
- ofpbuf_uninit(&conj);
- }
- }
-
- /* Clean up. */
- expr_matches_destroy(&matches);
- ofpbuf_uninit(&ofpacts);
- return update_conj_id_ofs(conj_id_ofs, n_conjs);
-}
-
-static void
-put_load(const uint8_t *data, size_t len,
- enum mf_field_id dst, int ofs, int n_bits,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
- mf_from_id(dst), NULL,
- NULL);
- bitwise_copy(data, len, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
- bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
-}
-
-static void
-consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_mac_binding *b,
- struct ovn_desired_flow_table *flow_table)
-{
- const struct sbrec_port_binding *pb
- = lport_lookup_by_name(sbrec_port_binding_by_name, b->logical_port);
- if (!pb) {
- return;
- }
-
- struct eth_addr mac;
- if (!eth_addr_from_string(b->mac, &mac)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac);
- return;
- }
-
- struct match match = MATCH_CATCHALL_INITIALIZER;
- if (strchr(b->ip, '.')) {
- ovs_be32 ip;
- if (!ip_parse(b->ip, &ip)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
- return;
- }
- match_set_reg(&match, 0, ntohl(ip));
- } else {
- struct in6_addr ip6;
- if (!ipv6_parse(b->ip, &ip6)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
- return;
- }
- ovs_be128 value;
- memcpy(&value, &ip6, sizeof(value));
- match_set_xxreg(&match, 0, ntoh128(value));
- }
-
- match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
-
- uint64_t stub[1024 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
- put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match, &ofpacts,
- &b->header_.uuid);
- ofpbuf_uninit(&ofpacts);
-}
-
-/* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
- * southbound database. */
-static void
-add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_mac_binding_table *mac_binding_table,
- struct ovn_desired_flow_table *flow_table)
-{
- const struct sbrec_mac_binding *b;
- SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
- consider_neighbor_flow(sbrec_port_binding_by_name, b, flow_table);
- }
-}
-
-/* Handles neighbor changes in mac_binding table. */
-void
-lflow_handle_changed_neighbors(
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_mac_binding_table *mac_binding_table,
- struct ovn_desired_flow_table *flow_table)
-{
-
- const struct sbrec_mac_binding *mb;
- /* Handle deleted mac_bindings first, to avoid *duplicated flow* problem
- * when same flow needs to be added. */
- SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (mb, mac_binding_table) {
- /* Remove any flows that should be removed. */
- if (sbrec_mac_binding_is_deleted(mb)) {
- VLOG_DBG("handle deleted mac_binding "UUID_FMT,
- UUID_ARGS(&mb->header_.uuid));
- ofctrl_remove_flows(flow_table, &mb->header_.uuid);
- }
- }
- SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (mb, mac_binding_table) {
- if (!sbrec_mac_binding_is_deleted(mb)) {
- if (!sbrec_mac_binding_is_new(mb)) {
- VLOG_DBG("handle updated mac_binding "UUID_FMT,
- UUID_ARGS(&mb->header_.uuid));
- ofctrl_remove_flows(flow_table, &mb->header_.uuid);
- }
- VLOG_DBG("handle new mac_binding "UUID_FMT,
- UUID_ARGS(&mb->header_.uuid));
- consider_neighbor_flow(sbrec_port_binding_by_name, mb, flow_table);
- }
- }
-}
-
-
-/* Translates logical flows in the Logical_Flow table in the OVN_SB database
- * into OpenFlow flows. See ovn-architecture(7) for more information. */
-void
-lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *dhcp_options_table,
- const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
- const struct sbrec_logical_flow_table *logical_flow_table,
- const struct sbrec_mac_binding_table *mac_binding_table,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *flow_table,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *lfrr,
- uint32_t *conj_id_ofs)
-{
- COVERAGE_INC(lflow_run);
-
- add_logical_flows(sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name, dhcp_options_table,
- dhcpv6_options_table, logical_flow_table,
- local_datapaths, chassis, addr_sets, port_groups,
- active_tunnels, local_lport_ids, flow_table, group_table,
- meter_table, lfrr, conj_id_ofs);
- add_neighbor_flows(sbrec_port_binding_by_name, mac_binding_table,
- flow_table);
-}
-
-void
-lflow_destroy(void)
-{
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
- ovn_destroy_ovnfields();
-}
diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
deleted file mode 100644
index 4e1086eb6..000000000
--- a/ovn/controller/lflow.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_LFLOW_H
-#define OVN_LFLOW_H 1
-
-#include "ovn/logical-fields.h"
-
-/* Logical_Flow table translation to OpenFlow
- * ==========================================
- *
- * The Logical_Flow table obtained from the OVN_Southbound database works in
- * terms of logical entities, that is, logical flows among logical datapaths
- * and logical ports. This code translates these logical flows into OpenFlow
- * flows that, again, work in terms of logical entities implemented through
- * OpenFlow extensions (e.g. registers represent the logical input and output
- * ports).
- *
- * Physical-to-logical and logical-to-physical translation are implemented in
- * physical.[ch] as separate OpenFlow tables that run before and after,
- * respectively, the logical pipeline OpenFlow tables.
- */
-
-#include <stdint.h>
-#include "openvswitch/hmap.h"
-#include "openvswitch/uuid.h"
-#include "openvswitch/list.h"
-
-struct ovn_extend_table;
-struct ovsdb_idl_index;
-struct ovn_desired_flow_table;
-struct hmap;
-struct hmap_node;
-struct sbrec_chassis;
-struct sbrec_dhcp_options_table;
-struct sbrec_dhcpv6_options_table;
-struct sbrec_logical_flow_table;
-struct sbrec_mac_binding_table;
-struct simap;
-struct sset;
-struct uuid;
-
-/* OpenFlow table numbers.
- *
- * These are heavily documented in ovn-architecture(7), please update it if
- * you make any changes. */
-#define OFTABLE_PHY_TO_LOG 0
-#define OFTABLE_LOG_INGRESS_PIPELINE 8 /* First of LOG_PIPELINE_LEN tables. */
-#define OFTABLE_REMOTE_OUTPUT 32
-#define OFTABLE_LOCAL_OUTPUT 33
-#define OFTABLE_CHECK_LOOPBACK 34
-#define OFTABLE_LOG_EGRESS_PIPELINE 40 /* First of LOG_PIPELINE_LEN tables. */
-#define OFTABLE_SAVE_INPORT 64
-#define OFTABLE_LOG_TO_PHY 65
-#define OFTABLE_MAC_BINDING 66
-
-/* The number of tables for the ingress and egress pipelines. */
-#define LOG_PIPELINE_LEN 24
-
-enum ref_type {
- REF_TYPE_ADDRSET,
- REF_TYPE_PORTGROUP
-};
-
-/* Maintains the relationship for a pair of named resource and
- * a lflow, indexed by both ref_lflow_table and lflow_ref_table. */
-struct lflow_ref_list_node {
- struct ovs_list lflow_list; /* list for same lflow */
- struct ovs_list ref_list; /* list for same ref */
- enum ref_type type;
- char *ref_name;
- struct uuid lflow_uuid;
-};
-
-struct ref_lflow_node {
- struct hmap_node node;
- enum ref_type type; /* key */
- char *ref_name; /* key */
- struct ovs_list ref_lflow_head;
-};
-
-struct lflow_ref_node {
- struct hmap_node node;
- struct uuid lflow_uuid; /* key */
- struct ovs_list lflow_ref_head;
-};
-
-struct lflow_resource_ref {
- /* A map from a referenced resource type & name (e.g. address_set AS1)
- * to a list of lflows that are referencing the named resource. Data
- * type of each node in this hmap is struct ref_lflow_node. The
- * ref_lflow_head in each node points to a list of
- * lflow_ref_list_node.ref_list. */
- struct hmap ref_lflow_table;
-
- /* A map from a lflow uuid to a list of named resources that are
- * referenced by the lflow. Data type of each node in this hmap is
- * struct lflow_ref_node. The lflow_ref_head in each node points to
- * a list of lflow_ref_list_node.lflow_list. */
- struct hmap lflow_ref_table;
-};
-
-void lflow_resource_init(struct lflow_resource_ref *);
-void lflow_resource_destroy(struct lflow_resource_ref *);
-void lflow_resource_clear(struct lflow_resource_ref *);
-
-void lflow_init(void);
-void lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *,
- const struct sbrec_dhcpv6_options_table *,
- const struct sbrec_logical_flow_table *,
- const struct sbrec_mac_binding_table *,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *,
- uint32_t *conj_id_ofs);
-
-bool lflow_handle_changed_flows(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *,
- const struct sbrec_dhcpv6_options_table *,
- const struct sbrec_logical_flow_table *,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *,
- uint32_t *conj_id_ofs);
-
-bool lflow_handle_changed_ref(
- enum ref_type,
- const char *ref_name,
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_dhcp_options_table *,
- const struct sbrec_dhcpv6_options_table *,
- const struct sbrec_logical_flow_table *,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- const struct sset *active_tunnels,
- const struct sset *local_lport_ids,
- struct ovn_desired_flow_table *,
- struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- struct lflow_resource_ref *,
- uint32_t *conj_id_ofs,
- bool *changed);
-
-void lflow_handle_changed_neighbors(
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_mac_binding_table *,
- struct ovn_desired_flow_table *);
-
-void lflow_destroy(void);
-
-#endif /* ovn/lflow.h */
diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
deleted file mode 100644
index cc5c5fbb2..000000000
--- a/ovn/controller/lport.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "lib/sset.h"
-#include "lport.h"
-#include "hash.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/ovn-sb-idl.h"
-VLOG_DEFINE_THIS_MODULE(lport);
-
-const struct sbrec_port_binding *
-lport_lookup_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const char *name)
-{
- struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
- sbrec_port_binding_by_name);
- sbrec_port_binding_index_set_logical_port(pb, name);
-
- const struct sbrec_port_binding *retval = sbrec_port_binding_index_find(
- sbrec_port_binding_by_name, pb);
-
- sbrec_port_binding_index_destroy_row(pb);
-
- return retval;
-}
-
-const struct sbrec_port_binding *
-lport_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- uint64_t dp_key, uint64_t port_key)
-{
- /* Lookup datapath corresponding to dp_key. */
- const struct sbrec_datapath_binding *db = datapath_lookup_by_key(
- sbrec_datapath_binding_by_key, dp_key);
- if (!db) {
- return NULL;
- }
-
- /* Build key for an indexed lookup. */
- struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
- sbrec_port_binding_by_key);
- sbrec_port_binding_index_set_datapath(pb, db);
- sbrec_port_binding_index_set_tunnel_key(pb, port_key);
-
- const struct sbrec_port_binding *retval = sbrec_port_binding_index_find(
- sbrec_port_binding_by_key, pb);
-
- sbrec_port_binding_index_destroy_row(pb);
-
- return retval;
-}
-
-const struct sbrec_datapath_binding *
-datapath_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- uint64_t dp_key)
-{
- struct sbrec_datapath_binding *db = sbrec_datapath_binding_index_init_row(
- sbrec_datapath_binding_by_key);
- sbrec_datapath_binding_index_set_tunnel_key(db, dp_key);
-
- const struct sbrec_datapath_binding *retval
- = sbrec_datapath_binding_index_find(sbrec_datapath_binding_by_key,
- db);
-
- sbrec_datapath_binding_index_destroy_row(db);
-
- return retval;
-}
-
-const struct sbrec_multicast_group *
-mcgroup_lookup_by_dp_name(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- const struct sbrec_datapath_binding *db, const char *name)
-{
- /* Build key for an indexed lookup. */
- struct sbrec_multicast_group *mc = sbrec_multicast_group_index_init_row(
- sbrec_multicast_group_by_name_datapath);
- sbrec_multicast_group_index_set_name(mc, name);
- sbrec_multicast_group_index_set_datapath(mc, db);
-
- const struct sbrec_multicast_group *retval
- = sbrec_multicast_group_index_find(
- sbrec_multicast_group_by_name_datapath, mc);
-
- sbrec_multicast_group_index_destroy_row(mc);
-
- return retval;
-}
diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
deleted file mode 100644
index 7dcd5bee0..000000000
--- a/ovn/controller/lport.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_LPORT_H
-#define OVN_LPORT_H 1
-
-#include <stdint.h>
-
-struct ovsdb_idl_index;
-struct sbrec_chassis;
-struct sbrec_datapath_binding;
-struct sbrec_multicast_group;
-struct sbrec_port_binding;
-
-
-/* Database indexes.
- * =================
- *
- * If the database IDL were a little smarter, it would allow us to directly
- * look up data based on values of its fields. It's not that smart (yet), so
- * instead we define our own indexes.
- */
-
-const struct sbrec_port_binding *lport_lookup_by_name(
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const char *name);
-
-const struct sbrec_port_binding *lport_lookup_by_key(
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- uint64_t dp_key, uint64_t port_key);
-
-const struct sbrec_datapath_binding *datapath_lookup_by_key(
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key, uint64_t dp_key);
-
-const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
- const struct sbrec_datapath_binding *, const char *name);
-
-#endif /* ovn/lport.h */
diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
deleted file mode 100644
index 043abd69d..000000000
--- a/ovn/controller/ofctrl.c
+++ /dev/null
@@ -1,1393 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "bitmap.h"
-#include "byte-order.h"
-#include "dirs.h"
-#include "dp-packet.h"
-#include "flow.h"
-#include "hash.h"
-#include "hindex.h"
-#include "lflow.h"
-#include "ofctrl.h"
-#include "openflow/openflow.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/list.h"
-#include "openvswitch/match.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofp-flow.h"
-#include "openvswitch/ofp-group.h"
-#include "openvswitch/ofp-match.h"
-#include "openvswitch/ofp-msgs.h"
-#include "openvswitch/ofp-meter.h"
-#include "openvswitch/ofp-packet.h"
-#include "openvswitch/ofp-print.h"
-#include "openvswitch/ofp-util.h"
-#include "openvswitch/ofpbuf.h"
-#include "openvswitch/vlog.h"
-#include "ovn-controller.h"
-#include "ovn/actions.h"
-#include "ovn/lib/extend-table.h"
-#include "openvswitch/poll-loop.h"
-#include "physical.h"
-#include "openvswitch/rconn.h"
-#include "socket-util.h"
-#include "util.h"
-#include "vswitch-idl.h"
-
-VLOG_DEFINE_THIS_MODULE(ofctrl);
-
-/* An OpenFlow flow. */
-struct ovn_flow {
- struct hmap_node match_hmap_node; /* For match based hashing. */
- struct hindex_node uuid_hindex_node; /* For uuid based hashing. */
- struct ovs_list list_node; /* For handling lists of flows. */
-
- /* Key. */
- uint8_t table_id;
- uint16_t priority;
- struct minimatch match;
-
- /* Data. */
- struct uuid sb_uuid;
- struct ofpact *ofpacts;
- size_t ofpacts_len;
- uint64_t cookie;
-};
-
-static uint32_t ovn_flow_match_hash(const struct ovn_flow *);
-static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table,
- const struct ovn_flow *target,
- bool cmp_sb_uuid);
-static char *ovn_flow_to_string(const struct ovn_flow *);
-static void ovn_flow_log(const struct ovn_flow *, const char *action);
-static void ovn_flow_destroy(struct ovn_flow *);
-
-/* OpenFlow connection to the switch. */
-static struct rconn *swconn;
-
-/* Symbol table for OVN expressions. */
-static struct shash symtab;
-
-/* Last seen sequence number for 'swconn'. When this differs from
- * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
-static unsigned int seqno;
-
-/* Connection state machine. */
-#define STATES \
- STATE(S_NEW) \
- STATE(S_TLV_TABLE_REQUESTED) \
- STATE(S_TLV_TABLE_MOD_SENT) \
- STATE(S_CLEAR_FLOWS) \
- STATE(S_UPDATE_FLOWS)
-enum ofctrl_state {
-#define STATE(NAME) NAME,
- STATES
-#undef STATE
-};
-
-/* An in-flight update to the switch's flow table.
- *
- * When we receive a barrier reply from the switch with the given 'xid', we
- * know that the switch is caught up to northbound database sequence number
- * 'nb_cfg' (and make that available to the client via ofctrl_get_cur_cfg(), so
- * that it can store it into our Chassis record's nb_cfg column). */
-struct ofctrl_flow_update {
- struct ovs_list list_node; /* In 'flow_updates'. */
- ovs_be32 xid; /* OpenFlow transaction ID for barrier. */
- int64_t nb_cfg; /* Northbound database sequence number. */
-};
-
-static struct ofctrl_flow_update *
-ofctrl_flow_update_from_list_node(const struct ovs_list *list_node)
-{
- return CONTAINER_OF(list_node, struct ofctrl_flow_update, list_node);
-}
-
-/* Currently in-flight updates. */
-static struct ovs_list flow_updates;
-
-/* nb_cfg of latest committed flow update. */
-static int64_t cur_cfg;
-
-/* Current state. */
-static enum ofctrl_state state;
-
-/* Transaction IDs for messages in flight to the switch. */
-static ovs_be32 xid, xid2;
-
-/* Counter for in-flight OpenFlow messages on 'swconn'. We only send a new
- * round of flow table modifications to the switch when the counter falls to
- * zero, to avoid unbounded buffering. */
-static struct rconn_packet_counter *tx_counter;
-
-/* Flow table of "struct ovn_flow"s, that holds the flow table currently
- * installed in the switch. */
-static struct hmap installed_flows;
-
-/* A reference to the group_table. */
-static struct ovn_extend_table *groups;
-
-/* A reference to the meter_table. */
-static struct ovn_extend_table *meters;
-
-/* MFF_* field ID for our Geneve option. In S_TLV_TABLE_MOD_SENT, this is
- * the option we requested (we don't know whether we obtained it yet). In
- * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */
-static enum mf_field_id mff_ovn_geneve;
-
-/* Indicates if flows need to be reinstalled for scenarios when ovs
- * is restarted, even if there is no change in the desired flow table. */
-static bool need_reinstall_flows;
-
-static ovs_be32 queue_msg(struct ofpbuf *);
-
-static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
-
-static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
-
-static struct ofpbuf *encode_meter_mod(const struct ofputil_meter_mod *);
-
-static void ovn_installed_flow_table_clear(void);
-static void ovn_installed_flow_table_destroy(void);
-
-static void ofctrl_recv(const struct ofp_header *, enum ofptype);
-
-void
-ofctrl_init(struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- int inactivity_probe_interval)
-{
- swconn = rconn_create(inactivity_probe_interval, 0,
- DSCP_DEFAULT, 1 << OFP13_VERSION);
- tx_counter = rconn_packet_counter_create();
- hmap_init(&installed_flows);
- ovs_list_init(&flow_updates);
- ovn_init_symtab(&symtab);
- groups = group_table;
- meters = meter_table;
-}
-
-/* S_NEW, for a new connection.
- *
- * Sends NXT_TLV_TABLE_REQUEST and transitions to
- * S_TLV_TABLE_REQUESTED. */
-
-static void
-run_S_NEW(void)
-{
- struct ofpbuf *buf = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_REQUEST,
- rconn_get_version(swconn), 0);
- xid = queue_msg(buf);
- state = S_TLV_TABLE_REQUESTED;
-}
-
-static void
-recv_S_NEW(const struct ofp_header *oh OVS_UNUSED,
- enum ofptype type OVS_UNUSED,
- struct shash *pending_ct_zones OVS_UNUSED)
-{
- OVS_NOT_REACHED();
-}
-
-/* S_TLV_TABLE_REQUESTED, when NXT_TLV_TABLE_REQUEST has been sent
- * and we're waiting for a reply.
- *
- * If we receive an NXT_TLV_TABLE_REPLY:
- *
- * - If it contains our tunnel metadata option, assign its field ID to
- * mff_ovn_geneve and transition to S_CLEAR_FLOWS.
- *
- * - Otherwise, if there is an unused tunnel metadata field ID, send
- * NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST, and transition to
- * S_TLV_TABLE_MOD_SENT.
- *
- * - Otherwise, log an error, disable Geneve, and transition to
- * S_CLEAR_FLOWS.
- *
- * If we receive an OFPT_ERROR:
- *
- * - Log an error, disable Geneve, and transition to S_CLEAR_FLOWS. */
-
-static void
-run_S_TLV_TABLE_REQUESTED(void)
-{
-}
-
-static bool
-process_tlv_table_reply(const struct ofputil_tlv_table_reply *reply)
-{
- const struct ofputil_tlv_map *map;
- uint64_t md_free = UINT64_MAX;
- BUILD_ASSERT(TUN_METADATA_NUM_OPTS == 64);
-
- LIST_FOR_EACH (map, list_node, &reply->mappings) {
- if (map->option_class == OVN_GENEVE_CLASS
- && map->option_type == OVN_GENEVE_TYPE
- && map->option_len == OVN_GENEVE_LEN) {
- if (map->index >= TUN_METADATA_NUM_OPTS) {
- VLOG_ERR("desired Geneve tunnel option 0x%"PRIx16","
- "%"PRIu8",%"PRIu8" already in use with "
- "unsupported index %"PRIu16,
- map->option_class, map->option_type,
- map->option_len, map->index);
- return false;
- } else {
- mff_ovn_geneve = MFF_TUN_METADATA0 + map->index;
- state = S_CLEAR_FLOWS;
- return true;
- }
- }
-
- if (map->index < TUN_METADATA_NUM_OPTS) {
- md_free &= ~(UINT64_C(1) << map->index);
- }
- }
-
- VLOG_DBG("OVN Geneve option not found");
- if (!md_free) {
- VLOG_ERR("no Geneve options free for use by OVN");
- return false;
- }
-
- unsigned int index = rightmost_1bit_idx(md_free);
- mff_ovn_geneve = MFF_TUN_METADATA0 + index;
- struct ofputil_tlv_map tm;
- tm.option_class = OVN_GENEVE_CLASS;
- tm.option_type = OVN_GENEVE_TYPE;
- tm.option_len = OVN_GENEVE_LEN;
- tm.index = index;
-
- struct ofputil_tlv_table_mod ttm;
- ttm.command = NXTTMC_ADD;
- ovs_list_init(&ttm.mappings);
- ovs_list_push_back(&ttm.mappings, &tm.list_node);
-
- xid = queue_msg(ofputil_encode_tlv_table_mod(OFP13_VERSION, &ttm));
- xid2 = queue_msg(ofputil_encode_barrier_request(OFP13_VERSION));
- state = S_TLV_TABLE_MOD_SENT;
-
- return true;
-}
-
-static void
-recv_S_TLV_TABLE_REQUESTED(const struct ofp_header *oh, enum ofptype type,
- struct shash *pending_ct_zones OVS_UNUSED)
-{
- if (oh->xid != xid) {
- ofctrl_recv(oh, type);
- return;
- } else if (type == OFPTYPE_NXT_TLV_TABLE_REPLY) {
- struct ofputil_tlv_table_reply reply;
- enum ofperr error = ofputil_decode_tlv_table_reply(oh, &reply);
- if (!error) {
- bool ok = process_tlv_table_reply(&reply);
- ofputil_uninit_tlv_table(&reply.mappings);
- if (ok) {
- return;
- }
- } else {
- VLOG_ERR("failed to decode TLV table request (%s)",
- ofperr_to_string(error));
- }
- } else if (type == OFPTYPE_ERROR) {
- VLOG_ERR("switch refused to allocate Geneve option (%s)",
- ofperr_to_string(ofperr_decode_msg(oh, NULL)));
- } else {
- char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
- VLOG_ERR("unexpected reply to TLV table request (%s)", s);
- free(s);
- }
-
- /* Error path. */
- mff_ovn_geneve = 0;
- state = S_CLEAR_FLOWS;
-}
-
-/* S_TLV_TABLE_MOD_SENT, when NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST
- * have been sent and we're waiting for a reply to one or the other.
- *
- * If we receive an OFPT_ERROR:
- *
- * - If the error is NXTTMFC_ALREADY_MAPPED or NXTTMFC_DUP_ENTRY, we
- * raced with some other controller. Transition to S_NEW.
- *
- * - Otherwise, log an error, disable Geneve, and transition to
- * S_CLEAR_FLOWS.
- *
- * If we receive OFPT_BARRIER_REPLY:
- *
- * - Set the tunnel metadata field ID to the one that we requested.
- * Transition to S_CLEAR_FLOWS.
- */
-
-static void
-run_S_TLV_TABLE_MOD_SENT(void)
-{
-}
-
-static void
-recv_S_TLV_TABLE_MOD_SENT(const struct ofp_header *oh, enum ofptype type,
- struct shash *pending_ct_zones OVS_UNUSED)
-{
- if (oh->xid != xid && oh->xid != xid2) {
- ofctrl_recv(oh, type);
- } else if (oh->xid == xid2 && type == OFPTYPE_BARRIER_REPLY) {
- state = S_CLEAR_FLOWS;
- } else if (oh->xid == xid && type == OFPTYPE_ERROR) {
- enum ofperr error = ofperr_decode_msg(oh, NULL);
- if (error == OFPERR_NXTTMFC_ALREADY_MAPPED ||
- error == OFPERR_NXTTMFC_DUP_ENTRY) {
- VLOG_INFO("raced with another controller adding "
- "Geneve option (%s); trying again",
- ofperr_to_string(error));
- state = S_NEW;
- } else {
- VLOG_ERR("error adding Geneve option (%s)",
- ofperr_to_string(error));
- goto error;
- }
- } else {
- char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
- VLOG_ERR("unexpected reply to Geneve option allocation request (%s)",
- s);
- free(s);
- goto error;
- }
- return;
-
-error:
- state = S_CLEAR_FLOWS;
-}
-
-/* S_CLEAR_FLOWS, after we've established a Geneve metadata field ID and it's
- * time to set up some flows.
- *
- * Sends an OFPT_TABLE_MOD to clear all flows, then transitions to
- * S_UPDATE_FLOWS. */
-
-static void
-run_S_CLEAR_FLOWS(void)
-{
- VLOG_DBG("clearing all flows");
-
- need_reinstall_flows = true;
- /* Send a flow_mod to delete all flows. */
- struct ofputil_flow_mod fm = {
- .table_id = OFPTT_ALL,
- .command = OFPFC_DELETE,
- };
- minimatch_init_catchall(&fm.match);
- queue_msg(encode_flow_mod(&fm));
- minimatch_destroy(&fm.match);
-
- /* Send a group_mod to delete all groups. */
- struct ofputil_group_mod gm;
- memset(&gm, 0, sizeof gm);
- gm.command = OFPGC11_DELETE;
- gm.group_id = OFPG_ALL;
- gm.command_bucket_id = OFPG15_BUCKET_ALL;
- ovs_list_init(&gm.buckets);
- queue_msg(encode_group_mod(&gm));
- ofputil_uninit_group_mod(&gm);
-
- /* Clear installed_flows, to match the state of the switch. */
- ovn_installed_flow_table_clear();
-
- /* Clear existing groups, to match the state of the switch. */
- if (groups) {
- ovn_extend_table_clear(groups, true);
- }
-
- /* Send a meter_mod to delete all meters. */
- struct ofputil_meter_mod mm;
- memset(&mm, 0, sizeof mm);
- mm.command = OFPMC13_DELETE;
- mm.meter.meter_id = OFPM13_ALL;
- queue_msg(encode_meter_mod(&mm));
-
- /* Clear existing meters, to match the state of the switch. */
- if (meters) {
- ovn_extend_table_clear(meters, true);
- }
-
- /* All flow updates are irrelevant now. */
- struct ofctrl_flow_update *fup, *next;
- LIST_FOR_EACH_SAFE (fup, next, list_node, &flow_updates) {
- ovs_list_remove(&fup->list_node);
- free(fup);
- }
-
- state = S_UPDATE_FLOWS;
-}
-
-static void
-recv_S_CLEAR_FLOWS(const struct ofp_header *oh, enum ofptype type,
- struct shash *pending_ct_zones OVS_UNUSED)
-{
- ofctrl_recv(oh, type);
-}
-
-/* S_UPDATE_FLOWS, for maintaining the flow table over time.
- *
- * Compare the installed flows to the ones we want. Send OFPT_FLOW_MOD as
- * necessary.
- *
- * This is a terminal state. We only transition out of it if the connection
- * drops. */
-
-static void
-run_S_UPDATE_FLOWS(void)
-{
- /* Nothing to do here.
- *
- * Being in this state enables ofctrl_put() to work, however. */
-}
-
-static void
-recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type,
- struct shash *pending_ct_zones)
-{
- if (type == OFPTYPE_BARRIER_REPLY && !ovs_list_is_empty(&flow_updates)) {
- struct ofctrl_flow_update *fup = ofctrl_flow_update_from_list_node(
- ovs_list_front(&flow_updates));
- if (fup->xid == oh->xid) {
- if (fup->nb_cfg >= cur_cfg) {
- cur_cfg = fup->nb_cfg;
- }
- ovs_list_remove(&fup->list_node);
- free(fup);
- }
-
- /* If the barrier xid is associated with an outstanding conntrack
- * flush, the flush succeeded. Move the pending ct zone entry
- * to the next stage. */
- struct shash_node *iter;
- SHASH_FOR_EACH(iter, pending_ct_zones) {
- struct ct_zone_pending_entry *ctzpe = iter->data;
- if (ctzpe->state == CT_ZONE_OF_SENT && ctzpe->of_xid == oh->xid) {
- ctzpe->state = CT_ZONE_DB_QUEUED;
- }
- }
- } else {
- ofctrl_recv(oh, type);
- }
-}
-
-
-enum mf_field_id
-ofctrl_get_mf_field_id(void)
-{
- if (!rconn_is_connected(swconn)) {
- return 0;
- }
- return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS
- ? mff_ovn_geneve : 0);
-}
-
-/* Runs the OpenFlow state machine against 'br_int', which is local to the
- * hypervisor on which we are running. Attempts to negotiate a Geneve option
- * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE. */
-void
-ofctrl_run(const struct ovsrec_bridge *br_int, struct shash *pending_ct_zones)
-{
- char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int->name);
- if (strcmp(target, rconn_get_target(swconn))) {
- VLOG_INFO("%s: connecting to switch", target);
- rconn_connect(swconn, target, target);
- }
- free(target);
-
- rconn_run(swconn);
-
- if (!rconn_is_connected(swconn)) {
- return;
- }
- if (seqno != rconn_get_connection_seqno(swconn)) {
- seqno = rconn_get_connection_seqno(swconn);
- state = S_NEW;
-
- /* Reset the state of any outstanding ct flushes to resend them. */
- struct shash_node *iter;
- SHASH_FOR_EACH(iter, pending_ct_zones) {
- struct ct_zone_pending_entry *ctzpe = iter->data;
- if (ctzpe->state == CT_ZONE_OF_SENT) {
- ctzpe->state = CT_ZONE_OF_QUEUED;
- }
- }
- }
-
- bool progress = true;
- for (int i = 0; progress && i < 50; i++) {
- /* Allow the state machine to run. */
- enum ofctrl_state old_state = state;
- switch (state) {
-#define STATE(NAME) case NAME: run_##NAME(); break;
- STATES
-#undef STATE
- default:
- OVS_NOT_REACHED();
- }
-
- /* Try to process a received packet. */
- struct ofpbuf *msg = rconn_recv(swconn);
- if (msg) {
- const struct ofp_header *oh = msg->data;
- enum ofptype type;
- enum ofperr error;
-
- error = ofptype_decode(&type, oh);
- if (!error) {
- switch (state) {
-#define STATE(NAME) case NAME: recv_##NAME(oh, type, pending_ct_zones); break;
- STATES
-#undef STATE
- default:
- OVS_NOT_REACHED();
- }
- } else {
- char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
- VLOG_WARN("could not decode OpenFlow message (%s): %s",
- ofperr_to_string(error), s);
- free(s);
- }
-
- ofpbuf_delete(msg);
- }
-
- /* If we did some work, plan to go around again. */
- progress = old_state != state || msg;
- }
- if (progress) {
- /* We bailed out to limit the amount of work we do in one go, to allow
- * other code a chance to run. We were still making progress at that
- * point, so ensure that we come back again without waiting. */
- poll_immediate_wake();
- }
-}
-
-void
-ofctrl_wait(void)
-{
- rconn_run_wait(swconn);
- rconn_recv_wait(swconn);
-}
-
-void
-ofctrl_destroy(void)
-{
- rconn_destroy(swconn);
- ovn_installed_flow_table_destroy();
- rconn_packet_counter_destroy(tx_counter);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
-}
-
-int64_t
-ofctrl_get_cur_cfg(void)
-{
- return cur_cfg;
-}
-
-static ovs_be32
-queue_msg(struct ofpbuf *msg)
-{
- const struct ofp_header *oh = msg->data;
- ovs_be32 xid_ = oh->xid;
- rconn_send(swconn, msg, tx_counter);
- return xid_;
-}
-
-static void
-log_openflow_rl(struct vlog_rate_limit *rl, enum vlog_level level,
- const struct ofp_header *oh, const char *title)
-{
- if (!vlog_should_drop(&this_module, level, rl)) {
- char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 2);
- vlog(&this_module, level, "%s: %s", title, s);
- free(s);
- }
-}
-
-static void
-ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
-{
- if (type == OFPTYPE_ECHO_REQUEST) {
- queue_msg(ofputil_encode_echo_reply(oh));
- } else if (type == OFPTYPE_ERROR) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
- log_openflow_rl(&rl, VLL_INFO, oh, "OpenFlow error");
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
- log_openflow_rl(&rl, VLL_DBG, oh, "OpenFlow packet ignored");
- }
-}
-
-/* Flow table interfaces to the rest of ovn-controller. */
-
-/* Adds a flow to 'desired_flows' with the specified 'match' and 'actions' to
- * the OpenFlow table numbered 'table_id' with the given 'priority' and
- * OpenFlow 'cookie'. The caller retains ownership of 'match' and 'actions'.
- *
- * The flow is also added to a hash index based on sb_uuid.
- *
- * This just assembles the desired flow table in memory. Nothing is actually
- * sent to the switch until a later call to ofctrl_put().
- *
- * The caller should initialize its own hmap to hold the flows. */
-void
-ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table,
- uint8_t table_id, uint16_t priority,
- uint64_t cookie, const struct match *match,
- const struct ofpbuf *actions,
- const struct uuid *sb_uuid,
- bool log_duplicate_flow)
-{
- struct ovn_flow *f = xmalloc(sizeof *f);
- f->table_id = table_id;
- f->priority = priority;
- minimatch_init(&f->match, match);
- f->ofpacts = xmemdup(actions->data, actions->size);
- f->ofpacts_len = actions->size;
- f->sb_uuid = *sb_uuid;
- f->match_hmap_node.hash = ovn_flow_match_hash(f);
- f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid);
- f->cookie = cookie;
-
- ovn_flow_log(f, "ofctrl_add_flow");
-
- if (ovn_flow_lookup(&flow_table->match_flow_table, f, true)) {
- if (log_duplicate_flow) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
- if (!VLOG_DROP_DBG(&rl)) {
- char *s = ovn_flow_to_string(f);
- VLOG_DBG("dropping duplicate flow: %s", s);
- free(s);
- }
- }
- ovn_flow_destroy(f);
- return;
- }
-
- hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node,
- f->match_hmap_node.hash);
- hindex_insert(&flow_table->uuid_flow_table, &f->uuid_hindex_node,
- f->uuid_hindex_node.hash);
-}
-
-/* Removes a bundles of flows from the flow table. */
-void
-ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table,
- const struct uuid *sb_uuid)
-{
- struct ovn_flow *f, *next;
- HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node,
- uuid_hash(sb_uuid),
- &flow_table->uuid_flow_table) {
- if (uuid_equals(&f->sb_uuid, sb_uuid)) {
- ovn_flow_log(f, "ofctrl_remove_flow");
- hmap_remove(&flow_table->match_flow_table,
- &f->match_hmap_node);
- hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node);
- ovn_flow_destroy(f);
- }
- }
-
- /* remove any related group and meter info */
- ovn_extend_table_remove_desired(groups, sb_uuid);
- ovn_extend_table_remove_desired(meters, sb_uuid);
-}
-
-void
-ofctrl_add_flow(struct ovn_desired_flow_table *desired_flows,
- uint8_t table_id, uint16_t priority, uint64_t cookie,
- const struct match *match, const struct ofpbuf *actions,
- const struct uuid *sb_uuid)
-{
- ofctrl_check_and_add_flow(desired_flows, table_id, priority, cookie,
- match, actions, sb_uuid, true);
-}
-
-/* ovn_flow. */
-
-/* Returns a hash of the match key in 'f'. */
-static uint32_t
-ovn_flow_match_hash(const struct ovn_flow *f)
-{
- return hash_2words((f->table_id << 16) | f->priority,
- minimatch_hash(&f->match, 0));
-}
-
-/* Duplicate an ovn_flow structure. */
-struct ovn_flow *
-ofctrl_dup_flow(struct ovn_flow *src)
-{
- struct ovn_flow *dst = xmalloc(sizeof *dst);
- dst->table_id = src->table_id;
- dst->priority = src->priority;
- minimatch_clone(&dst->match, &src->match);
- dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len);
- dst->ofpacts_len = src->ofpacts_len;
- dst->sb_uuid = src->sb_uuid;
- dst->match_hmap_node.hash = ovn_flow_match_hash(dst);
- dst->uuid_hindex_node.hash = uuid_hash(&src->sb_uuid);
- return dst;
-}
-
-/* Finds and returns an ovn_flow in 'flow_table' whose key is identical to
- * 'target''s key, or NULL if there is none. */
-static struct ovn_flow *
-ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target,
- bool cmp_sb_uuid)
-{
- struct ovn_flow *f;
-
- HMAP_FOR_EACH_WITH_HASH (f, match_hmap_node, target->match_hmap_node.hash,
- flow_table) {
- if (f->table_id == target->table_id
- && f->priority == target->priority
- && minimatch_equal(&f->match, &target->match)) {
- if (!cmp_sb_uuid || uuid_equals(&target->sb_uuid, &f->sb_uuid)) {
- return f;
- }
- }
- }
- return NULL;
-}
-
-static char *
-ovn_flow_to_string(const struct ovn_flow *f)
-{
- struct ds s = DS_EMPTY_INITIALIZER;
- ds_put_format(&s, "sb_uuid="UUID_FMT", ", UUID_ARGS(&f->sb_uuid));
- ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
- ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
- minimatch_format(&f->match, NULL, NULL, &s, OFP_DEFAULT_PRIORITY);
- ds_put_cstr(&s, ", actions=");
- struct ofpact_format_params fp = { .s = &s };
- ofpacts_format(f->ofpacts, f->ofpacts_len, &fp);
- return ds_steal_cstr(&s);
-}
-
-static void
-ovn_flow_log(const struct ovn_flow *f, const char *action)
-{
- if (VLOG_IS_DBG_ENABLED()) {
- char *s = ovn_flow_to_string(f);
- VLOG_DBG("%s flow: %s", action, s);
- free(s);
- }
-}
-
-static void
-ovn_flow_destroy(struct ovn_flow *f)
-{
- if (f) {
- minimatch_destroy(&f->match);
- free(f->ofpacts);
- free(f);
- }
-}
-
-/* Flow tables of struct ovn_flow. */
-void
-ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table)
-{
- hmap_init(&flow_table->match_flow_table);
- hindex_init(&flow_table->uuid_flow_table);
-}
-
-void
-ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table)
-{
- struct ovn_flow *f, *next;
- HMAP_FOR_EACH_SAFE (f, next, match_hmap_node,
- &flow_table->match_flow_table) {
- hmap_remove(&flow_table->match_flow_table, &f->match_hmap_node);
- hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node);
- ovn_flow_destroy(f);
- }
-}
-
-void
-ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *flow_table)
-{
- ovn_desired_flow_table_clear(flow_table);
- hmap_destroy(&flow_table->match_flow_table);
- hindex_destroy(&flow_table->uuid_flow_table);
-}
-
-static void
-ovn_installed_flow_table_clear(void)
-{
- struct ovn_flow *f, *next;
- HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &installed_flows) {
- hmap_remove(&installed_flows, &f->match_hmap_node);
- ovn_flow_destroy(f);
- }
-}
-
-static void
-ovn_installed_flow_table_destroy(void)
-{
- ovn_installed_flow_table_clear();
- hmap_destroy(&installed_flows);
-}
-
-/* Flow table update. */
-
-static struct ofpbuf *
-encode_flow_mod(struct ofputil_flow_mod *fm)
-{
- fm->buffer_id = UINT32_MAX;
- fm->out_port = OFPP_ANY;
- fm->out_group = OFPG_ANY;
- return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM);
-}
-
-static void
-add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs)
-{
- struct ofpbuf *msg = encode_flow_mod(fm);
- ovs_list_push_back(msgs, &msg->list_node);
-}
-
-/* group_table. */
-
-static struct ofpbuf *
-encode_group_mod(const struct ofputil_group_mod *gm)
-{
- return ofputil_encode_group_mod(OFP13_VERSION, gm, NULL, -1);
-}
-
-static void
-add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs)
-{
- struct ofpbuf *msg = encode_group_mod(gm);
- ovs_list_push_back(msgs, &msg->list_node);
-}
-
-
-static struct ofpbuf *
-encode_meter_mod(const struct ofputil_meter_mod *mm)
-{
- return ofputil_encode_meter_mod(OFP13_VERSION, mm);
-}
-
-static void
-add_meter_mod(const struct ofputil_meter_mod *mm, struct ovs_list *msgs)
-{
- struct ofpbuf *msg = encode_meter_mod(mm);
- ovs_list_push_back(msgs, &msg->list_node);
-}
-
-static void
-add_ct_flush_zone(uint16_t zone_id, struct ovs_list *msgs)
-{
- struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_CT_FLUSH_ZONE,
- rconn_get_version(swconn), 0);
- struct nx_zone_id *nzi = ofpbuf_put_zeros(msg, sizeof *nzi);
- nzi->zone_id = htons(zone_id);
-
- ovs_list_push_back(msgs, &msg->list_node);
-}
-
-static void
-add_meter_string(struct ovn_extend_table_info *m_desired,
- struct ovs_list *msgs)
-{
- /* Create and install new meter. */
- struct ofputil_meter_mod mm;
- enum ofputil_protocol usable_protocols;
- char *meter_string = xasprintf("meter=%"PRIu32",%s",
- m_desired->table_id,
- &m_desired->name[9]);
- char *error = parse_ofp_meter_mod_str(&mm, meter_string, OFPMC13_ADD,
- &usable_protocols);
- if (!error) {
- add_meter_mod(&mm, msgs);
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_ERR_RL(&rl, "new meter %s %s", error, meter_string);
- free(error);
- }
- free(meter_string);
-}
-
-static void
-add_meter(struct ovn_extend_table_info *m_desired,
- const struct sbrec_meter_table *meter_table,
- struct ovs_list *msgs)
-{
- const struct sbrec_meter *sb_meter;
- SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) {
- if (!strcmp(m_desired->name, sb_meter->name)) {
- break;
- }
- }
-
- if (!sb_meter) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_ERR_RL(&rl, "could not find meter named \"%s\"", m_desired->name);
- return;
- }
-
- struct ofputil_meter_mod mm;
- mm.command = OFPMC13_ADD;
- mm.meter.meter_id = m_desired->table_id;
- mm.meter.flags = OFPMF13_STATS;
-
- if (!strcmp(sb_meter->unit, "pktps")) {
- mm.meter.flags |= OFPMF13_PKTPS;
- } else {
- mm.meter.flags |= OFPMF13_KBPS;
- }
-
- mm.meter.n_bands = sb_meter->n_bands;
- mm.meter.bands = xcalloc(mm.meter.n_bands, sizeof *mm.meter.bands);
-
- for (size_t i = 0; i < sb_meter->n_bands; i++) {
- struct sbrec_meter_band *sb_band = sb_meter->bands[i];
- struct ofputil_meter_band *mm_band = &mm.meter.bands[i];
-
- if (!strcmp(sb_band->action, "drop")) {
- mm_band->type = OFPMBT13_DROP;
- }
-
- mm_band->prec_level = 0;
- mm_band->rate = sb_band->rate;
- mm_band->burst_size = sb_band->burst_size;
-
- if (mm_band->burst_size) {
- mm.meter.flags |= OFPMF13_BURST;
- }
- }
-
- add_meter_mod(&mm, msgs);
- free(mm.meter.bands);
-}
-
-/* The flow table can be updated if the connection to the switch is up and
- * in the correct state and not backlogged with existing flow_mods. (Our
- * criteria for being backlogged appear very conservative, but the socket
- * between ovn-controller and OVS provides some buffering.) */
-static bool
-ofctrl_can_put(void)
-{
- if (state != S_UPDATE_FLOWS
- || rconn_packet_counter_n_packets(tx_counter)
- || rconn_get_version(swconn) < 0) {
- return false;
- }
- return true;
-}
-
-/* Replaces the flow table on the switch, if possible, by the flows added
- * with ofctrl_add_flow().
- *
- * Replaces the group table and meter table on the switch, if possible,
- * by the contents of '->desired'.
- *
- * Sends conntrack flush messages to each zone in 'pending_ct_zones' that
- * is in the CT_ZONE_OF_QUEUED state and then moves the zone into the
- * CT_ZONE_OF_SENT state.
- *
- * This should be called after ofctrl_run() within the main loop. */
-void
-ofctrl_put(struct ovn_desired_flow_table *flow_table,
- struct shash *pending_ct_zones,
- const struct sbrec_meter_table *meter_table,
- int64_t nb_cfg,
- bool flow_changed)
-{
- static bool skipped_last_time = false;
- static int64_t old_nb_cfg = 0;
- bool need_put = false;
- if (flow_changed || skipped_last_time || need_reinstall_flows) {
- need_put = true;
- } else if (nb_cfg != old_nb_cfg) {
- /* nb_cfg changed since last ofctrl_put() call */
- if (cur_cfg == old_nb_cfg) {
- /* we were up-to-date already, so just update with the
- * new nb_cfg */
- cur_cfg = nb_cfg;
- } else {
- need_put = true;
- }
- }
-
- old_nb_cfg = nb_cfg;
-
- if (!need_put) {
- VLOG_DBG("ofctrl_put not needed");
- return;
- }
- if (!ofctrl_can_put()) {
- VLOG_DBG("ofctrl_put can't be performed");
- skipped_last_time = true;
- return;
- }
-
- skipped_last_time = false;
- need_reinstall_flows = false;
-
- /* OpenFlow messages to send to the switch to bring it up-to-date. */
- struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
-
- /* Iterate through ct zones that need to be flushed. */
- struct shash_node *iter;
- SHASH_FOR_EACH(iter, pending_ct_zones) {
- struct ct_zone_pending_entry *ctzpe = iter->data;
- if (ctzpe->state == CT_ZONE_OF_QUEUED) {
- add_ct_flush_zone(ctzpe->zone, &msgs);
- ctzpe->state = CT_ZONE_OF_SENT;
- ctzpe->of_xid = 0;
- }
- }
-
- /* Iterate through all the desired groups. If there are new ones,
- * add them to the switch. */
- struct ovn_extend_table_info *desired;
- EXTEND_TABLE_FOR_EACH_UNINSTALLED (desired, groups) {
- /* Create and install new group. */
- struct ofputil_group_mod gm;
- enum ofputil_protocol usable_protocols;
- char *group_string = xasprintf("group_id=%"PRIu32",%s",
- desired->table_id,
- desired->name);
- char *error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD, group_string,
- NULL, NULL, &usable_protocols);
- if (!error) {
- add_group_mod(&gm, &msgs);
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_ERR_RL(&rl, "new group %s %s", error, group_string);
- free(error);
- }
- free(group_string);
- ofputil_uninit_group_mod(&gm);
- }
-
- /* Iterate through all the desired meters. If there are new ones,
- * add them to the switch. */
- struct ovn_extend_table_info *m_desired;
- EXTEND_TABLE_FOR_EACH_UNINSTALLED (m_desired, meters) {
- if (!strncmp(m_desired->name, "__string: ", 10)) {
- /* The "set-meter" action creates a meter entry name that
- * describes the meter itself. */
- add_meter_string(m_desired, &msgs);
- } else {
- add_meter(m_desired, meter_table, &msgs);
- }
- }
-
- /* Iterate through all of the installed flows. If any of them are no
- * longer desired, delete them; if any of them should have different
- * actions, update them. */
- struct ovn_flow *i, *next;
- HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) {
- struct ovn_flow *d = ovn_flow_lookup(&flow_table->match_flow_table,
- i, false);
- if (!d) {
- /* Installed flow is no longer desirable. Delete it from the
- * switch and from installed_flows. */
- struct ofputil_flow_mod fm = {
- .match = i->match,
- .priority = i->priority,
- .table_id = i->table_id,
- .command = OFPFC_DELETE_STRICT,
- };
- add_flow_mod(&fm, &msgs);
- ovn_flow_log(i, "removing installed");
-
- hmap_remove(&installed_flows, &i->match_hmap_node);
- ovn_flow_destroy(i);
- } else {
- if (!uuid_equals(&i->sb_uuid, &d->sb_uuid)) {
- /* Update installed flow's UUID. */
- i->sb_uuid = d->sb_uuid;
- }
- if (!ofpacts_equal(i->ofpacts, i->ofpacts_len,
- d->ofpacts, d->ofpacts_len)) {
- /* Update actions in installed flow. */
- struct ofputil_flow_mod fm = {
- .match = i->match,
- .priority = i->priority,
- .table_id = i->table_id,
- .ofpacts = d->ofpacts,
- .ofpacts_len = d->ofpacts_len,
- .command = OFPFC_MODIFY_STRICT,
- };
- add_flow_mod(&fm, &msgs);
- ovn_flow_log(i, "updating installed");
-
- /* Replace 'i''s actions by 'd''s. */
- free(i->ofpacts);
- i->ofpacts = xmemdup(d->ofpacts, d->ofpacts_len);
- i->ofpacts_len = d->ofpacts_len;
- }
-
- }
- }
-
- /* Iterate through the desired flows and add those that aren't found
- * in the installed flow table. */
- struct ovn_flow *d;
- HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) {
- i = ovn_flow_lookup(&installed_flows, d, false);
- if (!i) {
- /* Send flow_mod to add flow. */
- struct ofputil_flow_mod fm = {
- .match = d->match,
- .priority = d->priority,
- .table_id = d->table_id,
- .ofpacts = d->ofpacts,
- .ofpacts_len = d->ofpacts_len,
- .new_cookie = htonll(d->cookie),
- .command = OFPFC_ADD,
- };
- add_flow_mod(&fm, &msgs);
- ovn_flow_log(d, "adding installed");
-
- /* Copy 'd' from 'flow_table' to installed_flows. */
- struct ovn_flow *new_node = ofctrl_dup_flow(d);
- hmap_insert(&installed_flows, &new_node->match_hmap_node,
- new_node->match_hmap_node.hash);
- }
- }
-
- /* Iterate through the installed groups from previous runs. If they
- * are not needed delete them. */
- struct ovn_extend_table_info *installed, *next_group;
- EXTEND_TABLE_FOR_EACH_INSTALLED (installed, next_group, groups) {
- /* Delete the group. */
- struct ofputil_group_mod gm;
- enum ofputil_protocol usable_protocols;
- char *group_string = xasprintf("group_id=%"PRIu32"",
- installed->table_id);
- char *error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
- group_string, NULL, NULL,
- &usable_protocols);
- if (!error) {
- add_group_mod(&gm, &msgs);
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_ERR_RL(&rl, "Error deleting group %d: %s",
- installed->table_id, error);
- free(error);
- }
- free(group_string);
- ofputil_uninit_group_mod(&gm);
- ovn_extend_table_remove_existing(groups, installed);
- }
-
- /* Sync the contents of groups->desired to groups->existing. */
- ovn_extend_table_sync(groups);
-
- /* Iterate through the installed meters from previous runs. If they
- * are not needed delete them. */
- struct ovn_extend_table_info *m_installed, *next_meter;
- EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_meter, meters) {
- /* Delete the meter. */
- struct ofputil_meter_mod mm = {
- .command = OFPMC13_DELETE,
- .meter = { .meter_id = m_installed->table_id },
- };
- add_meter_mod(&mm, &msgs);
-
- ovn_extend_table_remove_existing(meters, m_installed);
- }
-
- /* Sync the contents of meters->desired to meters->existing. */
- ovn_extend_table_sync(meters);
-
- if (!ovs_list_is_empty(&msgs)) {
- /* Add a barrier to the list of messages. */
- struct ofpbuf *barrier = ofputil_encode_barrier_request(OFP13_VERSION);
- const struct ofp_header *oh = barrier->data;
- ovs_be32 xid_ = oh->xid;
- ovs_list_push_back(&msgs, &barrier->list_node);
-
- /* Queue the messages. */
- struct ofpbuf *msg;
- LIST_FOR_EACH_POP (msg, list_node, &msgs) {
- queue_msg(msg);
- }
-
- /* Store the barrier's xid with any newly sent ct flushes. */
- SHASH_FOR_EACH(iter, pending_ct_zones) {
- struct ct_zone_pending_entry *ctzpe = iter->data;
- if (ctzpe->state == CT_ZONE_OF_SENT && !ctzpe->of_xid) {
- ctzpe->of_xid = xid_;
- }
- }
-
- /* Track the flow update. */
- struct ofctrl_flow_update *fup, *prev;
- LIST_FOR_EACH_REVERSE_SAFE (fup, prev, list_node, &flow_updates) {
- if (nb_cfg < fup->nb_cfg) {
- /* This ofctrl_flow_update is for a configuration later than
- * 'nb_cfg'. This should not normally happen, because it means
- * that 'nb_cfg' in the SB_Global table of the southbound
- * database decreased, and it should normally be monotonically
- * increasing. */
- VLOG_WARN("nb_cfg regressed from %"PRId64" to %"PRId64,
- fup->nb_cfg, nb_cfg);
- ovs_list_remove(&fup->list_node);
- free(fup);
- } else if (nb_cfg == fup->nb_cfg) {
- /* This ofctrl_flow_update is for the same configuration as
- * 'nb_cfg'. Probably, some change to the physical topology
- * means that we had to revise the OpenFlow flow table even
- * though the logical topology did not change. Update fp->xid,
- * so that we don't send a notification that we're up-to-date
- * until we're really caught up. */
- VLOG_DBG("advanced xid target for nb_cfg=%"PRId64, nb_cfg);
- fup->xid = xid_;
- goto done;
- } else {
- break;
- }
- }
-
- /* Add a flow update. */
- fup = xmalloc(sizeof *fup);
- ovs_list_push_back(&flow_updates, &fup->list_node);
- fup->xid = xid_;
- fup->nb_cfg = nb_cfg;
- done:;
- } else if (!ovs_list_is_empty(&flow_updates)) {
- /* Getting up-to-date with 'nb_cfg' didn't require any extra flow table
- * changes, so whenever we get up-to-date with the most recent flow
- * table update, we're also up-to-date with 'nb_cfg'. */
- struct ofctrl_flow_update *fup = ofctrl_flow_update_from_list_node(
- ovs_list_back(&flow_updates));
- fup->nb_cfg = nb_cfg;
- } else {
- /* We were completely up-to-date before and still are. */
- cur_cfg = nb_cfg;
- }
-}
-
-/* Looks up the logical port with the name 'port_name' in 'br_int_'. If
- * found, returns true and sets '*portp' to the OpenFlow port number
- * assigned to the port. Otherwise, returns false. */
-static bool
-ofctrl_lookup_port(const void *br_int_, const char *port_name,
- unsigned int *portp)
-{
- const struct ovsrec_bridge *br_int = br_int_;
-
- for (int i = 0; i < br_int->n_ports; i++) {
- const struct ovsrec_port *port_rec = br_int->ports[i];
- for (int j = 0; j < port_rec->n_interfaces; j++) {
- const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
- const char *iface_id = smap_get(&iface_rec->external_ids,
- "iface-id");
-
- if (iface_id && !strcmp(iface_id, port_name)) {
- if (!iface_rec->n_ofport) {
- continue;
- }
-
- int64_t ofport = iface_rec->ofport[0];
- if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) {
- continue;
- }
- *portp = ofport;
- return true;
- }
- }
- }
-
- return false;
-}
-
-/* Generates a packet described by 'flow_s' in the syntax of an OVN
- * logical expression and injects it into 'br_int'. The flow
- * description must contain an ingress logical port that is present on
- * 'br_int'.
- *
- * Returns NULL if successful, otherwise an error message that the caller
- * must free(). */
-char *
-ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, const char *flow_s,
- const struct shash *addr_sets,
- const struct shash *port_groups)
-{
- int version = rconn_get_version(swconn);
- if (version < 0) {
- return xstrdup("OpenFlow channel not ready.");
- }
-
- struct flow uflow;
- char *error = expr_parse_microflow(flow_s, &symtab, addr_sets,
- port_groups, ofctrl_lookup_port,
- br_int, &uflow);
- if (error) {
- return error;
- }
-
- /* The physical OpenFlow port was stored in the logical ingress
- * port, so put it in the correct location for a flow structure. */
- uflow.in_port.ofp_port = u16_to_ofp(uflow.regs[MFF_LOG_INPORT - MFF_REG0]);
- uflow.regs[MFF_LOG_INPORT - MFF_REG0] = 0;
-
- if (!uflow.in_port.ofp_port) {
- return xstrdup("ingress port not found on hypervisor.");
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- flow_compose(&packet, &uflow, NULL, 64);
-
- uint64_t ofpacts_stub[1024 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
- resubmit->in_port = OFPP_IN_PORT;
- resubmit->table_id = 0;
-
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(&packet),
- .packet_len = dp_packet_size(&packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, uflow.in_port.ofp_port);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- queue_msg(ofputil_encode_packet_out(&po, proto));
- dp_packet_uninit(&packet);
- ofpbuf_uninit(&ofpacts);
-
- return NULL;
-}
-
-bool
-ofctrl_is_connected(void)
-{
- return rconn_is_connected(swconn);
-}
-
-void
-ofctrl_set_probe_interval(int probe_interval)
-{
- if (swconn) {
- rconn_set_probe_interval(swconn, probe_interval);
- }
-}
diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
deleted file mode 100644
index ed8918aae..000000000
--- a/ovn/controller/ofctrl.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef OFCTRL_H
-#define OFCTRL_H 1
-
-#include <stdint.h>
-
-#include "openvswitch/meta-flow.h"
-#include "ovsdb-idl.h"
-#include "hindex.h"
-
-struct ovn_extend_table;
-struct hmap;
-struct match;
-struct ofpbuf;
-struct ovsrec_bridge;
-struct sbrec_meter_table;
-struct shash;
-
-struct ovn_desired_flow_table {
- /* Hash map flow table using flow match conditions as hash key.*/
- struct hmap match_flow_table;
-
- /* SB uuid index for the nodes in match_flow_table.*/
- struct hindex uuid_flow_table;
-};
-
-/* Interface for OVN main loop. */
-void ofctrl_init(struct ovn_extend_table *group_table,
- struct ovn_extend_table *meter_table,
- int inactivity_probe_interval);
-void ofctrl_run(const struct ovsrec_bridge *br_int,
- struct shash *pending_ct_zones);
-enum mf_field_id ofctrl_get_mf_field_id(void);
-void ofctrl_put(struct ovn_desired_flow_table *,
- struct shash *pending_ct_zones,
- const struct sbrec_meter_table *,
- int64_t nb_cfg,
- bool flow_changed);
-void ofctrl_wait(void);
-void ofctrl_destroy(void);
-int64_t ofctrl_get_cur_cfg(void);
-
-struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source);
-
-void ofctrl_ct_flush_zone(uint16_t zone_id);
-
-char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int,
- const char *flow_s, const struct shash *addr_sets,
- const struct shash *port_groups);
-
-/* Flow table interfaces to the rest of ovn-controller. */
-void ofctrl_add_flow(struct ovn_desired_flow_table *, uint8_t table_id,
- uint16_t priority, uint64_t cookie,
- const struct match *, const struct ofpbuf *ofpacts,
- const struct uuid *);
-
-void ofctrl_remove_flows(struct ovn_desired_flow_table *, const struct uuid *);
-
-void ovn_desired_flow_table_init(struct ovn_desired_flow_table *);
-void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *);
-void ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *);
-
-void ofctrl_check_and_add_flow(struct ovn_desired_flow_table *,
- uint8_t table_id, uint16_t priority,
- uint64_t cookie, const struct match *,
- const struct ofpbuf *ofpacts,
- const struct uuid *, bool log_duplicate_flow);
-
-bool ofctrl_is_connected(void);
-void ofctrl_set_probe_interval(int probe_interval);
-
-#endif /* ovn/ofctrl.h */
diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml
deleted file mode 100644
index 780625ff8..000000000
--- a/ovn/controller/ovn-controller.8.xml
+++ /dev/null
@@ -1,456 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manpage program="ovn-controller" section="8" title="ovn-controller">
- <h1>Name</h1>
- <p>ovn-controller -- Open Virtual Network local controller</p>
-
- <h1>Synopsis</h1>
- <p><code>ovn-controller</code> [<var>options</var>] [<var>ovs-database</var>]</p>
-
- <h1>Description</h1>
- <p>
- <code>ovn-controller</code> is the local controller daemon for
- OVN, the Open Virtual Network. It connects up to the OVN
- Southbound database (see <code>ovn-sb</code>(5)) over the OVSDB
- protocol, and down to the Open vSwitch database (see
- <code>ovs-vswitchd.conf.db</code>(5)) over the OVSDB protocol and
- to <code>ovs-vswitchd</code>(8) via OpenFlow. Each hypervisor and
- software gateway in an OVN deployment runs its own independent
- copy of <code>ovn-controller</code>; thus,
- <code>ovn-controller</code>'s downward connections are
- machine-local and do not run over a physical network.
- </p>
-
- <h1>ACL Logging</h1>
- <p>
- ACL log messages are logged through <code>ovn-controller</code>'s
- logging mechanism. ACL log entries have the module
- <code>acl_log</code> at log level <code>info</code>. Configuring
- logging is described below in the <code>Logging Options</code>
- section.
- </p>
-
- <h1>Options</h1>
-
- <h2>Daemon Options</h2>
- <xi:include href="lib/daemon.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>Logging Options</h2>
- <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>PKI Options</h2>
- <p>
- PKI configuration is required in order to use SSL for the connections to
- the Northbound and Southbound databases.
- </p>
- <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
- <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
- <xi:include href="lib/ssl-peer-ca-cert.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>Other Options</h2>
-
- <xi:include href="lib/common.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
-
- <h1>Configuration</h1>
- <p>
- <code>ovn-controller</code> retrieves most of its configuration
- information from the local Open vSwitch's ovsdb-server instance.
- The default location is <code>db.sock</code> in the local Open
- vSwitch's "run" directory. It may be overridden by specifying the
- <var>ovs-database</var> argument as an OVSDB active or passive
- connection method, as described in <code>ovsdb</code>(7).
- </p>
-
- <p>
- <code>ovn-controller</code> assumes it gets configuration
- information from the following keys in the <code>Open_vSwitch</code>
- table of the local OVS instance:
- </p>
- <dl>
- <dt><code>external_ids:system-id</code></dt>
- <dd>The chassis name to use in the Chassis table.</dd>
-
- <dt><code>external_ids:hostname</code></dt>
- <dd>The hostname to use in the Chassis table.</dd>
-
- <dt><code>external_ids:ovn-bridge</code></dt>
- <dd>
- The integration bridge to which logical ports are attached. The
- default is <code>br-int</code>. If this bridge does not exist when
- ovn-controller starts, it will be created automatically with the
- default configuration suggested in <code>ovn-architecture</code>(7).
- </dd>
-
- <dt><code>external_ids:ovn-bridge-datapath-type</code></dt>
- <dd>
- This configuration is optional. If set, then the datapath type of
- the integration bridge will be set to the configured value. If this
- option is not set, then <code>ovn-controller</code> will not modify
- the existing <code>datapath-type</code> of the integration bridge.
- </dd>
-
- <dt><code>external_ids:ovn-remote</code></dt>
- <dd>
- <p>
- The OVN database that this system should connect to for its
- configuration, in one of the same forms documented above for the
- <var>ovs-database</var>.
- </p>
- </dd>
-
- <dt><code>external_ids:ovn-remote-probe-interval</code></dt>
- <dd>
- <p>
- The inactivity probe interval of the connection to the OVN database,
- in milliseconds.
- If the value is zero, it disables the connection keepalive feature.
- </p>
-
- <p>
- If the value is nonzero, then it will be forced to a value of
- at least 1000 ms.
- </p>
- </dd>
-
- <dt><code>external_ids:ovn-openflow-probe-interval</code></dt>
- <dd>
- <p>
- The inactivity probe interval of the OpenFlow connection to the
- OpenvSwitch integration bridge, in seconds.
- If the value is zero, it disables the connection keepalive feature.
- </p>
-
- <p>
- If the value is nonzero, then it will be forced to a value of
- at least 5s.
- </p>
- </dd>
-
- <dt><code>external_ids:ovn-encap-type</code></dt>
- <dd>
- <p>
- The encapsulation type that a chassis should use to connect to
- this node. Multiple encapsulation types may be specified with
- a comma-separated list. Each listed encapsulation type will
- be paired with <code>ovn-encap-ip</code>.
- </p>
-
- <p>
- Supported tunnel types for connecting hypervisors
- are <code>geneve</code> and <code>stt</code>. Gateways may
- use <code>geneve</code>, <code>vxlan</code>, or
- <code>stt</code>.
- </p>
-
- <p>
- Due to the limited amount of metadata in <code>vxlan</code>,
- the capabilities and performance of connected gateways will be
- reduced versus other tunnel formats.
- </p>
- </dd>
-
- <dt><code>external_ids:ovn-encap-ip</code></dt>
- <dd>
- The IP address that a chassis should use to connect to this node
- using encapsulation types specified by
- <code>external_ids:ovn-encap-type</code>.
- </dd>
-
- <dt><code>external_ids:ovn-bridge-mappings</code></dt>
- <dd>
- A list of key-value pairs that map a physical network name to a local
- ovs bridge that provides connectivity to that network. An example
- value mapping two physical network names to two ovs bridges would be:
- <code>physnet1:br-eth0,physnet2:br-eth1</code>.
- </dd>
-
- <dt><code>external_ids:ovn-encap-csum</code></dt>
- <dd>
- <code>ovn-encap-csum</code> indicates that encapsulation checksums can
- be transmitted and received with reasonable performance. It is a hint
- to senders transmitting data to this chassis that they should use
- checksums to protect OVN metadata. Set to <code>true</code> to enable
- or <code>false</code> to disable. Depending on the capabilities of the
- network interface card, enabling encapsulation checksum may incur
- performance loss. In such cases, encapsulation checksums can be disabled.
- </dd>
-
- <dt><code>external_ids:ovn-cms-options</code></dt>
- <dd>
- A list of options that will be consumed by the CMS Plugin and which
- specific to this particular chassis. An example would be:
- <code>cms_option1,cms_option2:foo</code>.
- </dd>
-
- <dt><code>external_ids:ovn-transport-zones</code></dt>
- <dd>
- <p>
- The transport zone(s) that this chassis belongs to. Transport
- zones is a way to group different chassis so that tunnels are only
- formed between members of the same group(s). Multiple transport
- zones may be specified with a comma-separated list. For example:
- tz1,tz2,tz3.
- </p>
- <p>
- If not set, the Chassis will be considered part of a default
- transport zone.
- </p>
- </dd>
- <dt><code>external_ids:ovn-chassis-mac-mappings</code></dt>
- <dd>
- A list of key-value pairs that map a chassis specific mac to
- a physical network name. An example
- value mapping two chassis macs to two physical network names would be:
- <code>physnet1:aa:bb:cc:dd:ee:ff,physnet2:a1:b2:c3:d4:e5:f6</code>.
- These are the macs that ovn-controller will replace a router port
- mac with, if packet is going from a distributed router port on
- vlan type logical switch.
- </dd>
- </dl>
-
- <p>
- <code>ovn-controller</code> reads the following values from the
- <code>Open_vSwitch</code> database of the local OVS instance:
- </p>
-
- <dl>
- <dt><code>datapath-type</code> from <ref table="Bridge" db="Open_vSwitch"/> table</dt>
- <dd>
- This value is read from local OVS integration bridge row of
- <ref table="Bridge" db="Open_vSwitch"/> table and populated in
- <ref key="datapath-type" table="Chassis" column="external_ids"
- db="OVN_Southbound"/> of the <ref table="Chassis" db="OVN_Southbound"/>
- table in the OVN_Southbound database.
- </dd>
-
- <dt><code>iface-types</code> from <ref table="Open_vSwitch" db="Open_vSwitch"/> table</dt>
- <dd>
- This value is populated in <ref key="iface-types" table="Chassis"
- column="external_ids" db="OVN_Southbound"/> of the
- <ref table="Chassis" db="OVN_Southbound"/> table in the OVN_Southbound
- database.
- </dd>
-
- <dt><code>private_key</code>, <code>certificate</code>,
- <code>ca_cert</code>, and <code>bootstrap_ca_cert</code>
- from <ref table="SSL" db="Open_vSwitch"/> table</dt>
- <dd>
- These values provide the SSL configuration used for connecting
- to the OVN southbound database server when an SSL connection type
- is configured via <code>external_ids:ovn-remote</code>. Note that
- this SSL configuration can also be provided via command-line options,
- the configuration in the database takes precedence if both are present.
- </dd>
- </dl>
-
- <h1>Open vSwitch Database Usage</h1>
-
- <p>
- <code>ovn-controller</code> uses a number of <code>external_ids</code>
- keys in the Open vSwitch database to keep track of ports and interfaces.
- For proper operation, users should not change or clear these keys:
- </p>
-
- <dl>
- <dt>
- <code>external_ids:ovn-chassis-id</code> in the <code>Port</code> table
- </dt>
- <dd>
- The presence of this key identifies a tunnel port within the
- integration bridge as one created by <code>ovn-controller</code> to
- reach a remote chassis. Its value is the chassis ID of the remote
- chassis.
- </dd>
-
- <dt>
- <code>external_ids:ct-zone-*</code> in the <code>Bridge</code> table
- </dt>
- <dd>
- Logical ports and gateway routers are assigned a connection
- tracking zone by <code>ovn-controller</code> for stateful
- services. To keep state across restarts of
- <code>ovn-controller</code>, these keys are stored in the
- integration bridge's Bridge table. The name contains a prefix
- of <code>ct-zone-</code> followed by the name of the logical
- port or gateway router's zone key. The value for this key
- identifies the zone used for this port.
- </dd>
-
- <dt>
- <code>external_ids:ovn-localnet-port</code> in the <code>Port</code>
- table
- </dt>
- <dd>
- <p>
- The presence of this key identifies a patch port as one created by
- <code>ovn-controller</code> to connect the integration bridge and
- another bridge to implement a <code>localnet</code> logical port.
- Its value is the name of the logical port with <code>type</code>
- set to <code>localnet</code> that the port implements. See
- <code>external_ids:ovn-bridge-mappings</code>, above, for more
- information.
- </p>
-
- <p>
- Each <code>localnet</code> logical port is implemented as a pair of
- patch ports, one in the integration bridge, one in a different
- bridge, with the same <code>external_ids:ovn-localnet-port</code>
- value.
- </p>
- </dd>
-
- <dt>
- <code>external_ids:ovn-l2gateway-port</code> in the <code>Port</code>
- table
- </dt>
- <dd>
- <p>
- The presence of this key identifies a patch port as one created by
- <code>ovn-controller</code> to connect the integration bridge and
- another bridge to implement a <code>l2gateway</code> logical port.
- Its value is the name of the logical port with <code>type</code>
- set to <code>l2gateway</code> that the port implements. See
- <code>external_ids:ovn-bridge-mappings</code>, above, for more
- information.
- </p>
-
- <p>
- Each <code>l2gateway</code> logical port is implemented as a pair
- of patch ports, one in the integration bridge, one in a different
- bridge, with the same <code>external_ids:ovn-l2gateway-port</code>
- value.
- </p>
- </dd>
-
- <dt>
- <code>external-ids:ovn-l3gateway-port</code> in the <code>Port</code>
- table
- </dt>
-
- <dd>
- <p>
- This key identifies a patch port as one created by
- <code>ovn-controller</code> to implement a <code>l3gateway</code>
- logical port. Its value is the name of the logical port with type
- set to <code>l3gateway</code>. This patch port is similar to
- the OVN logical patch port, except that <code>l3gateway</code>
- port can only be bound to a paticular chassis.
- </p>
- </dd>
-
- <dt>
- <code>external-ids:ovn-logical-patch-port</code> in the
- <code>Port</code> table
- </dt>
-
- <dd>
- <p>
- This key identifies a patch port as one created by
- <code>ovn-controller</code> to implement an OVN logical patch port
- within the integration bridge. Its value is the name of the OVN
- logical patch port that it implements.
- </p>
- </dd>
- </dl>
-
- <h1>OVN Southbound Database Usage</h1>
-
- <p>
- <code>ovn-controller</code> reads from much of the
- <code>OVN_Southbound</code> database to guide its operation.
- <code>ovn-controller</code> also writes to the following tables:
- </p>
-
- <dl>
- <dt><code>Chassis</code></dt>
- <dd>
- Upon startup, <code>ovn-controller</code> creates a row in this table
- to represent its own chassis. Upon graceful termination, e.g. with
- <code>ovs-appctl -t ovn-controller exit</code> (but not
- <code>SIGTERM</code>), <code>ovn-controller</code> removes its row.
- </dd>
-
- <dt><code>Encap</code></dt>
- <dd>
- Upon startup, <code>ovn-controller</code> creates a row or rows in this
- table that represent the tunnel encapsulations by which its chassis can
- be reached, and points its <code>Chassis</code> row to them. Upon
- graceful termination, <code>ovn-controller</code> removes these rows.
- </dd>
-
- <dt><code>Port_Binding</code></dt>
- <dd>
- At runtime, <code>ovn-controller</code> sets the <code>chassis</code>
- columns of ports that are resident on its chassis to point to its
- <code>Chassis</code> row, and, conversely, clears the
- <code>chassis</code> column of ports that point to its
- <code>Chassis</code> row but are no longer resident on its chassis.
- The <code>chassis</code> column has a weak reference type, so when
- <code>ovn-controller</code> gracefully exits and removes its
- <code>Chassis</code> row, the database server automatically clears any
- remaining references to that row.
- </dd>
-
- <dt><code>MAC_Binding</code></dt>
- <dd>
- At runtime, <code>ovn-controller</code> updates the
- <code>MAC_Binding</code> table as instructed by <code>put_arp</code>
- and <code>put_nd</code> logical actions. These changes persist beyond
- the lifetime of <code>ovn-controller</code>.
- </dd>
- </dl>
-
- <h1>Runtime Management Commands</h1>
- <p>
- <code>ovs-appctl</code> can send commands to a running
- <code>ovn-controller</code> process. The currently supported
- commands are described below.
- <dl>
- <dt><code>exit</code></dt>
- <dd>
- Causes <code>ovn-controller</code> to gracefully terminate.
- </dd>
-
- <dt><code>ct-zone-list</code></dt>
- <dd>
- Lists each local logical port and its connection tracking zone.
- </dd>
-
- <dt><code>meter-table-list</code></dt>
- <dd>
- Lists each meter table entry and its local meter id.
- </dd>
-
- <dt><code>group-table-list</code></dt>
- <dd>
- Lists each group table entry and its local group id.
- </dd>
-
- <dt><code>inject-pkt</code> <var>microflow</var></dt>
- <dd>
- <p>
- Injects <var>microflow</var> into the connected Open vSwitch
- instance. <var>microflow</var> must contain an ingress logical
- port (<code>inport</code> argument) that is present on the Open
- vSwitch instance.
- </p>
-
- <p>
- The <var>microflow</var> argument describes the packet whose
- forwarding is to be simulated, in the syntax of an OVN logical
- expression, as described in <code>ovn-sb</code>(5), to express
- constraints. The parser understands prerequisites; for example,
- if the expression refers to <code>ip4.src</code>, there is no
- need to explicitly state <code>ip4</code> or <code>eth.type ==
- 0x800</code>.
- </p>
- </dd>
-
- <dt><code>connection-status</code></dt>
- <dd>
- Show OVN SBDB connection status for the chassis.
- </dd>
- </dl>
- </p>
-
-</manpage>
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
deleted file mode 100644
index cf6c8ae79..000000000
--- a/ovn/controller/ovn-controller.c
+++ /dev/null
@@ -1,2366 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "ovn-controller.h"
-
-#include <errno.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "bfd.h"
-#include "binding.h"
-#include "chassis.h"
-#include "command-line.h"
-#include "compiler.h"
-#include "daemon.h"
-#include "dirs.h"
-#include "openvswitch/dynamic-string.h"
-#include "encaps.h"
-#include "fatal-signal.h"
-#include "ip-mcast.h"
-#include "openvswitch/hmap.h"
-#include "lflow.h"
-#include "lib/vswitch-idl.h"
-#include "lport.h"
-#include "ofctrl.h"
-#include "openvswitch/vconn.h"
-#include "openvswitch/vlog.h"
-#include "ovn/actions.h"
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/extend-table.h"
-#include "ovn/lib/ip-mcast-index.h"
-#include "ovn/lib/mcast-group-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn/lib/ovn-util.h"
-#include "patch.h"
-#include "physical.h"
-#include "pinctrl.h"
-#include "openvswitch/poll-loop.h"
-#include "lib/bitmap.h"
-#include "lib/hash.h"
-#include "smap.h"
-#include "sset.h"
-#include "stream-ssl.h"
-#include "stream.h"
-#include "unixctl.h"
-#include "util.h"
-#include "timeval.h"
-#include "timer.h"
-#include "stopwatch.h"
-#include "ovn/lib/inc-proc-eng.h"
-
-VLOG_DEFINE_THIS_MODULE(main);
-
-static unixctl_cb_func ovn_controller_exit;
-static unixctl_cb_func ct_zone_list;
-static unixctl_cb_func meter_table_list;
-static unixctl_cb_func group_table_list;
-static unixctl_cb_func inject_pkt;
-static unixctl_cb_func ovn_controller_conn_show;
-
-#define DEFAULT_BRIDGE_NAME "br-int"
-#define DEFAULT_PROBE_INTERVAL_MSEC 5000
-#define OFCTRL_DEFAULT_PROBE_INTERVAL_SEC 5
-
-#define CONTROLLER_LOOP_STOPWATCH_NAME "ovn-controller-flow-generation"
-
-static char *parse_options(int argc, char *argv[]);
-OVS_NO_RETURN static void usage(void);
-
-/* Pending packet to be injected into connected OVS. */
-struct pending_pkt {
- /* Setting 'conn' indicates that a request is pending. */
- struct unixctl_conn *conn;
- char *flow_s;
-};
-
-struct local_datapath *
-get_local_datapath(const struct hmap *local_datapaths, uint32_t tunnel_key)
-{
- struct hmap_node *node = hmap_first_with_hash(local_datapaths, tunnel_key);
- return (node
- ? CONTAINER_OF(node, struct local_datapath, hmap_node)
- : NULL);
-}
-
-uint32_t
-get_tunnel_type(const char *name)
-{
- if (!strcmp(name, "geneve")) {
- return GENEVE;
- } else if (!strcmp(name, "stt")) {
- return STT;
- } else if (!strcmp(name, "vxlan")) {
- return VXLAN;
- }
-
- return 0;
-}
-
-const struct ovsrec_bridge *
-get_bridge(const struct ovsrec_bridge_table *bridge_table, const char *br_name)
-{
- const struct ovsrec_bridge *br;
- OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
- if (!strcmp(br->name, br_name)) {
- return br;
- }
- }
- return NULL;
-}
-
-static void
-update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
- const struct sbrec_chassis *chassis,
- const struct sset *local_ifaces,
- struct hmap *local_datapaths)
-{
- /* Monitor Port_Bindings rows for local interfaces and local datapaths.
- *
- * Monitor Logical_Flow, MAC_Binding, Multicast_Group, and DNS tables for
- * local datapaths.
- *
- * Monitor Controller_Event rows for local chassis.
- *
- * Monitor IP_Multicast for local datapaths.
- *
- * Monitor IGMP_Groups for local chassis.
- *
- * We always monitor patch ports because they allow us to see the linkages
- * between related logical datapaths. That way, when we know that we have
- * a VIF on a particular logical switch, we immediately know to monitor all
- * the connected logical routers and logical switches. */
- struct ovsdb_idl_condition pb = OVSDB_IDL_CONDITION_INIT(&pb);
- struct ovsdb_idl_condition lf = OVSDB_IDL_CONDITION_INIT(&lf);
- struct ovsdb_idl_condition mb = OVSDB_IDL_CONDITION_INIT(&mb);
- struct ovsdb_idl_condition mg = OVSDB_IDL_CONDITION_INIT(&mg);
- struct ovsdb_idl_condition dns = OVSDB_IDL_CONDITION_INIT(&dns);
- struct ovsdb_idl_condition ce = OVSDB_IDL_CONDITION_INIT(&ce);
- struct ovsdb_idl_condition ip_mcast = OVSDB_IDL_CONDITION_INIT(&ip_mcast);
- struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp);
- sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "patch");
- /* XXX: We can optimize this, if we find a way to only monitor
- * ports that have a Gateway_Chassis that point's to our own
- * chassis */
- sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "chassisredirect");
- sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "external");
- if (chassis) {
- /* This should be mostly redundant with the other clauses for port
- * bindings, but it allows us to catch any ports that are assigned to
- * us but should not be. That way, we can clear their chassis
- * assignments. */
- sbrec_port_binding_add_clause_chassis(&pb, OVSDB_F_EQ,
- &chassis->header_.uuid);
-
- /* Ensure that we find out about l2gateway and l3gateway ports that
- * should be present on this chassis. Otherwise, we might never find
- * out about those ports, if their datapaths don't otherwise have a VIF
- * in this chassis. */
- const char *id = chassis->name;
- const struct smap l2 = SMAP_CONST1(&l2, "l2gateway-chassis", id);
- sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES, &l2);
- const struct smap l3 = SMAP_CONST1(&l3, "l3gateway-chassis", id);
- sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES, &l3);
-
- sbrec_controller_event_add_clause_chassis(&ce, OVSDB_F_EQ,
- &chassis->header_.uuid);
- sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ,
- &chassis->header_.uuid);
- }
- if (local_ifaces) {
- const char *name;
- SSET_FOR_EACH (name, local_ifaces) {
- sbrec_port_binding_add_clause_logical_port(&pb, OVSDB_F_EQ, name);
- sbrec_port_binding_add_clause_parent_port(&pb, OVSDB_F_EQ, name);
- }
- }
- if (local_datapaths) {
- const struct local_datapath *ld;
- HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
- struct uuid *uuid = CONST_CAST(struct uuid *,
- &ld->datapath->header_.uuid);
- sbrec_port_binding_add_clause_datapath(&pb, OVSDB_F_EQ, uuid);
- sbrec_logical_flow_add_clause_logical_datapath(&lf, OVSDB_F_EQ,
- uuid);
- sbrec_mac_binding_add_clause_datapath(&mb, OVSDB_F_EQ, uuid);
- sbrec_multicast_group_add_clause_datapath(&mg, OVSDB_F_EQ, uuid);
- sbrec_dns_add_clause_datapaths(&dns, OVSDB_F_INCLUDES, &uuid, 1);
- sbrec_ip_multicast_add_clause_datapath(&ip_mcast, OVSDB_F_EQ,
- uuid);
- }
- }
- sbrec_port_binding_set_condition(ovnsb_idl, &pb);
- sbrec_logical_flow_set_condition(ovnsb_idl, &lf);
- sbrec_mac_binding_set_condition(ovnsb_idl, &mb);
- sbrec_multicast_group_set_condition(ovnsb_idl, &mg);
- sbrec_dns_set_condition(ovnsb_idl, &dns);
- sbrec_controller_event_set_condition(ovnsb_idl, &ce);
- sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast);
- sbrec_igmp_group_set_condition(ovnsb_idl, &igmp);
- ovsdb_idl_condition_destroy(&pb);
- ovsdb_idl_condition_destroy(&lf);
- ovsdb_idl_condition_destroy(&mb);
- ovsdb_idl_condition_destroy(&mg);
- ovsdb_idl_condition_destroy(&dns);
- ovsdb_idl_condition_destroy(&ce);
- ovsdb_idl_condition_destroy(&ip_mcast);
- ovsdb_idl_condition_destroy(&igmp);
-}
-
-static const char *
-br_int_name(const struct ovsrec_open_vswitch *cfg)
-{
- return smap_get_def(&cfg->external_ids, "ovn-bridge", DEFAULT_BRIDGE_NAME);
-}
-
-static const struct ovsrec_bridge *
-create_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_open_vswitch_table *ovs_table)
-{
- if (!ovs_idl_txn) {
- return NULL;
- }
-
- const struct ovsrec_open_vswitch *cfg;
- cfg = ovsrec_open_vswitch_table_first(ovs_table);
- if (!cfg) {
- return NULL;
- }
- const char *bridge_name = br_int_name(cfg);
-
- ovsdb_idl_txn_add_comment(ovs_idl_txn,
- "ovn-controller: creating integration bridge '%s'", bridge_name);
-
- struct ovsrec_interface *iface;
- iface = ovsrec_interface_insert(ovs_idl_txn);
- ovsrec_interface_set_name(iface, bridge_name);
- ovsrec_interface_set_type(iface, "internal");
-
- struct ovsrec_port *port;
- port = ovsrec_port_insert(ovs_idl_txn);
- ovsrec_port_set_name(port, bridge_name);
- ovsrec_port_set_interfaces(port, &iface, 1);
-
- struct ovsrec_bridge *bridge;
- bridge = ovsrec_bridge_insert(ovs_idl_txn);
- ovsrec_bridge_set_name(bridge, bridge_name);
- ovsrec_bridge_set_fail_mode(bridge, "secure");
- const struct smap oc = SMAP_CONST1(&oc, "disable-in-band", "true");
- ovsrec_bridge_set_other_config(bridge, &oc);
- ovsrec_bridge_set_ports(bridge, &port, 1);
-
- struct ovsrec_bridge **bridges;
- size_t bytes = sizeof *bridges * cfg->n_bridges;
- bridges = xmalloc(bytes + sizeof *bridges);
- memcpy(bridges, cfg->bridges, bytes);
- bridges[cfg->n_bridges] = bridge;
- ovsrec_open_vswitch_verify_bridges(cfg);
- ovsrec_open_vswitch_set_bridges(cfg, bridges, cfg->n_bridges + 1);
- free(bridges);
-
- return bridge;
-}
-
-static const struct ovsrec_bridge *
-get_br_int(const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_open_vswitch_table *ovs_table)
-{
- const struct ovsrec_open_vswitch *cfg;
- cfg = ovsrec_open_vswitch_table_first(ovs_table);
- if (!cfg) {
- return NULL;
- }
-
- return get_bridge(bridge_table, br_int_name(cfg));
-}
-
-static const struct ovsrec_bridge *
-process_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_open_vswitch_table *ovs_table)
-{
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
- ovs_table);
- if (!br_int) {
- br_int = create_br_int(ovs_idl_txn, ovs_table);
- }
- if (br_int && ovs_idl_txn) {
- const struct ovsrec_open_vswitch *cfg;
- cfg = ovsrec_open_vswitch_table_first(ovs_table);
- ovs_assert(cfg);
- const char *datapath_type = smap_get(&cfg->external_ids,
- "ovn-bridge-datapath-type");
- /* Check for the datapath_type and set it only if it is defined in
- * cfg. */
- if (datapath_type && strcmp(br_int->datapath_type, datapath_type)) {
- ovsrec_bridge_set_datapath_type(br_int, datapath_type);
- }
- }
- return br_int;
-}
-
-static const char *
-get_ovs_chassis_id(const struct ovsrec_open_vswitch_table *ovs_table)
-{
- const struct ovsrec_open_vswitch *cfg
- = ovsrec_open_vswitch_table_first(ovs_table);
- const char *chassis_id = cfg ? smap_get(&cfg->external_ids, "system-id")
- : NULL;
-
- if (!chassis_id) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "'system-id' in Open_vSwitch database is missing.");
- }
-
- return chassis_id;
-}
-
-/* Iterate address sets in the southbound database. Create and update the
- * corresponding symtab entries as necessary. */
-static void
-addr_sets_init(const struct sbrec_address_set_table *address_set_table,
- struct shash *addr_sets)
-{
- const struct sbrec_address_set *as;
- SBREC_ADDRESS_SET_TABLE_FOR_EACH (as, address_set_table) {
- expr_const_sets_add(addr_sets, as->name,
- (const char *const *) as->addresses,
- as->n_addresses, true);
- }
-}
-
-static void
-addr_sets_update(const struct sbrec_address_set_table *address_set_table,
- struct shash *addr_sets, struct sset *new,
- struct sset *deleted, struct sset *updated)
-{
- const struct sbrec_address_set *as;
- SBREC_ADDRESS_SET_TABLE_FOR_EACH_TRACKED (as, address_set_table) {
- if (sbrec_address_set_is_deleted(as)) {
- expr_const_sets_remove(addr_sets, as->name);
- sset_add(deleted, as->name);
- } else {
- expr_const_sets_add(addr_sets, as->name,
- (const char *const *) as->addresses,
- as->n_addresses, true);
- if (sbrec_address_set_is_new(as)) {
- sset_add(new, as->name);
- } else {
- sset_add(updated, as->name);
- }
- }
- }
-}
-
-/* Iterate port groups in the southbound database. Create and update the
- * corresponding symtab entries as necessary. */
- static void
-port_groups_init(const struct sbrec_port_group_table *port_group_table,
- struct shash *port_groups)
-{
- const struct sbrec_port_group *pg;
- SBREC_PORT_GROUP_TABLE_FOR_EACH (pg, port_group_table) {
- expr_const_sets_add(port_groups, pg->name,
- (const char *const *) pg->ports,
- pg->n_ports, false);
- }
-}
-
-static void
-port_groups_update(const struct sbrec_port_group_table *port_group_table,
- struct shash *port_groups, struct sset *new,
- struct sset *deleted, struct sset *updated)
-{
- const struct sbrec_port_group *pg;
- SBREC_PORT_GROUP_TABLE_FOR_EACH_TRACKED (pg, port_group_table) {
- if (sbrec_port_group_is_deleted(pg)) {
- expr_const_sets_remove(port_groups, pg->name);
- sset_add(deleted, pg->name);
- } else {
- expr_const_sets_add(port_groups, pg->name,
- (const char *const *) pg->ports,
- pg->n_ports, false);
- if (sbrec_port_group_is_new(pg)) {
- sset_add(new, pg->name);
- } else {
- sset_add(updated, pg->name);
- }
- }
- }
-}
-
-static void
-update_ssl_config(const struct ovsrec_ssl_table *ssl_table)
-{
- const struct ovsrec_ssl *ssl = ovsrec_ssl_table_first(ssl_table);
-
- if (ssl) {
- stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate);
- stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
- }
-}
-
-static int
-get_ofctrl_probe_interval(struct ovsdb_idl *ovs_idl)
-{
- const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
- return smap_get_int(&cfg->external_ids,
- "ovn-openflow-probe-interval",
- OFCTRL_DEFAULT_PROBE_INTERVAL_SEC);
-}
-
-/* Retrieves the pointer to the OVN Southbound database from 'ovs_idl' and
- * updates 'sbdb_idl' with that pointer. */
-static void
-update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl)
-{
- const struct ovsrec_open_vswitch *cfg = ovsrec_open_vswitch_first(ovs_idl);
-
- /* Set remote based on user configuration. */
- const char *remote = NULL;
- if (cfg) {
- remote = smap_get(&cfg->external_ids, "ovn-remote");
- }
- ovsdb_idl_set_remote(ovnsb_idl, remote, true);
-
- /* Set probe interval, based on user configuration and the remote. */
- int default_interval = (remote && !stream_or_pstream_needs_probes(remote)
- ? 0 : DEFAULT_PROBE_INTERVAL_MSEC);
- int interval = smap_get_int(&cfg->external_ids,
- "ovn-remote-probe-interval", default_interval);
- ovsdb_idl_set_probe_interval(ovnsb_idl, interval);
-}
-
-static void
-update_ct_zones(const struct sset *lports, const struct hmap *local_datapaths,
- struct simap *ct_zones, unsigned long *ct_zone_bitmap,
- struct shash *pending_ct_zones)
-{
- struct simap_node *ct_zone, *ct_zone_next;
- int scan_start = 1;
- const char *user;
- struct sset all_users = SSET_INITIALIZER(&all_users);
-
- SSET_FOR_EACH(user, lports) {
- sset_add(&all_users, user);
- }
-
- /* Local patched datapath (gateway routers) need zones assigned. */
- const struct local_datapath *ld;
- HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
- /* XXX Add method to limit zone assignment to logical router
- * datapaths with NAT */
- char *dnat = alloc_nat_zone_key(&ld->datapath->header_.uuid, "dnat");
- char *snat = alloc_nat_zone_key(&ld->datapath->header_.uuid, "snat");
- sset_add(&all_users, dnat);
- sset_add(&all_users, snat);
- free(dnat);
- free(snat);
- }
-
- /* Delete zones that do not exist in above sset. */
- SIMAP_FOR_EACH_SAFE(ct_zone, ct_zone_next, ct_zones) {
- if (!sset_contains(&all_users, ct_zone->name)) {
- VLOG_DBG("removing ct zone %"PRId32" for '%s'",
- ct_zone->data, ct_zone->name);
-
- struct ct_zone_pending_entry *pending = xmalloc(sizeof *pending);
- pending->state = CT_ZONE_DB_QUEUED; /* Skip flushing zone. */
- pending->zone = ct_zone->data;
- pending->add = false;
- shash_add(pending_ct_zones, ct_zone->name, pending);
-
- bitmap_set0(ct_zone_bitmap, ct_zone->data);
- simap_delete(ct_zones, ct_zone);
- }
- }
-
- /* xxx This is wasteful to assign a zone to each port--even if no
- * xxx security policy is applied. */
-
- /* Assign a unique zone id for each logical port and two zones
- * to a gateway router. */
- SSET_FOR_EACH(user, &all_users) {
- int zone;
-
- if (simap_contains(ct_zones, user)) {
- continue;
- }
-
- /* We assume that there are 64K zones and that we own them all. */
- zone = bitmap_scan(ct_zone_bitmap, 0, scan_start, MAX_CT_ZONES + 1);
- if (zone == MAX_CT_ZONES + 1) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "exhausted all ct zones");
- return;
- }
- scan_start = zone + 1;
-
- VLOG_DBG("assigning ct zone %"PRId32" to '%s'", zone, user);
-
- struct ct_zone_pending_entry *pending = xmalloc(sizeof *pending);
- pending->state = CT_ZONE_OF_QUEUED;
- pending->zone = zone;
- pending->add = true;
- shash_add(pending_ct_zones, user, pending);
-
- bitmap_set1(ct_zone_bitmap, zone);
- simap_put(ct_zones, user, zone);
- }
-
- sset_destroy(&all_users);
-}
-
-static void
-commit_ct_zones(const struct ovsrec_bridge *br_int,
- struct shash *pending_ct_zones)
-{
- struct smap new_ids;
- smap_clone(&new_ids, &br_int->external_ids);
-
- bool updated = false;
- struct shash_node *iter;
- SHASH_FOR_EACH(iter, pending_ct_zones) {
- struct ct_zone_pending_entry *ctzpe = iter->data;
-
- /* The transaction is open, so any pending entries in the
- * CT_ZONE_DB_QUEUED must be sent and any in CT_ZONE_DB_QUEUED
- * need to be retried. */
- if (ctzpe->state != CT_ZONE_DB_QUEUED
- && ctzpe->state != CT_ZONE_DB_SENT) {
- continue;
- }
-
- char *user_str = xasprintf("ct-zone-%s", iter->name);
- if (ctzpe->add) {
- char *zone_str = xasprintf("%"PRId32, ctzpe->zone);
- smap_replace(&new_ids, user_str, zone_str);
- free(zone_str);
- } else {
- smap_remove(&new_ids, user_str);
- }
- free(user_str);
-
- ctzpe->state = CT_ZONE_DB_SENT;
- updated = true;
- }
-
- if (updated) {
- ovsrec_bridge_verify_external_ids(br_int);
- ovsrec_bridge_set_external_ids(br_int, &new_ids);
- }
- smap_destroy(&new_ids);
-}
-
-static void
-restore_ct_zones(const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_open_vswitch_table *ovs_table,
- struct simap *ct_zones, unsigned long *ct_zone_bitmap)
-{
- const struct ovsrec_open_vswitch *cfg;
- cfg = ovsrec_open_vswitch_table_first(ovs_table);
- if (!cfg) {
- return;
- }
-
- const struct ovsrec_bridge *br_int;
- br_int = get_bridge(bridge_table, br_int_name(cfg));
- if (!br_int) {
- /* If the integration bridge hasn't been defined, assume that
- * any existing ct-zone definitions aren't valid. */
- return;
- }
-
- struct smap_node *node;
- SMAP_FOR_EACH(node, &br_int->external_ids) {
- if (strncmp(node->key, "ct-zone-", 8)) {
- continue;
- }
-
- const char *user = node->key + 8;
- int zone = atoi(node->value);
-
- if (user[0] && zone) {
- VLOG_DBG("restoring ct zone %"PRId32" for '%s'", zone, user);
- bitmap_set1(ct_zone_bitmap, zone);
- simap_put(ct_zones, user, zone);
- }
- }
-}
-
-static int64_t
-get_nb_cfg(const struct sbrec_sb_global_table *sb_global_table)
-{
- const struct sbrec_sb_global *sb
- = sbrec_sb_global_table_first(sb_global_table);
- return sb ? sb->nb_cfg : 0;
-}
-
-static const char *
-get_transport_zones(const struct ovsrec_open_vswitch_table *ovs_table)
-{
- const struct ovsrec_open_vswitch *cfg
- = ovsrec_open_vswitch_table_first(ovs_table);
- return smap_get_def(&cfg->external_ids, "ovn-transport-zones", "");
-}
-
-static void
-ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
-{
- /* We do not monitor all tables by default, so modules must register
- * their interest explicitly. */
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_fail_mode);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_other_config);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_external_ids);
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_ssl);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_bootstrap_ca_cert);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_ca_cert);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_certificate);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_private_key);
- chassis_register_ovs_idl(ovs_idl);
- encaps_register_ovs_idl(ovs_idl);
- binding_register_ovs_idl(ovs_idl);
- bfd_register_ovs_idl(ovs_idl);
- physical_register_ovs_idl(ovs_idl);
-}
-
-#define SB_NODES \
- SB_NODE(chassis, "chassis") \
- SB_NODE(encap, "encap") \
- SB_NODE(address_set, "address_set") \
- SB_NODE(port_group, "port_group") \
- SB_NODE(multicast_group, "multicast_group") \
- SB_NODE(datapath_binding, "datapath_binding") \
- SB_NODE(port_binding, "port_binding") \
- SB_NODE(mac_binding, "mac_binding") \
- SB_NODE(logical_flow, "logical_flow") \
- SB_NODE(dhcp_options, "dhcp_options") \
- SB_NODE(dhcpv6_options, "dhcpv6_options") \
- SB_NODE(dns, "dns")
-
-enum sb_engine_node {
-#define SB_NODE(NAME, NAME_STR) SB_##NAME,
- SB_NODES
-#undef SB_NODE
-};
-
-#define SB_NODE(NAME, NAME_STR) ENGINE_FUNC_SB(NAME);
- SB_NODES
-#undef SB_NODE
-
-#define OVS_NODES \
- OVS_NODE(open_vswitch, "open_vswitch") \
- OVS_NODE(bridge, "bridge") \
- OVS_NODE(port, "port") \
- OVS_NODE(qos, "qos")
-
-enum ovs_engine_node {
-#define OVS_NODE(NAME, NAME_STR) OVS_##NAME,
- OVS_NODES
-#undef OVS_NODE
-};
-
-#define OVS_NODE(NAME, NAME_STR) ENGINE_FUNC_OVS(NAME);
- OVS_NODES
-#undef OVS_NODE
-
-struct ed_type_ofctrl_is_connected {
- bool connected;
-};
-
-static void
-en_ofctrl_is_connected_init(struct engine_node *node)
-{
- struct ed_type_ofctrl_is_connected *data =
- (struct ed_type_ofctrl_is_connected *)node->data;
- data->connected = false;
-}
-
-static void
-en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED)
-{
-}
-
-static void
-en_ofctrl_is_connected_run(struct engine_node *node)
-{
- struct ed_type_ofctrl_is_connected *data =
- (struct ed_type_ofctrl_is_connected *)node->data;
- if (data->connected != ofctrl_is_connected()) {
- data->connected = !data->connected;
- node->changed = true;
- return;
- }
- node->changed = false;
-}
-
-struct ed_type_addr_sets {
- struct shash addr_sets;
- bool change_tracked;
- struct sset new;
- struct sset deleted;
- struct sset updated;
-};
-
-static void
-en_addr_sets_init(struct engine_node *node)
-{
- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
- shash_init(&as->addr_sets);
- as->change_tracked = false;
- sset_init(&as->new);
- sset_init(&as->deleted);
- sset_init(&as->updated);
-}
-
-static void
-en_addr_sets_cleanup(struct engine_node *node)
-{
- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
- expr_const_sets_destroy(&as->addr_sets);
- shash_destroy(&as->addr_sets);
- sset_destroy(&as->new);
- sset_destroy(&as->deleted);
- sset_destroy(&as->updated);
-}
-
-static void
-en_addr_sets_run(struct engine_node *node)
-{
- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
-
- sset_clear(&as->new);
- sset_clear(&as->deleted);
- sset_clear(&as->updated);
- expr_const_sets_destroy(&as->addr_sets);
-
- struct sbrec_address_set_table *as_table =
- (struct sbrec_address_set_table *)EN_OVSDB_GET(
- engine_get_input("SB_address_set", node));
-
- addr_sets_init(as_table, &as->addr_sets);
-
- as->change_tracked = false;
- node->changed = true;
-}
-
-static bool
-addr_sets_sb_address_set_handler(struct engine_node *node)
-{
- struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
-
- sset_clear(&as->new);
- sset_clear(&as->deleted);
- sset_clear(&as->updated);
-
- struct sbrec_address_set_table *as_table =
- (struct sbrec_address_set_table *)EN_OVSDB_GET(
- engine_get_input("SB_address_set", node));
-
- addr_sets_update(as_table, &as->addr_sets, &as->new,
- &as->deleted, &as->updated);
-
- node->changed = !sset_is_empty(&as->new) || !sset_is_empty(&as->deleted)
- || !sset_is_empty(&as->updated);
-
- as->change_tracked = true;
- node->changed = true;
- return true;
-}
-
-struct ed_type_port_groups{
- struct shash port_groups;
- bool change_tracked;
- struct sset new;
- struct sset deleted;
- struct sset updated;
-};
-
-static void
-en_port_groups_init(struct engine_node *node)
-{
- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
- shash_init(&pg->port_groups);
- pg->change_tracked = false;
- sset_init(&pg->new);
- sset_init(&pg->deleted);
- sset_init(&pg->updated);
-}
-
-static void
-en_port_groups_cleanup(struct engine_node *node)
-{
- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
- expr_const_sets_destroy(&pg->port_groups);
- shash_destroy(&pg->port_groups);
- sset_destroy(&pg->new);
- sset_destroy(&pg->deleted);
- sset_destroy(&pg->updated);
-}
-
-static void
-en_port_groups_run(struct engine_node *node)
-{
- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
-
- sset_clear(&pg->new);
- sset_clear(&pg->deleted);
- sset_clear(&pg->updated);
- expr_const_sets_destroy(&pg->port_groups);
-
- struct sbrec_port_group_table *pg_table =
- (struct sbrec_port_group_table *)EN_OVSDB_GET(
- engine_get_input("SB_port_group", node));
-
- port_groups_init(pg_table, &pg->port_groups);
-
- pg->change_tracked = false;
- node->changed = true;
-}
-
-static bool
-port_groups_sb_port_group_handler(struct engine_node *node)
-{
- struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
-
- sset_clear(&pg->new);
- sset_clear(&pg->deleted);
- sset_clear(&pg->updated);
-
- struct sbrec_port_group_table *pg_table =
- (struct sbrec_port_group_table *)EN_OVSDB_GET(
- engine_get_input("SB_port_group", node));
-
- port_groups_update(pg_table, &pg->port_groups, &pg->new,
- &pg->deleted, &pg->updated);
-
- node->changed = !sset_is_empty(&pg->new) || !sset_is_empty(&pg->deleted)
- || !sset_is_empty(&pg->updated);
-
- pg->change_tracked = true;
- node->changed = true;
- return true;
-}
-
-struct ed_type_runtime_data {
- /* Contains "struct local_datapath" nodes. */
- struct hmap local_datapaths;
-
- /* Contains the name of each logical port resident on the local
- * hypervisor. These logical ports include the VIFs (and their child
- * logical ports, if any) that belong to VMs running on the hypervisor,
- * l2gateway ports for which options:l2gateway-chassis designates the
- * local hypervisor, and localnet ports. */
- struct sset local_lports;
-
- /* Contains the same ports as local_lports, but in the format:
- * <datapath-tunnel-key>_<port-tunnel-key> */
- struct sset local_lport_ids;
- struct sset active_tunnels;
-
- /* connection tracking zones. */
- unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)];
- struct shash pending_ct_zones;
- struct simap ct_zones;
-};
-
-static void
-en_runtime_data_init(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)node->data;
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- hmap_init(&data->local_datapaths);
- sset_init(&data->local_lports);
- sset_init(&data->local_lport_ids);
- sset_init(&data->active_tunnels);
- shash_init(&data->pending_ct_zones);
- simap_init(&data->ct_zones);
-
- /* Initialize connection tracking zones. */
- memset(data->ct_zone_bitmap, 0, sizeof data->ct_zone_bitmap);
- bitmap_set1(data->ct_zone_bitmap, 0); /* Zone 0 is reserved. */
- restore_ct_zones(bridge_table, ovs_table,
- &data->ct_zones, data->ct_zone_bitmap);
-}
-
-static void
-en_runtime_data_cleanup(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)node->data;
-
- sset_destroy(&data->local_lports);
- sset_destroy(&data->local_lport_ids);
- sset_destroy(&data->active_tunnels);
- struct local_datapath *cur_node, *next_node;
- HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
- &data->local_datapaths) {
- free(cur_node->peer_ports);
- hmap_remove(&data->local_datapaths, &cur_node->hmap_node);
- free(cur_node);
- }
- hmap_destroy(&data->local_datapaths);
-
- simap_destroy(&data->ct_zones);
- shash_destroy(&data->pending_ct_zones);
-}
-
-static void
-en_runtime_data_run(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)node->data;
- struct hmap *local_datapaths = &data->local_datapaths;
- struct sset *local_lports = &data->local_lports;
- struct sset *local_lport_ids = &data->local_lport_ids;
- struct sset *active_tunnels = &data->active_tunnels;
- unsigned long *ct_zone_bitmap = data->ct_zone_bitmap;
- struct shash *pending_ct_zones = &data->pending_ct_zones;
- struct simap *ct_zones = &data->ct_zones;
-
- static bool first_run = true;
- if (first_run) {
- /* don't cleanup since there is no data yet */
- first_run = false;
- } else {
- struct local_datapath *cur_node, *next_node;
- HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) {
- free(cur_node->peer_ports);
- hmap_remove(local_datapaths, &cur_node->hmap_node);
- free(cur_node);
- }
- hmap_clear(local_datapaths);
- sset_destroy(local_lports);
- sset_destroy(local_lport_ids);
- sset_destroy(active_tunnels);
- sset_init(local_lports);
- sset_init(local_lport_ids);
- sset_init(active_tunnels);
- }
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const char *chassis_id = get_ovs_chassis_id(ovs_table);
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
-
- ovs_assert(br_int && chassis_id);
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
-
- const struct sbrec_chassis *chassis
- = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- ovs_assert(chassis);
-
- struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected =
- (struct ed_type_ofctrl_is_connected *)engine_get_input(
- "ofctrl_is_connected", node)->data;
- if (ed_ofctrl_is_connected->connected) {
- /* Calculate the active tunnels only if have an an active
- * OpenFlow connection to br-int.
- * If we don't have a connection to br-int, it could mean
- * ovs-vswitchd is down for some reason and the BFD status
- * in the Interface rows could be stale. So its better to
- * consider 'active_tunnels' set to be empty if it's not
- * connected. */
- bfd_calculate_active_tunnels(br_int, active_tunnels);
- }
-
- struct ovsrec_port_table *port_table =
- (struct ovsrec_port_table *)EN_OVSDB_GET(
- engine_get_input("OVS_port", node));
-
- struct ovsrec_qos_table *qos_table =
- (struct ovsrec_qos_table *)EN_OVSDB_GET(
- engine_get_input("OVS_qos", node));
-
- struct sbrec_port_binding_table *pb_table =
- (struct sbrec_port_binding_table *)EN_OVSDB_GET(
- engine_get_input("SB_port_binding", node));
-
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_datapath_binding", node),
- "key");
-
- struct ovsdb_idl_index *sbrec_port_binding_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "name");
-
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "datapath");
-
- binding_run(engine_get_context()->ovnsb_idl_txn,
- engine_get_context()->ovs_idl_txn,
- sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- port_table, qos_table, pb_table,
- br_int, chassis,
- active_tunnels, local_datapaths,
- local_lports, local_lport_ids);
-
- update_ct_zones(local_lports, local_datapaths, ct_zones,
- ct_zone_bitmap, pending_ct_zones);
-
- node->changed = true;
-}
-
-static bool
-runtime_data_sb_port_binding_handler(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)node->data;
- struct sset *local_lports = &data->local_lports;
- struct sset *active_tunnels = &data->active_tunnels;
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const char *chassis_id = chassis_get_id();
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
-
- ovs_assert(br_int && chassis_id);
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
-
- const struct sbrec_chassis *chassis
- = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- ovs_assert(chassis);
-
- struct sbrec_port_binding_table *pb_table =
- (struct sbrec_port_binding_table *)EN_OVSDB_GET(
- engine_get_input("SB_port_binding", node));
-
- bool changed = binding_evaluate_port_binding_changes(
- pb_table, br_int, chassis, active_tunnels, local_lports);
-
- return !changed;
-}
-
-struct ed_type_mff_ovn_geneve {
- enum mf_field_id mff_ovn_geneve;
-};
-
-static void
-en_mff_ovn_geneve_init(struct engine_node *node)
-{
- struct ed_type_mff_ovn_geneve *data =
- (struct ed_type_mff_ovn_geneve *)node->data;
- data->mff_ovn_geneve = 0;
-}
-
-static void
-en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED)
-{
-}
-
-static void
-en_mff_ovn_geneve_run(struct engine_node *node)
-{
- struct ed_type_mff_ovn_geneve *data =
- (struct ed_type_mff_ovn_geneve *)node->data;
- enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id();
- if (data->mff_ovn_geneve != mff_ovn_geneve) {
- data->mff_ovn_geneve = mff_ovn_geneve;
- node->changed = true;
- return;
- }
- node->changed = false;
-}
-
-struct ed_type_flow_output {
- /* desired flows */
- struct ovn_desired_flow_table flow_table;
- /* group ids for load balancing */
- struct ovn_extend_table group_table;
- /* meter ids for QoS */
- struct ovn_extend_table meter_table;
- /* conjunction id offset */
- uint32_t conj_id_ofs;
- /* lflow resource cross reference */
- struct lflow_resource_ref lflow_resource_ref;
-};
-
-static void
-en_flow_output_init(struct engine_node *node)
-{
- struct ed_type_flow_output *data =
- (struct ed_type_flow_output *)node->data;
- ovn_desired_flow_table_init(&data->flow_table);
- ovn_extend_table_init(&data->group_table);
- ovn_extend_table_init(&data->meter_table);
- data->conj_id_ofs = 1;
- lflow_resource_init(&data->lflow_resource_ref);
-}
-
-static void
-en_flow_output_cleanup(struct engine_node *node)
-{
- struct ed_type_flow_output *data =
- (struct ed_type_flow_output *)node->data;
- ovn_desired_flow_table_destroy(&data->flow_table);
- ovn_extend_table_destroy(&data->group_table);
- ovn_extend_table_destroy(&data->meter_table);
- lflow_resource_destroy(&data->lflow_resource_ref);
-}
-
-static void
-en_flow_output_run(struct engine_node *node)
-{
- struct ed_type_runtime_data *rt_data =
- (struct ed_type_runtime_data *)engine_get_input(
- "runtime_data", node)->data;
- struct hmap *local_datapaths = &rt_data->local_datapaths;
- struct sset *local_lports = &rt_data->local_lports;
- struct sset *local_lport_ids = &rt_data->local_lport_ids;
- struct sset *active_tunnels = &rt_data->active_tunnels;
- struct simap *ct_zones = &rt_data->ct_zones;
-
- struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
- (struct ed_type_mff_ovn_geneve *)engine_get_input(
- "mff_ovn_geneve", node)->data;
- enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
- const char *chassis_id = chassis_get_id();
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
- struct ed_type_addr_sets *as_data =
- (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
- struct shash *addr_sets = &as_data->addr_sets;
-
- struct ed_type_port_groups *pg_data =
- (struct ed_type_port_groups *)engine_get_input(
- "port_groups", node)->data;
- struct shash *port_groups = &pg_data->port_groups;
-
- const struct sbrec_chassis *chassis = NULL;
- if (chassis_id) {
- chassis = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- }
-
- ovs_assert(br_int && chassis);
-
- struct ed_type_flow_output *fo =
- (struct ed_type_flow_output *)node->data;
- struct ovn_desired_flow_table *flow_table = &fo->flow_table;
- struct ovn_extend_table *group_table = &fo->group_table;
- struct ovn_extend_table *meter_table = &fo->meter_table;
- uint32_t *conj_id_ofs = &fo->conj_id_ofs;
- struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
-
- static bool first_run = true;
- if (first_run) {
- first_run = false;
- } else {
- ovn_desired_flow_table_clear(flow_table);
- ovn_extend_table_clear(group_table, false /* desired */);
- ovn_extend_table_clear(meter_table, false /* desired */);
- lflow_resource_clear(lfrr);
- }
-
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_multicast_group", node),
- "name_datapath");
-
- struct ovsdb_idl_index *sbrec_port_binding_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "name");
-
- struct sbrec_dhcp_options_table *dhcp_table =
- (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
- engine_get_input("SB_dhcp_options", node));
-
- struct sbrec_dhcpv6_options_table *dhcpv6_table =
- (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
- engine_get_input("SB_dhcpv6_options", node));
-
- struct sbrec_logical_flow_table *logical_flow_table =
- (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
- engine_get_input("SB_logical_flow", node));
-
- struct sbrec_mac_binding_table *mac_binding_table =
- (struct sbrec_mac_binding_table *)EN_OVSDB_GET(
- engine_get_input("SB_mac_binding", node));
-
- *conj_id_ofs = 1;
- lflow_run(sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,
- dhcp_table, dhcpv6_table,
- logical_flow_table,
- mac_binding_table,
- chassis, local_datapaths, addr_sets,
- port_groups, active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table, lfrr,
- conj_id_ofs);
-
- struct sbrec_multicast_group_table *multicast_group_table =
- (struct sbrec_multicast_group_table *)EN_OVSDB_GET(
- engine_get_input("SB_multicast_group", node));
-
- struct sbrec_port_binding_table *port_binding_table =
- (struct sbrec_port_binding_table *)EN_OVSDB_GET(
- engine_get_input("SB_port_binding", node));
-
- physical_run(sbrec_port_binding_by_name,
- multicast_group_table,
- port_binding_table,
- mff_ovn_geneve,
- br_int, chassis, ct_zones,
- local_datapaths, local_lports,
- active_tunnels,
- flow_table);
-
- node->changed = true;
-}
-
-static bool
-flow_output_sb_logical_flow_handler(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)engine_get_input(
- "runtime_data", node)->data;
- struct hmap *local_datapaths = &data->local_datapaths;
- struct sset *local_lport_ids = &data->local_lport_ids;
- struct sset *active_tunnels = &data->active_tunnels;
- struct ed_type_addr_sets *as_data =
- (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
- struct shash *addr_sets = &as_data->addr_sets;
-
- struct ed_type_port_groups *pg_data =
- (struct ed_type_port_groups *)engine_get_input(
- "port_groups", node)->data;
- struct shash *port_groups = &pg_data->port_groups;
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
- const char *chassis_id = chassis_get_id();
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
-
- const struct sbrec_chassis *chassis = NULL;
- if (chassis_id) {
- chassis = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- }
-
- ovs_assert(br_int && chassis);
-
- struct ed_type_flow_output *fo =
- (struct ed_type_flow_output *)node->data;
- struct ovn_desired_flow_table *flow_table = &fo->flow_table;
- struct ovn_extend_table *group_table = &fo->group_table;
- struct ovn_extend_table *meter_table = &fo->meter_table;
- uint32_t *conj_id_ofs = &fo->conj_id_ofs;
- struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
-
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_multicast_group", node),
- "name_datapath");
-
- struct ovsdb_idl_index *sbrec_port_binding_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "name");
-
- struct sbrec_dhcp_options_table *dhcp_table =
- (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
- engine_get_input("SB_dhcp_options", node));
-
- struct sbrec_dhcpv6_options_table *dhcpv6_table =
- (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
- engine_get_input("SB_dhcpv6_options", node));
-
- struct sbrec_logical_flow_table *logical_flow_table =
- (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
- engine_get_input("SB_logical_flow", node));
-
- bool handled = lflow_handle_changed_flows(
- sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,
- dhcp_table, dhcpv6_table,
- logical_flow_table,
- local_datapaths, chassis, addr_sets,
- port_groups, active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table, lfrr,
- conj_id_ofs);
-
- node->changed = true;
- return handled;
-}
-
-static bool
-flow_output_sb_mac_binding_handler(struct engine_node *node)
-{
- struct ovsdb_idl_index *sbrec_port_binding_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "name");
-
- struct sbrec_mac_binding_table *mac_binding_table =
- (struct sbrec_mac_binding_table *)EN_OVSDB_GET(
- engine_get_input("SB_mac_binding", node));
-
- struct ed_type_flow_output *fo =
- (struct ed_type_flow_output *)node->data;
- struct ovn_desired_flow_table *flow_table = &fo->flow_table;
-
- lflow_handle_changed_neighbors(sbrec_port_binding_by_name,
- mac_binding_table, flow_table);
-
- node->changed = true;
- return true;
-}
-
-static bool
-flow_output_sb_port_binding_handler(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)engine_get_input(
- "runtime_data", node)->data;
- struct hmap *local_datapaths = &data->local_datapaths;
- struct sset *active_tunnels = &data->active_tunnels;
- struct simap *ct_zones = &data->ct_zones;
-
- struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
- (struct ed_type_mff_ovn_geneve *)engine_get_input(
- "mff_ovn_geneve", node)->data;
- enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
- const char *chassis_id = chassis_get_id();
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
- const struct sbrec_chassis *chassis = NULL;
- if (chassis_id) {
- chassis = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- }
- ovs_assert(br_int && chassis);
-
- struct ed_type_flow_output *fo =
- (struct ed_type_flow_output *)node->data;
- struct ovn_desired_flow_table *flow_table = &fo->flow_table;
-
- struct ovsdb_idl_index *sbrec_port_binding_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "name");
-
- struct sbrec_port_binding_table *port_binding_table =
- (struct sbrec_port_binding_table *)EN_OVSDB_GET(
- engine_get_input("SB_port_binding", node));
-
- /* XXX: now we handle port-binding changes for physical flow processing
- * only, but port-binding change can have impact to logical flow
- * processing, too, in below circumstances:
- *
- * - When a port-binding for a lport is inserted/deleted but the lflow
- * using that lport doesn't change.
- *
- * This can happen only when the lport name is used by ACL match
- * condition, which is specified by user. Even in that case, if the port
- * is actually bound on the current chassis it will trigger recompute on
- * that chassis since ovs interface would be updated. So the only
- * situation this would have real impact is when user defines an ACL
- * that includes lport that is not on current chassis, and there is a
- * port-binding creation/deletion related to that lport.e.g.: an ACL is
- * defined:
- *
- * to-lport 1000 'outport=="A" && inport=="B"' allow-related
- *
- * If "A" is on current chassis, but "B" is lport that hasn't been
- * created yet. When a lport "B" is created and bound on another
- * chassis, the ACL will not take effect on the current chassis until a
- * recompute is triggered later. This case doesn't seem to be a problem
- * for real world use cases because usually lport is created before
- * being referenced by name in ACLs.
- *
- * - When is_chassis_resident(<lport>) is used in lflow. In this case the
- * port binding is not a regular VIF. It can be either "patch" or
- * "external", with ha-chassis-group assigned. In current
- * "runtime_data" handling, port-binding changes for these types always
- * trigger recomputing. So it is fine even if we do not handle it here.
- * (due to the ovsdb tracking support for referenced table changes,
- * ha-chassis-group changes will appear as port-binding change).
- *
- * - When a mac-binding doesn't change but the port-binding related to
- * that mac-binding is deleted. In this case the neighbor flow generated
- * for the mac-binding should be deleted. This would not cause any real
- * issue for now, since the port-binding related to mac-binding is
- * always logical router port, and any change to logical router port
- * would just trigger recompute.
- *
- * Although there is no correctness issue so far (except the unusual ACL
- * use case, which doesn't seem to be a real problem), it might be better
- * to handle this more gracefully, without the need to consider these
- * tricky scenarios. One approach is to maintain a mapping between lport
- * names and the lflows that uses them, and reprocess the related lflows
- * when related port-bindings change.
- */
- physical_handle_port_binding_changes(
- sbrec_port_binding_by_name,
- port_binding_table, mff_ovn_geneve,
- chassis, ct_zones, local_datapaths,
- active_tunnels, flow_table);
-
- node->changed = true;
- return true;
-}
-
-static bool
-flow_output_sb_multicast_group_handler(struct engine_node *node)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)engine_get_input(
- "runtime_data", node)->data;
- struct hmap *local_datapaths = &data->local_datapaths;
- struct simap *ct_zones = &data->ct_zones;
-
- struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
- (struct ed_type_mff_ovn_geneve *)engine_get_input(
- "mff_ovn_geneve", node)->data;
- enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
- const char *chassis_id = chassis_get_id();
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
- const struct sbrec_chassis *chassis = NULL;
- if (chassis_id) {
- chassis = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- }
- ovs_assert(br_int && chassis);
-
- struct ed_type_flow_output *fo =
- (struct ed_type_flow_output *)node->data;
- struct ovn_desired_flow_table *flow_table = &fo->flow_table;
-
- struct sbrec_multicast_group_table *multicast_group_table =
- (struct sbrec_multicast_group_table *)EN_OVSDB_GET(
- engine_get_input("SB_multicast_group", node));
-
- physical_handle_mc_group_changes(multicast_group_table,
- mff_ovn_geneve, chassis, ct_zones, local_datapaths,
- flow_table);
-
- node->changed = true;
- return true;
-
-}
-
-static bool
-_flow_output_resource_ref_handler(struct engine_node *node,
- enum ref_type ref_type)
-{
- struct ed_type_runtime_data *data =
- (struct ed_type_runtime_data *)engine_get_input(
- "runtime_data", node)->data;
- struct hmap *local_datapaths = &data->local_datapaths;
- struct sset *local_lport_ids = &data->local_lport_ids;
- struct sset *active_tunnels = &data->active_tunnels;
-
- struct ed_type_addr_sets *as_data =
- (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
- struct shash *addr_sets = &as_data->addr_sets;
-
- struct ed_type_port_groups *pg_data =
- (struct ed_type_port_groups *)engine_get_input(
- "port_groups", node)->data;
- struct shash *port_groups = &pg_data->port_groups;
-
- struct ovsrec_open_vswitch_table *ovs_table =
- (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
- engine_get_input("OVS_open_vswitch", node));
- struct ovsrec_bridge_table *bridge_table =
- (struct ovsrec_bridge_table *)EN_OVSDB_GET(
- engine_get_input("OVS_bridge", node));
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table, ovs_table);
- const char *chassis_id = chassis_get_id();
-
- struct ovsdb_idl_index *sbrec_chassis_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_chassis", node),
- "name");
- const struct sbrec_chassis *chassis = NULL;
- if (chassis_id) {
- chassis = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
- }
-
- ovs_assert(br_int && chassis);
-
- struct ed_type_flow_output *fo =
- (struct ed_type_flow_output *)node->data;
- struct ovn_desired_flow_table *flow_table = &fo->flow_table;
- struct ovn_extend_table *group_table = &fo->group_table;
- struct ovn_extend_table *meter_table = &fo->meter_table;
- uint32_t *conj_id_ofs = &fo->conj_id_ofs;
- struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
-
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_multicast_group", node),
- "name_datapath");
-
- struct ovsdb_idl_index *sbrec_port_binding_by_name =
- engine_ovsdb_node_get_index(
- engine_get_input("SB_port_binding", node),
- "name");
-
- struct sbrec_dhcp_options_table *dhcp_table =
- (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
- engine_get_input("SB_dhcp_options", node));
-
- struct sbrec_dhcpv6_options_table *dhcpv6_table =
- (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
- engine_get_input("SB_dhcpv6_options", node));
-
- struct sbrec_logical_flow_table *logical_flow_table =
- (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
- engine_get_input("SB_logical_flow", node));
-
- bool changed;
- const char *ref_name;
- struct sset *new, *updated, *deleted;
-
- switch (ref_type) {
- case REF_TYPE_ADDRSET:
- /* XXX: The change_tracked check may be added to inc-proc
- * framework. */
- if (!as_data->change_tracked) {
- return false;
- }
- new = &as_data->new;
- updated = &as_data->updated;
- deleted = &as_data->deleted;
- break;
- case REF_TYPE_PORTGROUP:
- if (!pg_data->change_tracked) {
- return false;
- }
- new = &pg_data->new;
- updated = &pg_data->updated;
- deleted = &pg_data->deleted;
- break;
- default:
- OVS_NOT_REACHED();
- }
-
-
- SSET_FOR_EACH (ref_name, deleted) {
- if (!lflow_handle_changed_ref(ref_type, ref_name,
- sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,dhcp_table,
- dhcpv6_table, logical_flow_table,
- local_datapaths, chassis, addr_sets,
- port_groups, active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table, lfrr,
- conj_id_ofs, &changed)) {
- return false;
- }
- node->changed = changed || node->changed;
- }
- SSET_FOR_EACH (ref_name, updated) {
- if (!lflow_handle_changed_ref(ref_type, ref_name,
- sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,dhcp_table,
- dhcpv6_table, logical_flow_table,
- local_datapaths, chassis, addr_sets,
- port_groups, active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table, lfrr,
- conj_id_ofs, &changed)) {
- return false;
- }
- node->changed = changed || node->changed;
- }
- SSET_FOR_EACH (ref_name, new) {
- if (!lflow_handle_changed_ref(ref_type, ref_name,
- sbrec_multicast_group_by_name_datapath,
- sbrec_port_binding_by_name,dhcp_table,
- dhcpv6_table, logical_flow_table,
- local_datapaths, chassis, addr_sets,
- port_groups, active_tunnels, local_lport_ids,
- flow_table, group_table, meter_table, lfrr,
- conj_id_ofs, &changed)) {
- return false;
- }
- node->changed = changed || node->changed;
- }
-
- return true;
-}
-
-static bool
-flow_output_addr_sets_handler(struct engine_node *node)
-{
- return _flow_output_resource_ref_handler(node, REF_TYPE_ADDRSET);
-}
-
-static bool
-flow_output_port_groups_handler(struct engine_node *node)
-{
- return _flow_output_resource_ref_handler(node, REF_TYPE_PORTGROUP);
-}
-
-struct ovn_controller_exit_args {
- bool *exiting;
- bool *restart;
-};
-
-int
-main(int argc, char *argv[])
-{
- struct unixctl_server *unixctl;
- bool exiting;
- bool restart;
- struct ovn_controller_exit_args exit_args = {&exiting, &restart};
- int retval;
-
- ovs_cmdl_proctitle_init(argc, argv);
- set_program_name(argv[0]);
- service_start(&argc, &argv);
- char *ovs_remote = parse_options(argc, argv);
- fatal_ignore_sigpipe();
-
- daemonize_start(false);
-
- retval = unixctl_server_create(NULL, &unixctl);
- if (retval) {
- exit(EXIT_FAILURE);
- }
- unixctl_command_register("exit", "", 0, 1, ovn_controller_exit,
- &exit_args);
-
- daemonize_complete();
-
- pinctrl_init();
- lflow_init();
-
- /* Connect to OVS OVSDB instance. */
- struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
- ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
- ctrl_register_ovs_idl(ovs_idl_loop.idl);
- ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl);
-
- /* Configure OVN SB database. */
- struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
- ovsdb_idl_create_unconnected(&sbrec_idl_class, true));
- ovsdb_idl_set_leader_only(ovnsb_idl_loop.idl, false);
-
- unixctl_command_register("connection-status", "", 0, 0,
- ovn_controller_conn_show, ovnsb_idl_loop.idl);
-
- struct ovsdb_idl_index *sbrec_chassis_by_name
- = chassis_index_create(ovnsb_idl_loop.idl);
- struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
- = mcast_group_index_create(ovnsb_idl_loop.idl);
- struct ovsdb_idl_index *sbrec_port_binding_by_name
- = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_logical_port);
- struct ovsdb_idl_index *sbrec_port_binding_by_key
- = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_tunnel_key,
- &sbrec_port_binding_col_datapath);
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath
- = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_datapath);
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key
- = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
- &sbrec_datapath_binding_col_tunnel_key);
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip
- = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
- &sbrec_mac_binding_col_logical_port,
- &sbrec_mac_binding_col_ip);
- struct ovsdb_idl_index *sbrec_ip_multicast
- = ip_mcast_index_create(ovnsb_idl_loop.idl);
- struct ovsdb_idl_index *sbrec_igmp_group
- = igmp_group_index_create(ovnsb_idl_loop.idl);
-
- ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);
- ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
-
- /* Omit the external_ids column of all the tables except for -
- * - DNS. pinctrl.c uses the external_ids column of DNS,
- * which it shouldn't. This should be removed.
- *
- * - Chassis - chassis.c copies the chassis configuration from
- * local open_vswitch table to the external_ids of
- * chassis.
- *
- * - Datapath_binding - lflow.c is using this to check if the datapath
- * is switch or not. This should be removed.
- * */
-
- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_sb_global_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_port_binding_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_ssl_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl,
- &sbrec_gateway_chassis_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_ha_chassis_col_external_ids);
- ovsdb_idl_omit(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_group_col_external_ids);
-
- update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL);
-
- stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
-
- /* Define inc-proc-engine nodes. */
- struct ed_type_runtime_data ed_runtime_data;
- struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve;
- struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected;
- struct ed_type_flow_output ed_flow_output;
- struct ed_type_addr_sets ed_addr_sets;
- struct ed_type_port_groups ed_port_groups;
-
- ENGINE_NODE(runtime_data, "runtime_data");
- ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
- ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
- ENGINE_NODE(flow_output, "flow_output");
- ENGINE_NODE(addr_sets, "addr_sets");
- ENGINE_NODE(port_groups, "port_groups");
-
-#define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
- SB_NODES
-#undef SB_NODE
-
-#define OVS_NODE(NAME, NAME_STR) ENGINE_NODE_OVS(NAME, NAME_STR);
- OVS_NODES
-#undef OVS_NODE
-
- engine_ovsdb_node_add_index(&en_sb_chassis, "name", sbrec_chassis_by_name);
- engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath",
- sbrec_multicast_group_by_name_datapath);
- engine_ovsdb_node_add_index(&en_sb_port_binding, "name",
- sbrec_port_binding_by_name);
- engine_ovsdb_node_add_index(&en_sb_port_binding, "key",
- sbrec_port_binding_by_key);
- engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath",
- sbrec_port_binding_by_datapath);
- engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key",
- sbrec_datapath_binding_by_key);
-
- /* Add dependencies between inc-proc-engine nodes. */
-
- engine_add_input(&en_addr_sets, &en_sb_address_set,
- addr_sets_sb_address_set_handler);
- engine_add_input(&en_port_groups, &en_sb_port_group,
- port_groups_sb_port_group_handler);
-
- engine_add_input(&en_flow_output, &en_addr_sets,
- flow_output_addr_sets_handler);
- engine_add_input(&en_flow_output, &en_port_groups,
- flow_output_port_groups_handler);
- engine_add_input(&en_flow_output, &en_runtime_data, NULL);
- engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
-
- engine_add_input(&en_flow_output, &en_ovs_open_vswitch, NULL);
- engine_add_input(&en_flow_output, &en_ovs_bridge, NULL);
-
- engine_add_input(&en_flow_output, &en_sb_chassis, NULL);
- engine_add_input(&en_flow_output, &en_sb_encap, NULL);
- engine_add_input(&en_flow_output, &en_sb_multicast_group,
- flow_output_sb_multicast_group_handler);
- engine_add_input(&en_flow_output, &en_sb_port_binding,
- flow_output_sb_port_binding_handler);
- engine_add_input(&en_flow_output, &en_sb_mac_binding,
- flow_output_sb_mac_binding_handler);
- engine_add_input(&en_flow_output, &en_sb_logical_flow,
- flow_output_sb_logical_flow_handler);
- engine_add_input(&en_flow_output, &en_sb_dhcp_options, NULL);
- engine_add_input(&en_flow_output, &en_sb_dhcpv6_options, NULL);
- engine_add_input(&en_flow_output, &en_sb_dns, NULL);
-
- engine_add_input(&en_runtime_data, &en_ofctrl_is_connected, NULL);
-
- engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL);
- engine_add_input(&en_runtime_data, &en_ovs_bridge, NULL);
- engine_add_input(&en_runtime_data, &en_ovs_port, NULL);
- engine_add_input(&en_runtime_data, &en_ovs_qos, NULL);
-
- engine_add_input(&en_runtime_data, &en_sb_chassis, NULL);
- engine_add_input(&en_runtime_data, &en_sb_datapath_binding, NULL);
- engine_add_input(&en_runtime_data, &en_sb_port_binding,
- runtime_data_sb_port_binding_handler);
-
- engine_init(&en_flow_output);
-
- ofctrl_init(&ed_flow_output.group_table,
- &ed_flow_output.meter_table,
- get_ofctrl_probe_interval(ovs_idl_loop.idl));
-
- unixctl_command_register("group-table-list", "", 0, 0,
- group_table_list, &ed_flow_output.group_table);
-
- unixctl_command_register("meter-table-list", "", 0, 0,
- meter_table_list, &ed_flow_output.meter_table);
-
- unixctl_command_register("ct-zone-list", "", 0, 0,
- ct_zone_list, &ed_runtime_data.ct_zones);
-
- struct pending_pkt pending_pkt = { .conn = NULL };
- unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
- &pending_pkt);
-
- uint64_t engine_run_id = 0;
- uint64_t old_engine_run_id = 0;
-
- unsigned int ovs_cond_seqno = UINT_MAX;
- unsigned int ovnsb_cond_seqno = UINT_MAX;
-
- /* Main loop. */
- exiting = false;
- restart = false;
- while (!exiting) {
- update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl);
- update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
- ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl));
- old_engine_run_id = engine_run_id;
-
- struct ovsdb_idl_txn *ovs_idl_txn = ovsdb_idl_loop_run(&ovs_idl_loop);
- unsigned int new_ovs_cond_seqno
- = ovsdb_idl_get_condition_seqno(ovs_idl_loop.idl);
- if (new_ovs_cond_seqno != ovs_cond_seqno) {
- if (!new_ovs_cond_seqno) {
- VLOG_INFO("OVS IDL reconnected, force recompute.");
- engine_set_force_recompute(true);
- }
- ovs_cond_seqno = new_ovs_cond_seqno;
- }
-
- struct ovsdb_idl_txn *ovnsb_idl_txn
- = ovsdb_idl_loop_run(&ovnsb_idl_loop);
- unsigned int new_ovnsb_cond_seqno
- = ovsdb_idl_get_condition_seqno(ovnsb_idl_loop.idl);
- if (new_ovnsb_cond_seqno != ovnsb_cond_seqno) {
- if (!new_ovnsb_cond_seqno) {
- VLOG_INFO("OVNSB IDL reconnected, force recompute.");
- engine_set_force_recompute(true);
- }
- ovnsb_cond_seqno = new_ovnsb_cond_seqno;
- }
-
- struct engine_context eng_ctx = {
- .ovs_idl_txn = ovs_idl_txn,
- .ovnsb_idl_txn = ovnsb_idl_txn
- };
-
- engine_set_context(&eng_ctx);
-
- if (ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl)) {
- /* Contains the transport zones that this Chassis belongs to */
- struct sset transport_zones = SSET_INITIALIZER(&transport_zones);
- sset_from_delimited_string(&transport_zones,
- get_transport_zones(ovsrec_open_vswitch_table_get(
- ovs_idl_loop.idl)), ",");
-
- const struct ovsrec_bridge_table *bridge_table =
- ovsrec_bridge_table_get(ovs_idl_loop.idl);
- const struct ovsrec_open_vswitch_table *ovs_table =
- ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
- const struct sbrec_chassis_table *chassis_table =
- sbrec_chassis_table_get(ovnsb_idl_loop.idl);
- const struct ovsrec_bridge *br_int =
- process_br_int(ovs_idl_txn, bridge_table, ovs_table);
- const char *chassis_id = get_ovs_chassis_id(ovs_table);
- const struct sbrec_chassis *chassis = NULL;
- if (chassis_id) {
- chassis = chassis_run(ovnsb_idl_txn, sbrec_chassis_by_name,
- ovs_table, chassis_table, chassis_id,
- br_int, &transport_zones);
- }
-
- if (br_int) {
- ofctrl_run(br_int, &ed_runtime_data.pending_ct_zones);
-
- if (chassis) {
- patch_run(ovs_idl_txn,
- ovsrec_bridge_table_get(ovs_idl_loop.idl),
- ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
- ovsrec_port_table_get(ovs_idl_loop.idl),
- sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
- br_int, chassis);
- encaps_run(ovs_idl_txn,
- bridge_table, br_int,
- sbrec_chassis_table_get(ovnsb_idl_loop.idl),
- chassis_id,
- sbrec_sb_global_first(ovnsb_idl_loop.idl),
- &transport_zones);
-
- stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME,
- time_msec());
- if (ovnsb_idl_txn) {
- engine_run(&en_flow_output, ++engine_run_id);
- }
- stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
- time_msec());
- if (ovs_idl_txn) {
- commit_ct_zones(br_int,
- &ed_runtime_data.pending_ct_zones);
- bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl),
- br_int, chassis,
- sbrec_ha_chassis_group_table_get(
- ovnsb_idl_loop.idl),
- sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
- }
- ofctrl_put(&ed_flow_output.flow_table,
- &ed_runtime_data.pending_ct_zones,
- sbrec_meter_table_get(ovnsb_idl_loop.idl),
- get_nb_cfg(sbrec_sb_global_table_get(
- ovnsb_idl_loop.idl)),
- en_flow_output.changed);
- pinctrl_run(ovnsb_idl_txn,
- sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_key,
- sbrec_port_binding_by_name,
- sbrec_mac_binding_by_lport_ip,
- sbrec_igmp_group,
- sbrec_ip_multicast,
- sbrec_dns_table_get(ovnsb_idl_loop.idl),
- sbrec_controller_event_table_get(
- ovnsb_idl_loop.idl),
- br_int, chassis,
- &ed_runtime_data.local_datapaths,
- &ed_runtime_data.active_tunnels);
-
- if (en_runtime_data.changed) {
- update_sb_monitors(ovnsb_idl_loop.idl, chassis,
- &ed_runtime_data.local_lports,
- &ed_runtime_data.local_datapaths);
- }
- }
-
- }
- if (old_engine_run_id == engine_run_id) {
- if (engine_need_run(&en_flow_output)) {
- VLOG_DBG("engine did not run, force recompute next time: "
- "br_int %p, chassis %p", br_int, chassis);
- engine_set_force_recompute(true);
- poll_immediate_wake();
- } else {
- VLOG_DBG("engine did not run, and it was not needed"
- " either: br_int %p, chassis %p",
- br_int, chassis);
- }
- } else {
- engine_set_force_recompute(false);
- }
-
- if (ovnsb_idl_txn && chassis) {
- int64_t cur_cfg = ofctrl_get_cur_cfg();
- if (cur_cfg && cur_cfg != chassis->nb_cfg) {
- sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
- }
- }
-
-
- if (pending_pkt.conn) {
- if (br_int && chassis) {
- char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s,
- &ed_addr_sets.addr_sets, &ed_port_groups.port_groups);
- if (error) {
- unixctl_command_reply_error(pending_pkt.conn, error);
- free(error);
- } else {
- VLOG_DBG("Pending_pkt conn but br_int %p or chassis "
- "%p not ready. run-id: %"PRIu64, br_int,
- chassis, engine_run_id);
- unixctl_command_reply_error(pending_pkt.conn,
- "ovn-controller not ready.");
- }
- }
- pending_pkt.conn = NULL;
- free(pending_pkt.flow_s);
- }
-
- sset_destroy(&transport_zones);
-
- if (br_int) {
- ofctrl_wait();
- pinctrl_wait(ovnsb_idl_txn);
- }
- }
-
- unixctl_server_run(unixctl);
-
- unixctl_server_wait(unixctl);
- if (exiting || pending_pkt.conn) {
- poll_immediate_wake();
- }
-
- if (!ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop)) {
- VLOG_INFO("OVNSB commit failed, force recompute next time.");
- engine_set_force_recompute(true);
- }
-
- if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) {
- struct shash_node *iter, *iter_next;
- SHASH_FOR_EACH_SAFE (iter, iter_next,
- &ed_runtime_data.pending_ct_zones) {
- struct ct_zone_pending_entry *ctzpe = iter->data;
- if (ctzpe->state == CT_ZONE_DB_SENT) {
- shash_delete(&ed_runtime_data.pending_ct_zones, iter);
- free(ctzpe);
- }
- }
- }
-
- ovsdb_idl_track_clear(ovnsb_idl_loop.idl);
- ovsdb_idl_track_clear(ovs_idl_loop.idl);
- poll_block();
- if (should_service_stop()) {
- exiting = true;
- }
- }
-
- engine_set_context(NULL);
- engine_cleanup(&en_flow_output);
-
- /* It's time to exit. Clean up the databases if we are not restarting */
- if (!restart) {
- bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
- while (!done) {
- update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl);
- update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
-
- struct ovsdb_idl_txn *ovs_idl_txn
- = ovsdb_idl_loop_run(&ovs_idl_loop);
- struct ovsdb_idl_txn *ovnsb_idl_txn
- = ovsdb_idl_loop_run(&ovnsb_idl_loop);
-
- const struct ovsrec_bridge_table *bridge_table
- = ovsrec_bridge_table_get(ovs_idl_loop.idl);
- const struct ovsrec_open_vswitch_table *ovs_table
- = ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
-
- const struct sbrec_port_binding_table *port_binding_table
- = sbrec_port_binding_table_get(ovnsb_idl_loop.idl);
-
- const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
- ovs_table);
- const char *chassis_id = chassis_get_id();
- const struct sbrec_chassis *chassis
- = (chassis_id
- ? chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id)
- : NULL);
-
- /* Run all of the cleanup functions, even if one of them returns
- * false. We're done if all of them return true. */
- done = binding_cleanup(ovnsb_idl_txn, port_binding_table, chassis);
- done = chassis_cleanup(ovnsb_idl_txn, chassis) && done;
- done = encaps_cleanup(ovs_idl_txn, br_int) && done;
- done = igmp_group_cleanup(ovnsb_idl_txn, sbrec_igmp_group) && done;
- if (done) {
- poll_immediate_wake();
- }
-
- ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
- ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
- poll_block();
- }
- }
-
- unixctl_server_destroy(unixctl);
- lflow_destroy();
- ofctrl_destroy();
- pinctrl_destroy();
-
- ovsdb_idl_loop_destroy(&ovs_idl_loop);
- ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
-
- free(ovs_remote);
- service_stop();
-
- exit(retval);
-}
-
-static char *
-parse_options(int argc, char *argv[])
-{
- enum {
- OPT_PEER_CA_CERT = UCHAR_MAX + 1,
- OPT_BOOTSTRAP_CA_CERT,
- VLOG_OPTION_ENUMS,
- DAEMON_OPTION_ENUMS,
- SSL_OPTION_ENUMS,
- };
-
- static struct option long_options[] = {
- {"help", no_argument, NULL, 'h'},
- {"version", no_argument, NULL, 'V'},
- VLOG_LONG_OPTIONS,
- DAEMON_LONG_OPTIONS,
- STREAM_SSL_LONG_OPTIONS,
- {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
- {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
- {NULL, 0, NULL, 0}
- };
- char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
-
- for (;;) {
- int c;
-
- c = getopt_long(argc, argv, short_options, long_options, NULL);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'h':
- usage();
-
- case 'V':
- ovs_print_version(OFP13_VERSION, OFP13_VERSION);
- exit(EXIT_SUCCESS);
-
- VLOG_OPTION_HANDLERS
- DAEMON_OPTION_HANDLERS
- STREAM_SSL_OPTION_HANDLERS
-
- case OPT_PEER_CA_CERT:
- stream_ssl_set_peer_ca_cert_file(optarg);
- break;
-
- case OPT_BOOTSTRAP_CA_CERT:
- stream_ssl_set_ca_cert_file(optarg, true);
- break;
-
- case '?':
- exit(EXIT_FAILURE);
-
- default:
- abort();
- }
- }
- free(short_options);
-
- argc -= optind;
- argv += optind;
-
- char *ovs_remote;
- if (argc == 0) {
- ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
- } else if (argc == 1) {
- ovs_remote = xstrdup(argv[0]);
- } else {
- VLOG_FATAL("exactly zero or one non-option argument required; "
- "use --help for usage");
- }
- return ovs_remote;
-}
-
-static void
-usage(void)
-{
- printf("%s: OVN controller\n"
- "usage %s [OPTIONS] [OVS-DATABASE]\n"
- "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
- program_name, program_name);
- stream_usage("OVS-DATABASE", true, false, true);
- daemon_usage();
- vlog_usage();
- printf("\nOther options:\n"
- " -h, --help display this help message\n"
- " -V, --version display version information\n");
- exit(EXIT_SUCCESS);
-}
-
-static void
-ovn_controller_exit(struct unixctl_conn *conn, int argc,
- const char *argv[], void *exit_args_)
-{
- struct ovn_controller_exit_args *exit_args = exit_args_;
- *exit_args->exiting = true;
- *exit_args->restart = argc == 2 && !strcmp(argv[1], "--restart");
- unixctl_command_reply(conn, NULL);
-}
-
-static void
-ct_zone_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *ct_zones_)
-{
- struct simap *ct_zones = ct_zones_;
- struct ds ds = DS_EMPTY_INITIALIZER;
- struct simap_node *zone;
-
- SIMAP_FOR_EACH(zone, ct_zones) {
- ds_put_format(&ds, "%s %d\n", zone->name, zone->data);
- }
-
- unixctl_command_reply(conn, ds_cstr(&ds));
- ds_destroy(&ds);
-}
-
-static void
-meter_table_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *meter_table_)
-{
- struct ovn_extend_table *meter_table = meter_table_;
- struct ds ds = DS_EMPTY_INITIALIZER;
- struct simap meters = SIMAP_INITIALIZER(&meters);
-
- struct ovn_extend_table_info *m_installed, *next_meter;
- EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_meter, meter_table) {
- simap_put(&meters, m_installed->name, m_installed->table_id);
- }
-
- const struct simap_node **nodes = simap_sort(&meters);
- size_t n_nodes = simap_count(&meters);
- for (size_t i = 0; i < n_nodes; i++) {
- const struct simap_node *node = nodes[i];
- ds_put_format(&ds, "%s: %d\n", node->name, node->data);
- }
-
- free(nodes);
- simap_destroy(&meters);
-
- unixctl_command_reply(conn, ds_cstr(&ds));
- ds_destroy(&ds);
-}
-
-static void
-group_table_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *group_table_)
-{
- struct ovn_extend_table *group_table = group_table_;
- struct ds ds = DS_EMPTY_INITIALIZER;
- struct simap groups = SIMAP_INITIALIZER(&groups);
-
- struct ovn_extend_table_info *m_installed, *next_group;
- EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_group, group_table) {
- simap_put(&groups, m_installed->name, m_installed->table_id);
- }
-
- const struct simap_node **nodes = simap_sort(&groups);
- size_t n_nodes = simap_count(&groups);
- for (size_t i = 0; i < n_nodes; i++) {
- const struct simap_node *node = nodes[i];
- ds_put_format(&ds, "%s: %d\n", node->name, node->data);
- }
-
- free(nodes);
- simap_destroy(&groups);
-
- unixctl_command_reply(conn, ds_cstr(&ds));
- ds_destroy(&ds);
-}
-
-static void
-inject_pkt(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[], void *pending_pkt_)
-{
- struct pending_pkt *pending_pkt = pending_pkt_;
-
- if (pending_pkt->conn) {
- unixctl_command_reply_error(conn, "already pending packet injection");
- return;
- }
- pending_pkt->conn = conn;
- pending_pkt->flow_s = xstrdup(argv[1]);
-}
-
-static void
-ovn_controller_conn_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *idl_)
-{
- const char *result = "not connected";
- const struct ovsdb_idl *idl = idl_;
-
- if (ovsdb_idl_is_connected(idl)) {
- result = "connected";
- }
- unixctl_command_reply(conn, result);
-}
diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h
deleted file mode 100644
index 078c9eabe..000000000
--- a/ovn/controller/ovn-controller.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef OVN_CONTROLLER_H
-#define OVN_CONTROLLER_H 1
-
-#include "simap.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-struct ovsrec_bridge_table;
-
-/* Linux supports a maximum of 64K zones, which seems like a fine default. */
-#define MAX_CT_ZONES 65535
-
-/* States to move through when a new conntrack zone has been allocated. */
-enum ct_zone_pending_state {
- CT_ZONE_OF_QUEUED, /* Waiting to send conntrack flush command. */
- CT_ZONE_OF_SENT, /* Sent and waiting for confirmation on flush. */
- CT_ZONE_DB_QUEUED, /* Waiting for DB transaction to open. */
- CT_ZONE_DB_SENT, /* Sent and waiting for confirmation from DB. */
-};
-
-struct ct_zone_pending_entry {
- int zone;
- bool add; /* Is the entry being added? */
- ovs_be32 of_xid; /* Transaction id for barrier. */
- enum ct_zone_pending_state state;
-};
-
-/* A logical datapath that has some relevance to this hypervisor. A logical
- * datapath D is relevant to hypervisor H if:
- *
- * - Some VIF or l2gateway or l3gateway port in D is located on H.
- *
- * - D is reachable over a series of hops across patch ports, starting from
- * a datapath relevant to H.
- *
- * The 'hmap_node''s hash value is 'datapath->tunnel_key'. */
-struct local_datapath {
- struct hmap_node hmap_node;
- const struct sbrec_datapath_binding *datapath;
-
- /* The localnet port in this datapath, if any (at most one is allowed). */
- const struct sbrec_port_binding *localnet_port;
-
- /* True if this datapath contains an l3gateway port located on this
- * hypervisor. */
- bool has_local_l3gateway;
-
- const struct sbrec_port_binding **peer_ports;
- size_t n_peer_ports;
-};
-
-struct local_datapath *get_local_datapath(const struct hmap *,
- uint32_t tunnel_key);
-
-const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *,
- const char *br_name);
-
-struct sbrec_encap *preferred_encap(const struct sbrec_chassis *);
-
-/* Must be a bit-field ordered from most-preferred (higher number) to
- * least-preferred (lower number). */
-enum chassis_tunnel_type {
- GENEVE = 1 << 2,
- STT = 1 << 1,
- VXLAN = 1 << 0
-};
-
-uint32_t get_tunnel_type(const char *name);
-
-#endif /* ovn/ovn-controller.h */
diff --git a/ovn/controller/patch.c b/ovn/controller/patch.c
deleted file mode 100644
index a6770c6d5..000000000
--- a/ovn/controller/patch.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "patch.h"
-
-#include "hash.h"
-#include "lflow.h"
-#include "lib/vswitch-idl.h"
-#include "lport.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/vlog.h"
-#include "ovn-controller.h"
-
-VLOG_DEFINE_THIS_MODULE(patch);
-
-static char *
-patch_port_name(const char *src, const char *dst)
-{
- return xasprintf("patch-%s-to-%s", src, dst);
-}
-
-/* Return true if 'port' is a patch port with the specified 'peer'. */
-static bool
-match_patch_port(const struct ovsrec_port *port, const char *peer)
-{
- for (size_t i = 0; i < port->n_interfaces; i++) {
- struct ovsrec_interface *iface = port->interfaces[i];
- if (strcmp(iface->type, "patch")) {
- continue;
- }
- const char *iface_peer = smap_get(&iface->options, "peer");
- if (iface_peer && !strcmp(iface_peer, peer)) {
- return true;
- }
- }
- return false;
-}
-
-/* Creates a patch port in bridge 'src' named 'src_name', whose peer is
- * 'dst_name' in bridge 'dst'. Initializes the patch port's external-ids:'key'
- * to 'key'.
- *
- * If such a patch port already exists, removes it from 'existing_ports'. */
-static void
-create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn,
- const char *key, const char *value,
- const struct ovsrec_bridge *src, const char *src_name,
- const struct ovsrec_bridge *dst, const char *dst_name,
- struct shash *existing_ports)
-{
- for (size_t i = 0; i < src->n_ports; i++) {
- if (match_patch_port(src->ports[i], dst_name)) {
- /* Patch port already exists on 'src'. */
- shash_find_and_delete(existing_ports, src->ports[i]->name);
- return;
- }
- }
-
- ovsdb_idl_txn_add_comment(ovs_idl_txn,
- "ovn-controller: creating patch port '%s' from '%s' to '%s'",
- src_name, src->name, dst->name);
-
- struct ovsrec_interface *iface;
- iface = ovsrec_interface_insert(ovs_idl_txn);
- ovsrec_interface_set_name(iface, src_name);
- ovsrec_interface_set_type(iface, "patch");
- const struct smap options = SMAP_CONST1(&options, "peer", dst_name);
- ovsrec_interface_set_options(iface, &options);
-
- struct ovsrec_port *port;
- port = ovsrec_port_insert(ovs_idl_txn);
- ovsrec_port_set_name(port, src_name);
- ovsrec_port_set_interfaces(port, &iface, 1);
- const struct smap ids = SMAP_CONST1(&ids, key, value);
- ovsrec_port_set_external_ids(port, &ids);
-
- struct ovsrec_port **ports;
- ports = xmalloc(sizeof *ports * (src->n_ports + 1));
- memcpy(ports, src->ports, sizeof *ports * src->n_ports);
- ports[src->n_ports] = port;
- ovsrec_bridge_verify_ports(src);
- ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
-
- free(ports);
-}
-
-static void
-remove_port(const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_port *port)
-{
- const struct ovsrec_bridge *bridge;
-
- /* We know the port we want to delete, but we have to find the bridge its
- * on to do so. Note this only runs on a config change that should be
- * pretty rare. */
- OVSREC_BRIDGE_TABLE_FOR_EACH (bridge, bridge_table) {
- size_t i;
- for (i = 0; i < bridge->n_ports; i++) {
- if (bridge->ports[i] != port) {
- continue;
- }
- struct ovsrec_port **new_ports;
- new_ports = xmemdup(bridge->ports,
- sizeof *new_ports * (bridge->n_ports - 1));
- if (i != bridge->n_ports - 1) {
- /* Removed port was not last */
- new_ports[i] = bridge->ports[bridge->n_ports - 1];
- }
- ovsrec_bridge_verify_ports(bridge);
- ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
- free(new_ports);
- ovsrec_port_delete(port);
- return;
- }
- }
-}
-
-/* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for
- * the local bridge mappings. Removes any patch ports for bridge mappings that
- * already existed from 'existing_ports'. */
-static void
-add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_open_vswitch_table *ovs_table,
- const struct sbrec_port_binding_table *port_binding_table,
- const struct ovsrec_bridge *br_int,
- struct shash *existing_ports,
- const struct sbrec_chassis *chassis)
-{
- /* Get ovn-bridge-mappings. */
- const char *mappings_cfg = "";
- const struct ovsrec_open_vswitch *cfg;
- cfg = ovsrec_open_vswitch_table_first(ovs_table);
- if (cfg) {
- mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
- if (!mappings_cfg || !mappings_cfg[0]) {
- return;
- }
- }
-
- /* Parse bridge mappings. */
- struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
- char *cur, *next, *start;
- next = start = xstrdup(mappings_cfg);
- while ((cur = strsep(&next, ",")) && *cur) {
- char *network, *bridge = cur;
- const struct ovsrec_bridge *ovs_bridge;
-
- network = strsep(&bridge, ":");
- if (!bridge || !*network || !*bridge) {
- VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
- mappings_cfg);
- break;
- }
-
- ovs_bridge = get_bridge(bridge_table, bridge);
- if (!ovs_bridge) {
- VLOG_WARN("Bridge '%s' not found for network '%s'",
- bridge, network);
- continue;
- }
-
- shash_add(&bridge_mappings, network, ovs_bridge);
- }
- free(start);
-
- const struct sbrec_port_binding *binding;
- SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
- const char *patch_port_id;
- if (!strcmp(binding->type, "localnet")) {
- patch_port_id = "ovn-localnet-port";
- } else if (!strcmp(binding->type, "l2gateway")) {
- if (!binding->chassis
- || strcmp(chassis->name, binding->chassis->name)) {
- /* This L2 gateway port is not bound to this chassis,
- * so we should not create any patch ports for it. */
- continue;
- }
- patch_port_id = "ovn-l2gateway-port";
- } else {
- /* not a localnet or L2 gateway port. */
- continue;
- }
-
- const char *network = smap_get(&binding->options, "network_name");
- if (!network) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_ERR_RL(&rl, "%s port '%s' has no network name.",
- binding->type, binding->logical_port);
- continue;
- }
- struct ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings, network);
- if (!br_ln) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
- "with network name '%s'",
- binding->type, binding->logical_port, network);
- continue;
- }
-
- char *name1 = patch_port_name(br_int->name, binding->logical_port);
- char *name2 = patch_port_name(binding->logical_port, br_int->name);
- create_patch_port(ovs_idl_txn, patch_port_id, binding->logical_port,
- br_int, name1, br_ln, name2, existing_ports);
- create_patch_port(ovs_idl_txn, patch_port_id, binding->logical_port,
- br_ln, name2, br_int, name1, existing_ports);
- free(name1);
- free(name2);
- }
-
- shash_destroy(&bridge_mappings);
-}
-
-void
-patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge_table *bridge_table,
- const struct ovsrec_open_vswitch_table *ovs_table,
- const struct ovsrec_port_table *port_table,
- const struct sbrec_port_binding_table *port_binding_table,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis)
-{
- if (!ovs_idl_txn) {
- return;
- }
-
- /* Figure out what patch ports already exist.
- *
- * ovn-controller does not create or use ports of type "ovn-l3gateway-port"
- * or "ovn-logical-patch-port", but older version did. We still recognize
- * them here, so that we delete them at the end of this function, to avoid
- * leaving useless ports on upgrade. */
- struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
- const struct ovsrec_port *port;
- OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
- if (smap_get(&port->external_ids, "ovn-localnet-port")
- || smap_get(&port->external_ids, "ovn-l2gateway-port")
- || smap_get(&port->external_ids, "ovn-l3gateway-port")
- || smap_get(&port->external_ids, "ovn-logical-patch-port")) {
- shash_add(&existing_ports, port->name, port);
- }
- }
-
- /* Create in the database any patch ports that should exist. Remove from
- * 'existing_ports' any patch ports that do exist in the database and
- * should be there. */
- add_bridge_mappings(ovs_idl_txn, bridge_table, ovs_table,
- port_binding_table, br_int, &existing_ports, chassis);
-
- /* Now 'existing_ports' only still contains patch ports that exist in the
- * database but shouldn't. Delete them from the database. */
- struct shash_node *port_node, *port_next_node;
- SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
- port = port_node->data;
- shash_delete(&existing_ports, port_node);
- remove_port(bridge_table, port);
- }
- shash_destroy(&existing_ports);
-}
diff --git a/ovn/controller/patch.h b/ovn/controller/patch.h
deleted file mode 100644
index dd052cfd8..000000000
--- a/ovn/controller/patch.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_PATCH_H
-#define OVN_PATCH_H 1
-
-/* Patch Ports
- * ===========
- *
- * This module adds and removes patch ports between the integration bridge and
- * physical bridges, as directed by other-config:ovn-bridge-mappings. */
-
-struct hmap;
-struct ovsdb_idl_txn;
-struct ovsrec_bridge;
-struct ovsrec_bridge_table;
-struct ovsrec_open_vswitch_table;
-struct ovsrec_port_table;
-struct sbrec_port_binding_table;
-struct sbrec_chassis;
-
-void patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
- const struct ovsrec_bridge_table *,
- const struct ovsrec_open_vswitch_table *,
- const struct ovsrec_port_table *,
- const struct sbrec_port_binding_table *,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *);
-
-#endif /* ovn/patch.h */
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
deleted file mode 100644
index 316d3738c..000000000
--- a/ovn/controller/physical.c
+++ /dev/null
@@ -1,1459 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "binding.h"
-#include "byte-order.h"
-#include "encaps.h"
-#include "flow.h"
-#include "ha-chassis.h"
-#include "lflow.h"
-#include "lport.h"
-#include "chassis.h"
-#include "lib/bundle.h"
-#include "openvswitch/poll-loop.h"
-#include "lib/uuid.h"
-#include "ofctrl.h"
-#include "openvswitch/list.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/match.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofpbuf.h"
-#include "openvswitch/vlog.h"
-#include "openvswitch/ofp-parse.h"
-#include "ovn-controller.h"
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn/lib/ovn-util.h"
-#include "physical.h"
-#include "openvswitch/shash.h"
-#include "simap.h"
-#include "smap.h"
-#include "sset.h"
-#include "util.h"
-#include "vswitch-idl.h"
-
-VLOG_DEFINE_THIS_MODULE(physical);
-
-/* UUID to identify OF flows not associated with ovsdb rows. */
-static struct uuid *hc_uuid = NULL;
-
-void
-physical_register_ovs_idl(struct ovsdb_idl *ovs_idl)
-{
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
- ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
-
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
-
- ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
- ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
-}
-
-static struct simap localvif_to_ofport =
- SIMAP_INITIALIZER(&localvif_to_ofport);
-static struct hmap tunnels = HMAP_INITIALIZER(&tunnels);
-
-/* Maps from a chassis to the OpenFlow port number of the tunnel that can be
- * used to reach that chassis. */
-struct chassis_tunnel {
- struct hmap_node hmap_node;
- char *chassis_id;
- ofp_port_t ofport;
- enum chassis_tunnel_type type;
-};
-
-/*
- * This function looks up the list of tunnel ports (provided by
- * ovn-chassis-id ports) and returns the tunnel for the given chassid-id and
- * encap-ip. The ovn-chassis-id is formed using the chassis-id and encap-ip.
- * The list is hashed using the chassis-id. If the encap-ip is not specified,
- * it means we'll just return a tunnel for that chassis-id, i.e. we just check
- * for chassis-id and if there is a match, we'll return the tunnel.
- * If encap-ip is also provided we use both chassis-id and encap-ip to do
- * a more specific lookup.
- */
-static struct chassis_tunnel *
-chassis_tunnel_find(const char *chassis_id, char *encap_ip)
-{
- /*
- * If the specific encap_ip is given, look for the chassisid_ip entry,
- * else return the 1st found entry for the chassis.
- */
- struct chassis_tunnel *tun = NULL;
- HMAP_FOR_EACH_WITH_HASH (tun, hmap_node, hash_string(chassis_id, 0),
- &tunnels) {
- if (encaps_tunnel_id_match(tun->chassis_id, chassis_id, encap_ip)) {
- return tun;
- }
- }
- return NULL;
-}
-
-static void
-put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
- mf_from_id(dst), NULL,
- NULL);
- ovs_be64 n_value = htonll(value);
- bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
- bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
-}
-
-static void
-put_move(enum mf_field_id src, int src_ofs,
- enum mf_field_id dst, int dst_ofs,
- int n_bits,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
- move->src.field = mf_from_id(src);
- move->src.ofs = src_ofs;
- move->src.n_bits = n_bits;
- move->dst.field = mf_from_id(dst);
- move->dst.ofs = dst_ofs;
- move->dst.n_bits = n_bits;
-}
-
-static void
-put_resubmit(uint8_t table_id, struct ofpbuf *ofpacts)
-{
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts);
- resubmit->in_port = OFPP_IN_PORT;
- resubmit->table_id = table_id;
-}
-
-/*
- * For a port binding, get the corresponding ovn-chassis-id tunnel port
- * from the associated encap.
- */
-static struct chassis_tunnel *
-get_port_binding_tun(const struct sbrec_port_binding *binding)
-{
- struct sbrec_encap *encap = binding->encap;
- struct sbrec_chassis *chassis = binding->chassis;
- struct chassis_tunnel *tun = NULL;
-
- if (encap) {
- tun = chassis_tunnel_find(chassis->name, encap->ip);
- }
- if (!tun) {
- tun = chassis_tunnel_find(chassis->name, NULL);
- }
- return tun;
-}
-
-static void
-put_encapsulation(enum mf_field_id mff_ovn_geneve,
- const struct chassis_tunnel *tun,
- const struct sbrec_datapath_binding *datapath,
- uint16_t outport, struct ofpbuf *ofpacts)
-{
- if (tun->type == GENEVE) {
- put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts);
- put_load(outport, mff_ovn_geneve, 0, 32, ofpacts);
- put_move(MFF_LOG_INPORT, 0, mff_ovn_geneve, 16, 15, ofpacts);
- } else if (tun->type == STT) {
- put_load(datapath->tunnel_key | ((uint64_t) outport << 24),
- MFF_TUN_ID, 0, 64, ofpacts);
- put_move(MFF_LOG_INPORT, 0, MFF_TUN_ID, 40, 15, ofpacts);
- } else if (tun->type == VXLAN) {
- put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts);
- } else {
- OVS_NOT_REACHED();
- }
-}
-
-static void
-put_stack(enum mf_field_id field, struct ofpact_stack *stack)
-{
- stack->subfield.field = mf_from_id(field);
- stack->subfield.ofs = 0;
- stack->subfield.n_bits = stack->subfield.field->n_bits;
-}
-
-static const struct sbrec_port_binding *
-get_localnet_port(const struct hmap *local_datapaths, int64_t tunnel_key)
-{
- const struct local_datapath *ld = get_local_datapath(local_datapaths,
- tunnel_key);
- return ld ? ld->localnet_port : NULL;
-}
-
-/* Datapath zone IDs for connection tracking and NAT */
-struct zone_ids {
- int ct; /* MFF_LOG_CT_ZONE. */
- int dnat; /* MFF_LOG_DNAT_ZONE. */
- int snat; /* MFF_LOG_SNAT_ZONE. */
-};
-
-static struct zone_ids
-get_zone_ids(const struct sbrec_port_binding *binding,
- const struct simap *ct_zones)
-{
- struct zone_ids zone_ids;
-
- zone_ids.ct = simap_get(ct_zones, binding->logical_port);
-
- const struct uuid *key = &binding->datapath->header_.uuid;
-
- char *dnat = alloc_nat_zone_key(key, "dnat");
- zone_ids.dnat = simap_get(ct_zones, dnat);
- free(dnat);
-
- char *snat = alloc_nat_zone_key(key, "snat");
- zone_ids.snat = simap_get(ct_zones, snat);
- free(snat);
-
- return zone_ids;
-}
-
-static void
-put_replace_router_port_mac_flows(const struct
- sbrec_port_binding *localnet_port,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- struct ofpbuf *ofpacts_p,
- ofp_port_t ofport,
- struct ovn_desired_flow_table *flow_table)
-{
- struct local_datapath *ld = get_local_datapath(local_datapaths,
- localnet_port->datapath->
- tunnel_key);
- ovs_assert(ld);
-
- uint32_t dp_key = localnet_port->datapath->tunnel_key;
- uint32_t port_key = localnet_port->tunnel_key;
- int tag = localnet_port->tag ? *localnet_port->tag : 0;
- const char *network = smap_get(&localnet_port->options, "network_name");
- struct eth_addr chassis_mac;
-
- if (!network) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Physical network not configured for datapath:"
- "%"PRId64" with localnet port",
- localnet_port->datapath->tunnel_key);
- return;
- }
-
- /* Get chassis mac */
- if (!chassis_get_mac(chassis, network, &chassis_mac)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- /* Keeping the log level low for backward compatibility.
- * Chassis mac is a new configuration.
- */
- VLOG_DBG_RL(&rl, "Could not get chassis mac for network: %s", network);
- return;
- }
-
- for (int i = 0; i < ld->n_peer_ports; i++) {
- const struct sbrec_port_binding *rport_binding = ld->peer_ports[i];
- struct eth_addr router_port_mac;
- struct match match;
- struct ofpact_mac *replace_mac;
-
- /* Table 65, priority 150.
- * =======================
- *
- * Implements output to localnet port.
- * a. Flow replaces ingress router port mac with a chassis mac.
- * b. Flow appends the vlan id localnet port is configured with.
- */
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
-
- ovs_assert(rport_binding->n_mac == 1);
- char *err_str = str_to_mac(rport_binding->mac[0], &router_port_mac);
- if (err_str) {
- /* Parsing of mac failed. */
- VLOG_WARN("Parsing or router port mac failed for router port: %s, "
- "with error: %s", rport_binding->logical_port, err_str);
- free(err_str);
- return;
- }
-
- /* Replace Router mac flow */
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
- match_set_dl_src(&match, router_port_mac);
-
- replace_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
- replace_mac->mac = chassis_mac;
-
- if (tag) {
- struct ofpact_vlan_vid *vlan_vid;
- vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p);
- vlan_vid->vlan_vid = tag;
- vlan_vid->push_vlan_if_needed = true;
- }
-
- ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
-
- ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 150, 0,
- &match, ofpacts_p, &localnet_port->header_.uuid);
- }
-}
-
-static void
-put_local_common_flows(uint32_t dp_key, uint32_t port_key,
- uint32_t parent_port_key,
- const struct zone_ids *zone_ids,
- struct ofpbuf *ofpacts_p,
- struct ovn_desired_flow_table *flow_table)
-{
- struct match match;
-
- /* Table 33, priority 100.
- * =======================
- *
- * Implements output to local hypervisor. Each flow matches a
- * logical output port on the local hypervisor, and resubmits to
- * table 34.
- */
-
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
-
- /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-
- if (zone_ids) {
- if (zone_ids->ct) {
- put_load(zone_ids->ct, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
- }
- if (zone_ids->dnat) {
- put_load(zone_ids->dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
- }
- if (zone_ids->snat) {
- put_load(zone_ids->snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
- }
- }
-
- /* Resubmit to table 34. */
- put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
- &match, ofpacts_p, hc_uuid);
-
- /* Table 34, Priority 100.
- * =======================
- *
- * Drop packets whose logical inport and outport are the same
- * and the MLF_ALLOW_LOOPBACK flag is not set. */
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
- 0, MLF_ALLOW_LOOPBACK);
- match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key);
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
- ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100, 0,
- &match, ofpacts_p, hc_uuid);
-
- /* Table 64, Priority 100.
- * =======================
- *
- * If the packet is supposed to hair-pin because the
- * - "loopback" flag is set
- * - or if the destination is a nested container
- * - or if "nested_container" flag is set and the destination is the
- * parent port,
- * temporarily set the in_port to zero, resubmit to
- * table 65 for logical-to-physical translation, then restore
- * the port number.
- *
- * If 'parent_port_key' is set, then the 'port_key' represents a nested
- * container. */
-
- bool nested_container = parent_port_key ? true: false;
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
- if (!nested_container) {
- match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
- MLF_ALLOW_LOOPBACK, MLF_ALLOW_LOOPBACK);
- }
-
- put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p));
- put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
- put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
- put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
- ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
- &match, ofpacts_p, hc_uuid);
-
- if (nested_container) {
- /* It's a nested container and when the packet from the nested
- * container is to be sent to the parent port, "nested_container"
- * flag will be set. We need to temporarily set the in_port to zero
- * as mentioned in the comment above.
- *
- * If a parent port has multiple child ports, then this if condition
- * will be hit multiple times, but we want to add only one flow.
- * ofctrl_add_flow() logs a warning message for duplicate flows.
- * So use the function 'ofctrl_check_and_add_flow' which doesn't
- * log a warning.
- *
- * Other option is to add this flow for all the ports which are not
- * nested containers. In which case we will add this flow for all the
- * ports even if they don't have any child ports which is
- * unnecessary.
- */
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, parent_port_key);
- match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
- MLF_NESTED_CONTAINER, MLF_NESTED_CONTAINER);
-
- put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p));
- put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
- put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
- put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
- ofctrl_check_and_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
- &match, ofpacts_p, hc_uuid, false);
- }
-}
-
-static void
-load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
- const struct zone_ids *zone_ids,
- struct ofpbuf *ofpacts_p)
-{
- if (zone_ids) {
- if (zone_ids->ct) {
- put_load(zone_ids->ct, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
- }
- if (zone_ids->dnat) {
- put_load(zone_ids->dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
- }
- if (zone_ids->snat) {
- put_load(zone_ids->snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
- }
- }
-
- /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
- uint32_t dp_key = binding->datapath->tunnel_key;
- uint32_t port_key = binding->tunnel_key;
- put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, ofpacts_p);
- put_load(port_key, MFF_LOG_INPORT, 0, 32, ofpacts_p);
-}
-
-static void
-consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- enum mf_field_id mff_ovn_geneve,
- const struct simap *ct_zones,
- const struct sset *active_tunnels,
- const struct hmap *local_datapaths,
- const struct sbrec_port_binding *binding,
- const struct sbrec_chassis *chassis,
- struct ovn_desired_flow_table *flow_table,
- struct ofpbuf *ofpacts_p)
-{
- uint32_t dp_key = binding->datapath->tunnel_key;
- uint32_t port_key = binding->tunnel_key;
- if (!get_local_datapath(local_datapaths, dp_key)) {
- return;
- }
-
- struct match match;
- if (!strcmp(binding->type, "patch")
- || (!strcmp(binding->type, "l3gateway")
- && binding->chassis == chassis)) {
- const char *peer_name = smap_get(&binding->options, "peer");
- if (!peer_name) {
- return;
- }
-
- const struct sbrec_port_binding *peer = lport_lookup_by_name(
- sbrec_port_binding_by_name, peer_name);
- if (!peer || strcmp(peer->type, binding->type)) {
- return;
- }
- const char *peer_peer_name = smap_get(&peer->options, "peer");
- if (!peer_peer_name || strcmp(peer_peer_name, binding->logical_port)) {
- return;
- }
-
- struct zone_ids binding_zones = get_zone_ids(binding, ct_zones);
- put_local_common_flows(dp_key, port_key, 0, &binding_zones,
- ofpacts_p, flow_table);
-
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-
- size_t clone_ofs = ofpacts_p->size;
- struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts_p);
- ofpact_put_CT_CLEAR(ofpacts_p);
- put_load(0, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
- put_load(0, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
- put_load(0, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
- struct zone_ids peer_zones = get_zone_ids(peer, ct_zones);
- load_logical_ingress_metadata(peer, &peer_zones, ofpacts_p);
- put_load(0, MFF_LOG_FLAGS, 0, 32, ofpacts_p);
- put_load(0, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
- for (int i = 0; i < MFF_N_LOG_REGS; i++) {
- put_load(0, MFF_LOG_REG0 + i, 0, 32, ofpacts_p);
- }
- put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
- put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
- clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof *clone);
- ofpacts_p->header = clone;
- ofpact_finish_CLONE(ofpacts_p, &clone);
-
- ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
- &match, ofpacts_p, &binding->header_.uuid);
- return;
- }
-
- struct ha_chassis_ordered *ha_ch_ordered
- = ha_chassis_get_ordered(binding->ha_chassis_group);
-
- if (!strcmp(binding->type, "chassisredirect")
- && (binding->chassis == chassis
- || ha_chassis_group_is_active(binding->ha_chassis_group,
- active_tunnels, chassis))) {
-
- /* Table 33, priority 100.
- * =======================
- *
- * Implements output to local hypervisor. Each flow matches a
- * logical output port on the local hypervisor, and resubmits to
- * table 34. For ports of type "chassisredirect", the logical
- * output port is changed from the "chassisredirect" port to the
- * underlying distributed port. */
-
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-
- const char *distributed_port = smap_get_def(&binding->options,
- "distributed-port", "");
- const struct sbrec_port_binding *distributed_binding
- = lport_lookup_by_name(sbrec_port_binding_by_name,
- distributed_port);
-
- if (!distributed_binding) {
- /* Packet will be dropped. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "No port binding record for distributed "
- "port %s referred by chassisredirect port %s",
- distributed_port,
- binding->logical_port);
- } else if (binding->datapath !=
- distributed_binding->datapath) {
- /* Packet will be dropped. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl,
- "chassisredirect port %s refers to "
- "distributed port %s in wrong datapath",
- binding->logical_port,
- distributed_port);
- } else {
- put_load(distributed_binding->tunnel_key,
- MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
-
- struct zone_ids zone_ids = get_zone_ids(distributed_binding,
- ct_zones);
- if (zone_ids.ct) {
- put_load(zone_ids.ct, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
- }
- if (zone_ids.dnat) {
- put_load(zone_ids.dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
- }
- if (zone_ids.snat) {
- put_load(zone_ids.snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
- }
-
- /* Resubmit to table 34. */
- put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
- }
-
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
- &match, ofpacts_p, &binding->header_.uuid);
-
- goto out;
- }
-
- /* Find the OpenFlow port for the logical port, as 'ofport'. This is
- * one of:
- *
- * - If the port is a VIF on the chassis we're managing, the
- * OpenFlow port for the VIF. 'tun' will be NULL.
- *
- * The same logic handles ports that OVN implements as Open vSwitch
- * patch ports, that is, "localnet" and "l2gateway" ports.
- *
- * For a container nested inside a VM and accessible via a VLAN,
- * 'tag' is the VLAN ID; otherwise 'tag' is 0.
- *
- * For a localnet or l2gateway patch port, if a VLAN ID was
- * configured, 'tag' is set to that VLAN ID; otherwise 'tag' is 0.
- *
- * - If the port is on a remote chassis, the OpenFlow port for a
- * tunnel to the VIF's remote chassis. 'tun' identifies that
- * tunnel.
- */
-
- int tag = 0;
- bool nested_container = false;
- const struct sbrec_port_binding *parent_port = NULL;
- ofp_port_t ofport;
- bool is_remote = false;
- if (binding->parent_port && *binding->parent_port) {
- if (!binding->tag) {
- goto out;
- }
- ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
- binding->parent_port));
- if (ofport) {
- tag = *binding->tag;
- nested_container = true;
- parent_port = lport_lookup_by_name(
- sbrec_port_binding_by_name, binding->parent_port);
- }
- } else {
- ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
- binding->logical_port));
- const char *requested_chassis = smap_get(&binding->options,
- "requested-chassis");
- if (ofport && requested_chassis && requested_chassis[0] &&
- strcmp(requested_chassis, chassis->name) &&
- strcmp(requested_chassis, chassis->hostname)) {
- /* Even though there is an ofport for this port_binding, it is
- * requested on a different chassis. So ignore this ofport.
- */
- ofport = 0;
- }
-
- if ((!strcmp(binding->type, "localnet")
- || !strcmp(binding->type, "l2gateway"))
- && ofport && binding->tag) {
- tag = *binding->tag;
- }
- }
-
- bool is_ha_remote = false;
- const struct chassis_tunnel *tun = NULL;
- const struct sbrec_port_binding *localnet_port =
- get_localnet_port(local_datapaths, dp_key);
- if (!ofport) {
- /* It is remote port, may be reached by tunnel or localnet port */
- is_remote = true;
- if (localnet_port) {
- ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
- localnet_port->logical_port));
- if (!ofport) {
- goto out;
- }
- } else {
- if (!ha_ch_ordered || ha_ch_ordered->n_ha_ch < 2) {
- /* It's on a single remote chassis */
- if (!binding->chassis) {
- goto out;
- }
- tun = chassis_tunnel_find(binding->chassis->name, NULL);
- if (!tun) {
- goto out;
- }
- ofport = tun->ofport;
- } else {
- /* It's distributed across the chassis belonging to
- * an HA chassis group. */
- is_ha_remote = true;
- }
- }
- }
-
- if (!is_remote) {
- /* Packets that arrive from a vif can belong to a VM or
- * to a container located inside that VM. Packets that
- * arrive from containers have a tag (vlan) associated with them.
- */
-
- struct zone_ids zone_ids = get_zone_ids(binding, ct_zones);
- uint32_t parent_port_key = parent_port ? parent_port->tunnel_key : 0;
- /* Pass the parent port tunnel key if the port is a nested
- * container. */
- put_local_common_flows(dp_key, port_key, parent_port_key, &zone_ids,
- ofpacts_p, flow_table);
-
- /* Table 0, Priority 150 and 100.
- * ==============================
- *
- * Priority 150 is for tagged traffic. This may be containers in a
- * VM or a VLAN on a local network. For such traffic, match on the
- * tags and then strip the tag.
- *
- * Priority 100 is for traffic belonging to VMs or untagged locally
- * connected networks.
- *
- * For both types of traffic: set MFF_LOG_INPORT to the logical
- * input port, MFF_LOG_DATAPATH to the logical datapath, and
- * resubmit into the logical ingress pipeline starting at table
- * 16. */
- ofpbuf_clear(ofpacts_p);
- match_init_catchall(&match);
- match_set_in_port(&match, ofport);
-
- /* Match a VLAN tag and strip it, including stripping priority tags
- * (e.g. VLAN ID 0). In the latter case we'll add a second flow
- * for frames that lack any 802.1Q header later. */
- if (tag || !strcmp(binding->type, "localnet")
- || !strcmp(binding->type, "l2gateway")) {
- match_set_dl_vlan(&match, htons(tag), 0);
- if (nested_container) {
- /* When a packet comes from a container sitting behind a
- * parent_port, we should let it loopback to other containers
- * or the parent_port itself. Indicate this by setting the
- * MLF_NESTED_CONTAINER_BIT in MFF_LOG_FLAGS.*/
- put_load(1, MFF_LOG_FLAGS, MLF_NESTED_CONTAINER_BIT, 1,
- ofpacts_p);
- }
- ofpact_put_STRIP_VLAN(ofpacts_p);
- }
-
- /* Remember the size with just strip vlan added so far,
- * as we're going to remove this with ofpbuf_pull() later. */
- uint32_t ofpacts_orig_size = ofpacts_p->size;
-
- load_logical_ingress_metadata(binding, &zone_ids, ofpacts_p);
-
- /* Resubmit to first logical ingress pipeline table. */
- put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
- tag ? 150 : 100, 0, &match, ofpacts_p,
- &binding->header_.uuid);
-
- if (!tag && (!strcmp(binding->type, "localnet")
- || !strcmp(binding->type, "l2gateway"))) {
-
- /* Add a second flow for frames that lack any 802.1Q
- * header. For these, drop the OFPACT_STRIP_VLAN
- * action. */
- ofpbuf_pull(ofpacts_p, ofpacts_orig_size);
- match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
- ofctrl_add_flow(flow_table, 0, 100, 0, &match, ofpacts_p,
- &binding->header_.uuid);
- }
-
- /* Table 65, Priority 100.
- * =======================
- *
- * Deliver the packet to the local vif. */
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
- if (tag) {
- /* For containers sitting behind a local vif, tag the packets
- * before delivering them. */
- struct ofpact_vlan_vid *vlan_vid;
- vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p);
- vlan_vid->vlan_vid = tag;
- vlan_vid->push_vlan_if_needed = true;
- }
- ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
- if (tag) {
- /* Revert the tag added to the packets headed to containers
- * in the previous step. If we don't do this, the packets
- * that are to be broadcasted to a VM in the same logical
- * switch will also contain the tag. */
- ofpact_put_STRIP_VLAN(ofpacts_p);
- }
- ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
- &match, ofpacts_p, &binding->header_.uuid);
-
- if (!strcmp(binding->type, "localnet")) {
- put_replace_router_port_mac_flows(binding, chassis,
- local_datapaths, ofpacts_p,
- ofport, flow_table);
- }
-
- } else if (!tun && !is_ha_remote) {
- /* Remote port connected by localnet port */
- /* Table 33, priority 100.
- * =======================
- *
- * Implements switching to localnet port. Each flow matches a
- * logical output port on remote hypervisor, switch the output port
- * to connected localnet port and resubmits to same table.
- */
-
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
-
- /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-
- put_load(localnet_port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
-
- /* Resubmit to table 33. */
- put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
- &match, ofpacts_p, &binding->header_.uuid);
- } else {
- /* Remote port connected by tunnel */
-
- /* Table 32, priority 100.
- * =======================
- *
- * Handles traffic that needs to be sent to a remote hypervisor. Each
- * flow matches an output port that includes a logical port on a remote
- * hypervisor, and tunnels the packet to that hypervisor.
- */
- match_init_catchall(&match);
- ofpbuf_clear(ofpacts_p);
-
- /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
-
- if (!is_ha_remote) {
- /* Setup encapsulation */
- const struct chassis_tunnel *rem_tun =
- get_port_binding_tun(binding);
- if (!rem_tun) {
- goto out;
- }
- put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
- port_key, ofpacts_p);
- /* Output to tunnel. */
- ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport;
- } else {
- /* Make sure all tunnel endpoints use the same encapsulation,
- * and set it up */
- for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
- const struct sbrec_chassis *ch =
- ha_ch_ordered->ha_ch[i].chassis;
- if (!ch) {
- continue;
- }
- if (!tun) {
- tun = chassis_tunnel_find(ch->name, NULL);
- } else {
- struct chassis_tunnel *chassis_tunnel =
- chassis_tunnel_find(ch->name, NULL);
- if (chassis_tunnel &&
- tun->type != chassis_tunnel->type) {
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis "
- "with mixed encapsulations, only "
- "uniform encapsulations are "
- "supported.",
- binding->logical_port);
- goto out;
- }
- }
- }
- if (!tun) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis in "
- "HA chassis group of port %s",
- binding->logical_port);
- goto out;
- }
-
- put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
- port_key, ofpacts_p);
-
- /* Output to tunnels with active/backup */
- struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p);
-
- for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
- const struct sbrec_chassis *ch =
- ha_ch_ordered->ha_ch[i].chassis;
- if (!ch) {
- continue;
- }
- tun = chassis_tunnel_find(ch->name, NULL);
- if (!tun) {
- continue;
- }
- if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Remote endpoints for port beyond "
- "BUNDLE_MAX_SLAVES");
- break;
- }
- ofpbuf_put(ofpacts_p, &tun->ofport,
- sizeof tun->ofport);
- bundle = ofpacts_p->header;
- bundle->n_slaves++;
- }
-
- bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
- /* Although ACTIVE_BACKUP bundle algorithm seems to ignore
- * the next two fields, those are always set */
- bundle->basis = 0;
- bundle->fields = NX_HASH_FIELDS_ETH_SRC;
- ofpact_finish_BUNDLE(ofpacts_p, &bundle);
- }
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
- &match, ofpacts_p, &binding->header_.uuid);
- }
-out:
- if (ha_ch_ordered) {
- ha_chassis_destroy_ordered(ha_ch_ordered);
- }
-}
-
-static void
-consider_mc_group(enum mf_field_id mff_ovn_geneve,
- const struct simap *ct_zones,
- const struct hmap *local_datapaths,
- const struct sbrec_chassis *chassis,
- const struct sbrec_multicast_group *mc,
- struct ovn_desired_flow_table *flow_table)
-{
- uint32_t dp_key = mc->datapath->tunnel_key;
- if (!get_local_datapath(local_datapaths, dp_key)) {
- return;
- }
-
- struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis);
- struct match match;
-
- match_init_catchall(&match);
- match_set_metadata(&match, htonll(dp_key));
- match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, mc->tunnel_key);
-
- /* Go through all of the ports in the multicast group:
- *
- * - For remote ports, add the chassis to 'remote_chassis'.
- *
- * - For local ports (other than logical patch ports), add actions
- * to 'ofpacts' to set the output port and resubmit.
- *
- * - For logical patch ports, add actions to 'remote_ofpacts'
- * instead. (If we put them in 'ofpacts', then the output
- * would happen on every hypervisor in the multicast group,
- * effectively duplicating the packet.)
- */
- struct ofpbuf ofpacts;
- ofpbuf_init(&ofpacts, 0);
- struct ofpbuf remote_ofpacts;
- ofpbuf_init(&remote_ofpacts, 0);
- for (size_t i = 0; i < mc->n_ports; i++) {
- struct sbrec_port_binding *port = mc->ports[i];
-
- if (port->datapath != mc->datapath) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, UUID_FMT": multicast group contains ports "
- "in wrong datapath",
- UUID_ARGS(&mc->header_.uuid));
- continue;
- }
-
- int zone_id = simap_get(ct_zones, port->logical_port);
- if (zone_id) {
- put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts);
- }
-
- if (!strcmp(port->type, "patch")) {
- put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32,
- &remote_ofpacts);
- put_resubmit(OFTABLE_CHECK_LOOPBACK, &remote_ofpacts);
- } else if (simap_contains(&localvif_to_ofport,
- (port->parent_port && *port->parent_port)
- ? port->parent_port : port->logical_port)
- || (!strcmp(port->type, "l3gateway")
- && port->chassis == chassis)) {
- put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
- put_resubmit(OFTABLE_CHECK_LOOPBACK, &ofpacts);
- } else if (port->chassis && !get_localnet_port(local_datapaths,
- mc->datapath->tunnel_key)) {
- /* Add remote chassis only when localnet port not exist,
- * otherwise multicast will reach remote ports through localnet
- * port. */
- sset_add(&remote_chassis, port->chassis->name);
- }
- }
-
- /* Table 33, priority 100.
- * =======================
- *
- * Handle output to the local logical ports in the multicast group, if
- * any. */
- bool local_ports = ofpacts.size > 0;
- if (local_ports) {
- /* Following delivery to local logical ports, restore the multicast
- * group as the logical output port. */
- put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
-
- ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
- &match, &ofpacts, &mc->header_.uuid);
- }
-
- /* Table 32, priority 100.
- * =======================
- *
- * Handle output to the remote chassis in the multicast group, if
- * any. */
- if (!sset_is_empty(&remote_chassis) || remote_ofpacts.size > 0) {
- if (remote_ofpacts.size > 0) {
- /* Following delivery to logical patch ports, restore the
- * multicast group as the logical output port. */
- put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32,
- &remote_ofpacts);
- }
-
- const char *chassis_name;
- const struct chassis_tunnel *prev = NULL;
- SSET_FOR_EACH (chassis_name, &remote_chassis) {
- const struct chassis_tunnel *tun
- = chassis_tunnel_find(chassis_name, NULL);
- if (!tun) {
- continue;
- }
-
- if (!prev || tun->type != prev->type) {
- put_encapsulation(mff_ovn_geneve, tun, mc->datapath,
- mc->tunnel_key, &remote_ofpacts);
- prev = tun;
- }
- ofpact_put_OUTPUT(&remote_ofpacts)->port = tun->ofport;
- }
-
- if (remote_ofpacts.size) {
- if (local_ports) {
- put_resubmit(OFTABLE_LOCAL_OUTPUT, &remote_ofpacts);
- }
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
- &match, &remote_ofpacts, &mc->header_.uuid);
- }
- }
- ofpbuf_uninit(&ofpacts);
- ofpbuf_uninit(&remote_ofpacts);
- sset_destroy(&remote_chassis);
-}
-
-/* Replaces 'old' by 'new' (destroying 'new'). Returns true if 'old' and 'new'
- * contained different data, false if they were the same. */
-static bool
-update_ofports(struct simap *old, struct simap *new)
-{
- bool changed = !simap_equal(old, new);
- simap_swap(old, new);
- simap_destroy(new);
- return changed;
-}
-
-void physical_handle_port_binding_changes(
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_port_binding_table *pb_table,
- enum mf_field_id mff_ovn_geneve,
- const struct sbrec_chassis *chassis,
- const struct simap *ct_zones,
- struct hmap *local_datapaths,
- struct sset *active_tunnels,
- struct ovn_desired_flow_table *flow_table)
-{
- const struct sbrec_port_binding *binding;
- struct ofpbuf ofpacts;
- ofpbuf_init(&ofpacts, 0);
- SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding, pb_table) {
- if (sbrec_port_binding_is_deleted(binding)) {
- ofctrl_remove_flows(flow_table, &binding->header_.uuid);
- } else {
- if (!sbrec_port_binding_is_new(binding)) {
- ofctrl_remove_flows(flow_table, &binding->header_.uuid);
- }
- consider_port_binding(sbrec_port_binding_by_name,
- mff_ovn_geneve, ct_zones,
- active_tunnels, local_datapaths,
- binding, chassis,
- flow_table, &ofpacts);
- }
- }
-
-}
-
-void
-physical_handle_mc_group_changes(
- const struct sbrec_multicast_group_table *multicast_group_table,
- enum mf_field_id mff_ovn_geneve,
- const struct sbrec_chassis *chassis,
- const struct simap *ct_zones,
- const struct hmap *local_datapaths,
- struct ovn_desired_flow_table *flow_table)
-{
- const struct sbrec_multicast_group *mc;
- SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_TRACKED (mc, multicast_group_table) {
- if (sbrec_multicast_group_is_deleted(mc)) {
- ofctrl_remove_flows(flow_table, &mc->header_.uuid);
- } else {
- if (!sbrec_multicast_group_is_new(mc)) {
- ofctrl_remove_flows(flow_table, &mc->header_.uuid);
- }
- consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths,
- chassis, mc, flow_table);
- }
- }
-}
-
-void
-physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_multicast_group_table *multicast_group_table,
- const struct sbrec_port_binding_table *port_binding_table,
- enum mf_field_id mff_ovn_geneve,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis,
- const struct simap *ct_zones,
- const struct hmap *local_datapaths,
- const struct sset *local_lports,
- const struct sset *active_tunnels,
- struct ovn_desired_flow_table *flow_table)
-{
- if (!hc_uuid) {
- hc_uuid = xmalloc(sizeof(struct uuid));
- uuid_generate(hc_uuid);
- }
-
- /* This bool tracks physical mapping changes. */
- bool physical_map_changed = false;
-
- struct simap new_localvif_to_ofport =
- SIMAP_INITIALIZER(&new_localvif_to_ofport);
- struct simap new_tunnel_to_ofport =
- SIMAP_INITIALIZER(&new_tunnel_to_ofport);
- for (int i = 0; i < br_int->n_ports; i++) {
- const struct ovsrec_port *port_rec = br_int->ports[i];
- if (!strcmp(port_rec->name, br_int->name)) {
- continue;
- }
-
- const char *tunnel_id = smap_get(&port_rec->external_ids,
- "ovn-chassis-id");
- if (tunnel_id &&
- encaps_tunnel_id_match(tunnel_id, chassis->name, NULL)) {
- continue;
- }
-
- const char *localnet = smap_get(&port_rec->external_ids,
- "ovn-localnet-port");
- const char *l2gateway = smap_get(&port_rec->external_ids,
- "ovn-l2gateway-port");
-
- for (int j = 0; j < port_rec->n_interfaces; j++) {
- const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
-
- /* Get OpenFlow port number. */
- if (!iface_rec->n_ofport) {
- continue;
- }
- int64_t ofport = iface_rec->ofport[0];
- if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) {
- continue;
- }
-
- /* Record as patch to local net, logical patch port, chassis, or
- * local logical port. */
- bool is_patch = !strcmp(iface_rec->type, "patch");
- if (is_patch && localnet) {
- /* localnet patch ports can be handled just like VIFs. */
- simap_put(&new_localvif_to_ofport, localnet, ofport);
- break;
- } else if (is_patch && l2gateway) {
- /* L2 gateway patch ports can be handled just like VIFs. */
- simap_put(&new_localvif_to_ofport, l2gateway, ofport);
- break;
- } else if (tunnel_id) {
- enum chassis_tunnel_type tunnel_type;
- if (!strcmp(iface_rec->type, "geneve")) {
- tunnel_type = GENEVE;
- if (!mff_ovn_geneve) {
- continue;
- }
- } else if (!strcmp(iface_rec->type, "stt")) {
- tunnel_type = STT;
- } else if (!strcmp(iface_rec->type, "vxlan")) {
- tunnel_type = VXLAN;
- } else {
- continue;
- }
-
- simap_put(&new_tunnel_to_ofport, tunnel_id, ofport);
- /*
- * We split the tunnel_id to get the chassis-id
- * and hash the tunnel list on the chassis-id. The
- * reason to use the chassis-id alone is because
- * there might be cases (multicast, gateway chassis)
- * where we need to tunnel to the chassis, but won't
- * have the encap-ip specifically.
- */
- char *hash_id = NULL;
- char *ip = NULL;
-
- if (!encaps_tunnel_id_parse(tunnel_id, &hash_id, &ip)) {
- continue;
- }
- struct chassis_tunnel *tun = chassis_tunnel_find(hash_id, ip);
- if (tun) {
- /* If the tunnel's ofport has changed, update. */
- if (tun->ofport != u16_to_ofp(ofport) ||
- tun->type != tunnel_type) {
- tun->ofport = u16_to_ofp(ofport);
- tun->type = tunnel_type;
- physical_map_changed = true;
- }
- } else {
- tun = xmalloc(sizeof *tun);
- hmap_insert(&tunnels, &tun->hmap_node,
- hash_string(hash_id, 0));
- tun->chassis_id = xstrdup(tunnel_id);
- tun->ofport = u16_to_ofp(ofport);
- tun->type = tunnel_type;
- physical_map_changed = true;
- }
- free(hash_id);
- free(ip);
- break;
- } else {
- const char *iface_id = smap_get(&iface_rec->external_ids,
- "iface-id");
- if (iface_id) {
- simap_put(&new_localvif_to_ofport, iface_id, ofport);
- }
- }
- }
- }
-
- /* Remove tunnels that are no longer here. */
- struct chassis_tunnel *tun, *tun_next;
- HMAP_FOR_EACH_SAFE (tun, tun_next, hmap_node, &tunnels) {
- if (!simap_find(&new_tunnel_to_ofport, tun->chassis_id)) {
- hmap_remove(&tunnels, &tun->hmap_node);
- physical_map_changed = true;
- free(tun->chassis_id);
- free(tun);
- }
- }
-
- /* Capture changed or removed openflow ports. */
- physical_map_changed |= update_ofports(&localvif_to_ofport,
- &new_localvif_to_ofport);
- if (physical_map_changed) {
- /* Reprocess logical flow table immediately. */
- poll_immediate_wake();
- }
-
- struct ofpbuf ofpacts;
- ofpbuf_init(&ofpacts, 0);
-
- /* Set up flows in table 0 for physical-to-logical translation and in table
- * 64 for logical-to-physical translation. */
- const struct sbrec_port_binding *binding;
- SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
- consider_port_binding(sbrec_port_binding_by_name,
- mff_ovn_geneve, ct_zones,
- active_tunnels, local_datapaths,
- binding, chassis,
- flow_table, &ofpacts);
- }
-
- /* Handle output to multicast groups, in tables 32 and 33. */
- const struct sbrec_multicast_group *mc;
- SBREC_MULTICAST_GROUP_TABLE_FOR_EACH (mc, multicast_group_table) {
- consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths,
- chassis, mc, flow_table);
- }
-
- /* Table 0, priority 100.
- * ======================
- *
- * Process packets that arrive from a remote hypervisor (by matching
- * on tunnel in_port). */
-
- /* Add flows for Geneve and STT encapsulations. These
- * encapsulations have metadata about the ingress and egress logical
- * ports. We set MFF_LOG_DATAPATH, MFF_LOG_INPORT, and
- * MFF_LOG_OUTPORT from the tunnel key data, then resubmit to table
- * 33 to handle packets to the local hypervisor. */
- HMAP_FOR_EACH (tun, hmap_node, &tunnels) {
- struct match match = MATCH_CATCHALL_INITIALIZER;
- match_set_in_port(&match, tun->ofport);
-
- ofpbuf_clear(&ofpacts);
- if (tun->type == GENEVE) {
- put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts);
- put_move(mff_ovn_geneve, 16, MFF_LOG_INPORT, 0, 15,
- &ofpacts);
- put_move(mff_ovn_geneve, 0, MFF_LOG_OUTPORT, 0, 16,
- &ofpacts);
- } else if (tun->type == STT) {
- put_move(MFF_TUN_ID, 40, MFF_LOG_INPORT, 0, 15, &ofpacts);
- put_move(MFF_TUN_ID, 24, MFF_LOG_OUTPORT, 0, 16, &ofpacts);
- put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts);
- } else if (tun->type == VXLAN) {
- /* We'll handle VXLAN later. */
- continue;
- } else {
- OVS_NOT_REACHED();
- }
-
- put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
-
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
- &ofpacts, hc_uuid);
- }
-
- /* Add flows for VXLAN encapsulations. Due to the limited amount of
- * metadata, we only support VXLAN for connections to gateways. The
- * VNI is used to populate MFF_LOG_DATAPATH. The gateway's logical
- * port is set to MFF_LOG_INPORT. Then the packet is resubmitted to
- * table 16 to determine the logical egress port. */
- HMAP_FOR_EACH (tun, hmap_node, &tunnels) {
- if (tun->type != VXLAN) {
- continue;
- }
-
- SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
- struct match match = MATCH_CATCHALL_INITIALIZER;
-
- if (!binding->chassis ||
- !encaps_tunnel_id_match(tun->chassis_id,
- binding->chassis->name, NULL)) {
- continue;
- }
-
- match_set_in_port(&match, tun->ofport);
- match_set_tun_id(&match, htonll(binding->datapath->tunnel_key));
-
- ofpbuf_clear(&ofpacts);
- put_move(MFF_TUN_ID, 0, MFF_LOG_DATAPATH, 0, 24, &ofpacts);
- put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 15, &ofpacts);
- /* For packets received from a vxlan tunnel, set a flag to that
- * effect. */
- put_load(1, MFF_LOG_FLAGS, MLF_RCV_FROM_VXLAN_BIT, 1, &ofpacts);
- put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
-
- ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
- &ofpacts, hc_uuid);
- }
- }
-
- /* Table 32, priority 150.
- * =======================
- *
- * Handles packets received from a VXLAN tunnel which get resubmitted to
- * OFTABLE_LOG_INGRESS_PIPELINE due to lack of needed metadata in VXLAN,
- * explicitly skip sending back out any tunnels and resubmit to table 33
- * for local delivery.
- */
- struct match match;
- match_init_catchall(&match);
- match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
- MLF_RCV_FROM_VXLAN, MLF_RCV_FROM_VXLAN);
-
- /* Resubmit to table 33. */
- ofpbuf_clear(&ofpacts);
- put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
- &match, &ofpacts, hc_uuid);
-
- /* Table 32, priority 150.
- * =======================
- *
- * Packets that should not be sent to other hypervisors.
- */
- match_init_catchall(&match);
- match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
- MLF_LOCAL_ONLY, MLF_LOCAL_ONLY);
- /* Resubmit to table 33. */
- ofpbuf_clear(&ofpacts);
- put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
- &match, &ofpacts, hc_uuid);
-
- /* Table 32, priority 150.
- * =======================
- *
- * Handles packets received from ports of type "localport". These ports
- * are present on every hypervisor. Traffic that originates at one should
- * never go over a tunnel to a remote hypervisor, so resubmit them to table
- * 33 for local delivery. */
- match_init_catchall(&match);
- ofpbuf_clear(&ofpacts);
- put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- const char *localport;
- SSET_FOR_EACH (localport, local_lports) {
- /* Iterate over all local logical ports and insert a drop
- * rule with higher priority for every localport in this
- * datapath. */
- const struct sbrec_port_binding *pb = lport_lookup_by_name(
- sbrec_port_binding_by_name, localport);
- if (pb && !strcmp(pb->type, "localport")) {
- match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, pb->tunnel_key);
- match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
- &match, &ofpacts, hc_uuid);
- }
- }
-
- /* Table 32, Priority 0.
- * =======================
- *
- * Resubmit packets that are not directed at tunnels or part of a
- * multicast group to the local output table. */
- match_init_catchall(&match);
- ofpbuf_clear(&ofpacts);
- put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match, &ofpacts,
- hc_uuid);
-
- /* Table 34, Priority 0.
- * =======================
- *
- * Resubmit packets that don't output to the ingress port (already checked
- * in table 33) to the logical egress pipeline, clearing the logical
- * registers (for consistent behavior with packets that get tunneled). */
- match_init_catchall(&match);
- ofpbuf_clear(&ofpacts);
- for (int i = 0; i < MFF_N_LOG_REGS; i++) {
- put_load(0, MFF_REG0 + i, 0, 32, &ofpacts);
- }
- put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, 0, &match,
- &ofpacts, hc_uuid);
-
- /* Table 64, Priority 0.
- * =======================
- *
- * Resubmit packets that do not have the MLF_ALLOW_LOOPBACK flag set
- * to table 65 for logical-to-physical translation. */
- match_init_catchall(&match);
- ofpbuf_clear(&ofpacts);
- put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts);
- ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, 0, &match, &ofpacts,
- hc_uuid);
-
- ofpbuf_uninit(&ofpacts);
-
- simap_destroy(&new_tunnel_to_ofport);
-}
diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
deleted file mode 100644
index c5544e8de..000000000
--- a/ovn/controller/physical.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_PHYSICAL_H
-#define OVN_PHYSICAL_H 1
-
-/* Logical/Physical Translation
- * ============================
- *
- * This module implements physical-to-logical and logical-to-physical
- * translation as separate OpenFlow tables that run before the ingress pipeline
- * and after the egress pipeline, respectively, as well as to connect the
- * two pipelines.
- */
-
-#include "openvswitch/meta-flow.h"
-
-struct hmap;
-struct ovsdb_idl_index;
-struct ovsrec_bridge;
-struct simap;
-struct sbrec_multicast_group_table;
-struct sbrec_port_binding_table;
-struct sset;
-
-/* OVN Geneve option information.
- *
- * Keep these in sync with the documentation in ovn-architecture(7). */
-#define OVN_GENEVE_CLASS 0x0102 /* Assigned Geneve class for OVN. */
-#define OVN_GENEVE_TYPE 0x80 /* Critical option. */
-#define OVN_GENEVE_LEN 4
-
-void physical_register_ovs_idl(struct ovsdb_idl *);
-void physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_multicast_group_table *,
- const struct sbrec_port_binding_table *,
- enum mf_field_id mff_ovn_geneve,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis,
- const struct simap *ct_zones,
- const struct hmap *local_datapaths,
- const struct sset *local_lports,
- const struct sset *active_tunnels,
- struct ovn_desired_flow_table *);
-void physical_handle_port_binding_changes(
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_port_binding_table *,
- enum mf_field_id mff_ovn_geneve,
- const struct sbrec_chassis *,
- const struct simap *ct_zones,
- struct hmap *local_datapaths,
- struct sset *active_tunnels,
- struct ovn_desired_flow_table *);
-
-void physical_handle_mc_group_changes(
- const struct sbrec_multicast_group_table *,
- enum mf_field_id mff_ovn_geneve,
- const struct sbrec_chassis *,
- const struct simap *ct_zones,
- const struct hmap *local_datapaths,
- struct ovn_desired_flow_table *);
-#endif /* ovn/physical.h */
diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
deleted file mode 100644
index b115d1a57..000000000
--- a/ovn/controller/pinctrl.c
+++ /dev/null
@@ -1,4342 +0,0 @@
-/* Copyright (c) 2015, 2016, 2017 Red Hat, Inc.
- * Copyright (c) 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "pinctrl.h"
-
-#include "coverage.h"
-#include "csum.h"
-#include "dirs.h"
-#include "dp-packet.h"
-#include "encaps.h"
-#include "flow.h"
-#include "ha-chassis.h"
-#include "lport.h"
-#include "nx-match.h"
-#include "ovn-controller.h"
-#include "latch.h"
-#include "lib/packets.h"
-#include "lib/sset.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofp-msgs.h"
-#include "openvswitch/ofp-packet.h"
-#include "openvswitch/ofp-print.h"
-#include "openvswitch/ofp-switch.h"
-#include "openvswitch/ofp-util.h"
-#include "openvswitch/vlog.h"
-
-#include "lib/dhcp.h"
-#include "ovn-controller.h"
-#include "ovn/actions.h"
-#include "ovn/lex.h"
-#include "ovn/lib/acl-log.h"
-#include "ovn/lib/ip-mcast-index.h"
-#include "ovn/lib/mcast-group-index.h"
-#include "ovn/lib/ovn-l7.h"
-#include "ovn/lib/ovn-util.h"
-#include "ovn/logical-fields.h"
-#include "openvswitch/poll-loop.h"
-#include "openvswitch/rconn.h"
-#include "socket-util.h"
-#include "seq.h"
-#include "timeval.h"
-#include "vswitch-idl.h"
-#include "lflow.h"
-#include "ip-mcast.h"
-
-VLOG_DEFINE_THIS_MODULE(pinctrl);
-
-/* pinctrl module creates a thread - pinctrl_handler to handle
- * the packet-ins from ovs-vswitchd. Some of the OVN actions
- * are translated to OF 'controller' actions. See include/ovn/actions.h
- * for more details.
- *
- * pinctrl_handler thread doesn't access the Southbound IDL object. But
- * some of the OVN actions which gets translated to 'controller'
- * OF action, require data from Southbound DB. Below are the details
- * on how these actions are implemented.
- *
- * pinctrl_run() function is called by ovn-controller main thread.
- * A Mutex - 'pinctrl_mutex' is used between the pinctrl_handler() thread
- * and pinctrl_run().
- *
- * - dns_lookup - In order to do a DNS lookup, this action needs
- * to access the 'DNS' table. pinctrl_run() builds a
- * local DNS cache - 'dns_cache'. See sync_dns_cache()
- * for more details.
- * The function 'pinctrl_handle_dns_lookup()' (which is
- * called with in the pinctrl_handler thread) looks into
- * the local DNS cache to resolve the DNS requests.
- *
- * - put_arp/put_nd - These actions stores the IPv4/IPv6 and MAC addresses
- * in the 'MAC_Binding' table.
- * The function 'pinctrl_handle_put_mac_binding()' (which
- * is called with in the pinctrl_handler thread), stores
- * the IPv4/IPv6 and MAC addresses in the
- * hmap - put_mac_bindings.
- *
- * pinctrl_run(), reads these mac bindings from the hmap
- * 'put_mac_bindings' and writes to the 'MAC_Binding'
- * table in the Southbound DB.
- *
- * - arp/nd_ns - These actions generate an ARP/IPv6 Neighbor solicit
- * requests. The original packets are buffered and
- * injected back when put_arp/put_nd resolves
- * corresponding ARP/IPv6 Neighbor solicit requests.
- * When pinctrl_run(), writes the mac bindings from the
- * 'put_mac_bindings' hmap to the MAC_Binding table in
- * SB DB, run_buffered_binding will add the buffered
- * packets to buffered_mac_bindings and notify
- * pinctrl_handler.
- *
- * The pinctrl_handler thread calls the function -
- * send_mac_binding_buffered_pkts(), which uses
- * the hmap - 'buffered_mac_bindings' and reinjects the
- * buffered packets.
- *
- * - igmp - This action punts an IGMP packet to the controller
- * which maintains multicast group information. The
- * multicast groups (mcast_snoop_map) are synced to
- * the 'IGMP_Group' table by ip_mcast_sync().
- * ip_mcast_sync() also reads the 'IP_Multicast'
- * (snooping and querier) configuration and builds a
- * local configuration mcast_cfg_map.
- * ip_mcast_snoop_run() which runs in the
- * pinctrl_handler() thread configures the per datapath
- * mcast_snoop_map entries according to mcast_cfg_map.
- *
- * pinctrl module also periodically sends IPv6 Router Solicitation requests
- * and gARPs (for the router gateway IPs and configured NAT addresses).
- *
- * IPv6 RA handling - pinctrl_run() prepares the IPv6 RA information
- * (see prepare_ipv6_ras()) in the shash 'ipv6_ras' by
- * looking into the Southbound DB table - Port_Binding.
- *
- * pinctrl_handler thread sends the periodic IPv6 RAs using
- * the shash - 'ipv6_ras'
- *
- * gARP handling - pinctrl_run() prepares the gARP information
- * (see send_garp_prepare()) in the shash 'send_garp_data'
- * by looking into the Southbound DB table Port_Binding.
- *
- * pinctrl_handler() thread sends these gARPs using the
- * shash 'send_garp_data'.
- *
- * IGMP Queries - pinctrl_run() prepares the IGMP queries (at most one
- * per local datapath) based on the mcast_snoop_map
- * contents and stores them in mcast_query_list.
- *
- * pinctrl_handler thread sends the periodic IGMP queries
- * by walking the mcast_query_list.
- *
- * Notification between pinctrl_handler() and pinctrl_run()
- * -------------------------------------------------------
- * 'struct seq' is used for notification between pinctrl_handler() thread
- * and pinctrl_run().
- * 'pinctrl_handler_seq' is used by pinctrl_run() to
- * wake up pinctrl_handler thread from poll_block() if any changes happened
- * in 'send_garp_data', 'ipv6_ras' and 'buffered_mac_bindings' structures.
- *
- * 'pinctrl_main_seq' is used by pinctrl_handler() thread to wake up
- * the main thread from poll_block() when mac bindings/igmp groups need to
- * be updated in the Southboubd DB.
- * */
-
-static struct ovs_mutex pinctrl_mutex = OVS_MUTEX_INITIALIZER;
-static struct seq *pinctrl_handler_seq;
-static struct seq *pinctrl_main_seq;
-
-static void *pinctrl_handler(void *arg);
-
-struct pinctrl {
- char *br_int_name;
- pthread_t pinctrl_thread;
- /* Latch to destroy the 'pinctrl_thread' */
- struct latch pinctrl_thread_exit;
-};
-
-static struct pinctrl pinctrl;
-
-static void init_buffered_packets_map(void);
-static void destroy_buffered_packets_map(void);
-static void
-run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
- const struct hmap *local_datapaths)
- OVS_REQUIRES(pinctrl_mutex);
-
-static void pinctrl_handle_put_mac_binding(const struct flow *md,
- const struct flow *headers,
- bool is_arp)
- OVS_REQUIRES(pinctrl_mutex);
-static void init_put_mac_bindings(void);
-static void destroy_put_mac_bindings(void);
-static void run_put_mac_bindings(
- struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip)
- OVS_REQUIRES(pinctrl_mutex);
-static void wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn);
-static void flush_put_mac_bindings(void);
-static void send_mac_binding_buffered_pkts(struct rconn *swconn)
- OVS_REQUIRES(pinctrl_mutex);
-
-static void init_send_garps(void);
-static void destroy_send_garps(void);
-static void send_garp_wait(long long int send_garp_time);
-static void send_garp_prepare(
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct ovsrec_bridge *,
- const struct sbrec_chassis *,
- const struct hmap *local_datapaths,
- const struct sset *active_tunnels)
- OVS_REQUIRES(pinctrl_mutex);
-static void send_garp_run(struct rconn *swconn, long long int *send_garp_time)
- OVS_REQUIRES(pinctrl_mutex);
-static void pinctrl_handle_nd_na(struct rconn *swconn,
- const struct flow *ip_flow,
- const struct match *md,
- struct ofpbuf *userdata,
- bool is_router);
-static void reload_metadata(struct ofpbuf *ofpacts,
- const struct match *md);
-static void pinctrl_handle_put_nd_ra_opts(
- struct rconn *swconn,
- const struct flow *ip_flow, struct dp_packet *pkt_in,
- struct ofputil_packet_in *pin, struct ofpbuf *userdata,
- struct ofpbuf *continuation);
-static void pinctrl_handle_nd_ns(struct rconn *swconn,
- const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md,
- struct ofpbuf *userdata);
-static void pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
- const struct flow *in_flow,
- struct dp_packet *pkt_in,
- struct ofputil_packet_in *pin,
- struct ofpbuf *userdata,
- struct ofpbuf *continuation);
-static void
-pinctrl_handle_event(struct ofpbuf *userdata)
- OVS_REQUIRES(pinctrl_mutex);
-static void wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn);
-static void init_ipv6_ras(void);
-static void destroy_ipv6_ras(void);
-static void ipv6_ra_wait(long long int send_ipv6_ra_time);
-static void prepare_ipv6_ras(
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct hmap *local_datapaths)
- OVS_REQUIRES(pinctrl_mutex);
-static void send_ipv6_ras(struct rconn *swconn,
- long long int *send_ipv6_ra_time)
- OVS_REQUIRES(pinctrl_mutex);
-
-static void ip_mcast_snoop_init(void);
-static void ip_mcast_snoop_destroy(void);
-static void ip_mcast_snoop_run(void)
- OVS_REQUIRES(pinctrl_mutex);
-static void ip_mcast_querier_run(struct rconn *swconn,
- long long int *query_time);
-static void ip_mcast_querier_wait(long long int query_time);
-static void ip_mcast_sync(
- struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_igmp_groups,
- struct ovsdb_idl_index *sbrec_ip_multicast)
- OVS_REQUIRES(pinctrl_mutex);
-static void pinctrl_ip_mcast_handle_igmp(
- struct rconn *swconn,
- const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md,
- struct ofpbuf *userdata);
-
-static bool may_inject_pkts(void);
-
-COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
-COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
-COVERAGE_DEFINE(pinctrl_drop_controller_event);
-
-struct empty_lb_backends_event {
- struct hmap_node hmap_node;
- long long int timestamp;
-
- char *vip;
- char *protocol;
- char *load_balancer;
-};
-
-static struct hmap event_table[OVN_EVENT_MAX];
-static int64_t event_seq_num;
-
-static void
-init_event_table(void)
-{
- for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
- hmap_init(&event_table[i]);
- }
-}
-
-#define EVENT_TIMEOUT 10000
-static void
-empty_lb_backends_event_gc(bool flush)
-{
- struct empty_lb_backends_event *cur_ce, *next_ce;
- long long int now = time_msec();
-
- HMAP_FOR_EACH_SAFE (cur_ce, next_ce, hmap_node,
- &event_table[OVN_EVENT_EMPTY_LB_BACKENDS]) {
- if ((now < cur_ce->timestamp + EVENT_TIMEOUT) && !flush) {
- continue;
- }
-
- free(cur_ce->vip);
- free(cur_ce->protocol);
- free(cur_ce->load_balancer);
- hmap_remove(&event_table[OVN_EVENT_EMPTY_LB_BACKENDS],
- &cur_ce->hmap_node);
- free(cur_ce);
- }
-}
-
-static void
-event_table_gc(bool flush)
-{
- empty_lb_backends_event_gc(flush);
-}
-
-static void
-event_table_destroy(void)
-{
- event_table_gc(true);
- for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
- hmap_destroy(&event_table[i]);
- }
-}
-
-static struct empty_lb_backends_event *
-pinctrl_find_empty_lb_backends_event(char *vip, char *protocol,
- char *load_balancer, uint32_t hash)
-{
- struct empty_lb_backends_event *ce;
- HMAP_FOR_EACH_WITH_HASH (ce, hmap_node, hash,
- &event_table[OVN_EVENT_EMPTY_LB_BACKENDS]) {
- if (!strcmp(ce->vip, vip) &&
- !strcmp(ce->protocol, protocol) &&
- !strcmp(ce->load_balancer, load_balancer)) {
- return ce;
- }
- }
- return NULL;
-}
-
-static const struct sbrec_controller_event *
-empty_lb_backends_lookup(struct empty_lb_backends_event *event,
- const struct sbrec_controller_event_table *ce_table,
- const struct sbrec_chassis *chassis)
-{
- const struct sbrec_controller_event *sbrec_event;
- const char *event_type = event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS);
- char ref_uuid[UUID_LEN + 1];
- sprintf(ref_uuid, UUID_FMT, UUID_ARGS(&chassis->header_.uuid));
-
- SBREC_CONTROLLER_EVENT_TABLE_FOR_EACH (sbrec_event, ce_table) {
- if (strcmp(sbrec_event->event_type, event_type)) {
- continue;
- }
-
- char chassis_uuid[UUID_LEN + 1];
- sprintf(chassis_uuid, UUID_FMT,
- UUID_ARGS(&sbrec_event->chassis->header_.uuid));
- if (strcmp(ref_uuid, chassis_uuid)) {
- continue;
- }
-
- const char *vip = smap_get(&sbrec_event->event_info, "vip");
- const char *protocol = smap_get(&sbrec_event->event_info, "protocol");
- const char *load_balancer = smap_get(&sbrec_event->event_info,
- "load_balancer");
-
- if (!strcmp(event->vip, vip) &&
- !strcmp(event->protocol, protocol) &&
- !strcmp(event->load_balancer, load_balancer)) {
- return sbrec_event;
- }
- }
-
- return NULL;
-}
-
-static void
-controller_event_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_controller_event_table *ce_table,
- const struct sbrec_chassis *chassis)
- OVS_REQUIRES(pinctrl_mutex)
-{
- if (!ovnsb_idl_txn) {
- goto out;
- }
-
- struct empty_lb_backends_event *empty_lbs;
- HMAP_FOR_EACH (empty_lbs, hmap_node,
- &event_table[OVN_EVENT_EMPTY_LB_BACKENDS]) {
- const struct sbrec_controller_event *event;
-
- event = empty_lb_backends_lookup(empty_lbs, ce_table, chassis);
- if (!event) {
- struct smap event_info = SMAP_INITIALIZER(&event_info);
-
- smap_add(&event_info, "vip", empty_lbs->vip);
- smap_add(&event_info, "protocol", empty_lbs->protocol);
- smap_add(&event_info, "load_balancer", empty_lbs->load_balancer);
-
- event = sbrec_controller_event_insert(ovnsb_idl_txn);
- sbrec_controller_event_set_event_type(event,
- event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS));
- sbrec_controller_event_set_seq_num(event, ++event_seq_num);
- sbrec_controller_event_set_event_info(event, &event_info);
- sbrec_controller_event_set_chassis(event, chassis);
- }
- }
-
-out:
- event_table_gc(!!ovnsb_idl_txn);
-}
-
-void
-pinctrl_init(void)
-{
- init_put_mac_bindings();
- init_send_garps();
- init_ipv6_ras();
- init_buffered_packets_map();
- init_event_table();
- ip_mcast_snoop_init();
- pinctrl.br_int_name = NULL;
- pinctrl_handler_seq = seq_create();
- pinctrl_main_seq = seq_create();
-
- latch_init(&pinctrl.pinctrl_thread_exit);
- pinctrl.pinctrl_thread = ovs_thread_create("ovn_pinctrl", pinctrl_handler,
- &pinctrl);
-}
-
-static ovs_be32
-queue_msg(struct rconn *swconn, struct ofpbuf *msg)
-{
- const struct ofp_header *oh = msg->data;
- ovs_be32 xid = oh->xid;
-
- rconn_send(swconn, msg, NULL);
- return xid;
-}
-
-/* Sets up 'swconn', a newly (re)connected connection to a switch. */
-static void
-pinctrl_setup(struct rconn *swconn)
-{
- /* Fetch the switch configuration. The response later will allow us to
- * change the miss_send_len to UINT16_MAX, so that we can enable
- * asynchronous messages. */
- queue_msg(swconn, ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST,
- rconn_get_version(swconn), 0));
-
- /* Set a packet-in format that supports userdata. */
- queue_msg(swconn,
- ofputil_encode_set_packet_in_format(rconn_get_version(swconn),
- OFPUTIL_PACKET_IN_NXT2));
-}
-
-static void
-set_switch_config(struct rconn *swconn,
- const struct ofputil_switch_config *config)
-{
- enum ofp_version version = rconn_get_version(swconn);
- struct ofpbuf *request = ofputil_encode_set_config(config, version);
- queue_msg(swconn, request);
-}
-
-static void
-set_actions_and_enqueue_msg(struct rconn *swconn,
- const struct dp_packet *packet,
- const struct match *md,
- struct ofpbuf *userdata)
-{
- /* Copy metadata from 'md' into the packet-out via "set_field"
- * actions, then add actions from 'userdata'.
- */
- uint64_t ofpacts_stub[4096 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- enum ofp_version version = rconn_get_version(swconn);
-
- reload_metadata(&ofpacts, md);
- enum ofperr error = ofpacts_pull_openflow_actions(userdata, userdata->size,
- version, NULL, NULL,
- &ofpacts);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "failed to parse actions from userdata (%s)",
- ofperr_to_string(error));
- ofpbuf_uninit(&ofpacts);
- return;
- }
-
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(packet),
- .packet_len = dp_packet_size(packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
- ofpbuf_uninit(&ofpacts);
-}
-
-struct buffer_info {
- struct ofpbuf ofpacts;
- struct dp_packet *p;
-};
-
-#define BUFFER_QUEUE_DEPTH 4
-struct buffered_packets {
- struct hmap_node hmap_node;
- struct ovs_list list;
-
- /* key */
- struct in6_addr ip;
- struct eth_addr ea;
-
- long long int timestamp;
-
- struct buffer_info data[BUFFER_QUEUE_DEPTH];
- uint32_t head, tail;
-};
-
-static struct hmap buffered_packets_map;
-static struct ovs_list buffered_mac_bindings;
-
-static void
-init_buffered_packets_map(void)
-{
- hmap_init(&buffered_packets_map);
- ovs_list_init(&buffered_mac_bindings);
-}
-
-static void
-destroy_buffered_packets(struct buffered_packets *bp)
-{
- struct buffer_info *bi;
-
- while (bp->head != bp->tail) {
- bi = &bp->data[bp->head];
- dp_packet_delete(bi->p);
- ofpbuf_uninit(&bi->ofpacts);
-
- bp->head = (bp->head + 1) % BUFFER_QUEUE_DEPTH;
- }
-}
-
-static void
-destroy_buffered_packets_map(void)
-{
- struct buffered_packets *bp, *next;
- HMAP_FOR_EACH_SAFE (bp, next, hmap_node, &buffered_packets_map) {
- destroy_buffered_packets(bp);
- hmap_remove(&buffered_packets_map, &bp->hmap_node);
- free(bp);
- }
- hmap_destroy(&buffered_packets_map);
-
- LIST_FOR_EACH_POP (bp, list, &buffered_mac_bindings) {
- destroy_buffered_packets(bp);
- free(bp);
- }
-}
-
-static void
-buffered_push_packet(struct buffered_packets *bp,
- struct dp_packet *packet,
- const struct match *md)
-{
- uint32_t next = (bp->tail + 1) % BUFFER_QUEUE_DEPTH;
- struct buffer_info *bi = &bp->data[bp->tail];
-
- ofpbuf_init(&bi->ofpacts, 4096);
-
- reload_metadata(&bi->ofpacts, md);
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&bi->ofpacts);
- resubmit->in_port = OFPP_CONTROLLER;
- resubmit->table_id = OFTABLE_REMOTE_OUTPUT;
-
- bi->p = packet;
-
- if (next == bp->head) {
- bi = &bp->data[bp->head];
- dp_packet_delete(bi->p);
- ofpbuf_uninit(&bi->ofpacts);
- bp->head = (bp->head + 1) % BUFFER_QUEUE_DEPTH;
- }
- bp->tail = next;
-}
-
-static void
-buffered_send_packets(struct rconn *swconn, struct buffered_packets *bp,
- struct eth_addr *addr)
-{
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
-
- while (bp->head != bp->tail) {
- struct buffer_info *bi = &bp->data[bp->head];
- struct eth_header *eth = dp_packet_data(bi->p);
-
- eth->eth_dst = *addr;
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(bi->p),
- .packet_len = dp_packet_size(bi->p),
- .buffer_id = UINT32_MAX,
- .ofpacts = bi->ofpacts.data,
- .ofpacts_len = bi->ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
- queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
-
- ofpbuf_uninit(&bi->ofpacts);
- dp_packet_delete(bi->p);
-
- bp->head = (bp->head + 1) % BUFFER_QUEUE_DEPTH;
- }
-}
-
-#define BUFFER_MAP_TIMEOUT 10000
-static void
-buffered_packets_map_gc(void)
-{
- struct buffered_packets *cur_qp, *next_qp;
- long long int now = time_msec();
-
- HMAP_FOR_EACH_SAFE (cur_qp, next_qp, hmap_node, &buffered_packets_map) {
- if (now > cur_qp->timestamp + BUFFER_MAP_TIMEOUT) {
- destroy_buffered_packets(cur_qp);
- hmap_remove(&buffered_packets_map, &cur_qp->hmap_node);
- free(cur_qp);
- }
- }
-}
-
-static struct buffered_packets *
-pinctrl_find_buffered_packets(const struct in6_addr *ip, uint32_t hash)
-{
- struct buffered_packets *qp;
-
- HMAP_FOR_EACH_WITH_HASH (qp, hmap_node, hash,
- &buffered_packets_map) {
- if (IN6_ARE_ADDR_EQUAL(&qp->ip, ip)) {
- return qp;
- }
- }
- return NULL;
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static int
-pinctrl_handle_buffered_packets(const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md, bool is_arp)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct buffered_packets *bp;
- struct dp_packet *clone;
- struct in6_addr addr;
-
- if (is_arp) {
- addr = in6_addr_mapped_ipv4(ip_flow->nw_dst);
- } else {
- addr = ip_flow->ipv6_dst;
- }
-
- uint32_t hash = hash_bytes(&addr, sizeof addr, 0);
- bp = pinctrl_find_buffered_packets(&addr, hash);
- if (!bp) {
- if (hmap_count(&buffered_packets_map) >= 1000) {
- COVERAGE_INC(pinctrl_drop_buffered_packets_map);
- return -ENOMEM;
- }
-
- bp = xmalloc(sizeof *bp);
- hmap_insert(&buffered_packets_map, &bp->hmap_node, hash);
- bp->head = bp->tail = 0;
- bp->ip = addr;
- }
- bp->timestamp = time_msec();
- /* clone the packet to send it later with correct L2 address */
- clone = dp_packet_clone_data(dp_packet_data(pkt_in),
- dp_packet_size(pkt_in));
- buffered_push_packet(bp, clone, md);
-
- return 0;
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_arp(struct rconn *swconn, const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md, struct ofpbuf *userdata)
-{
- /* This action only works for IP packets, and the switch should only send
- * us IP packets this way, but check here just to be sure. */
- if (ip_flow->dl_type != htons(ETH_TYPE_IP)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "ARP action on non-IP packet (Ethertype %"PRIx16")",
- ntohs(ip_flow->dl_type));
- return;
- }
-
- ovs_mutex_lock(&pinctrl_mutex);
- pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, true);
- ovs_mutex_unlock(&pinctrl_mutex);
-
- /* Compose an ARP packet. */
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- compose_arp__(&packet);
-
- struct eth_header *eth = dp_packet_eth(&packet);
- eth->eth_dst = ip_flow->dl_dst;
- eth->eth_src = ip_flow->dl_src;
-
- struct arp_eth_header *arp = dp_packet_l3(&packet);
- arp->ar_op = htons(ARP_OP_REQUEST);
- arp->ar_sha = ip_flow->dl_src;
- put_16aligned_be32(&arp->ar_spa, ip_flow->nw_src);
- arp->ar_tha = eth_addr_zero;
- put_16aligned_be32(&arp->ar_tpa, ip_flow->nw_dst);
-
- if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
- eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
- ip_flow->vlans[0].tci);
- }
-
- set_actions_and_enqueue_msg(swconn, &packet, md, userdata);
- dp_packet_uninit(&packet);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_icmp(struct rconn *swconn, const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md, struct ofpbuf *userdata,
- bool include_orig_ip_datagram)
-{
- /* This action only works for IP packets, and the switch should only send
- * us IP packets this way, but check here just to be sure. */
- if (ip_flow->dl_type != htons(ETH_TYPE_IP) &&
- ip_flow->dl_type != htons(ETH_TYPE_IPV6)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl,
- "ICMP action on non-IP packet (eth_type 0x%"PRIx16")",
- ntohs(ip_flow->dl_type));
- return;
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
-
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- dp_packet_clear(&packet);
- packet.packet_type = htonl(PT_ETH);
-
- struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
- eh->eth_dst = ip_flow->dl_dst;
- eh->eth_src = ip_flow->dl_src;
-
- if (get_dl_type(ip_flow) == htons(ETH_TYPE_IP)) {
- struct ip_header *in_ip = dp_packet_l3(pkt_in);
- uint16_t in_ip_len = ntohs(in_ip->ip_tot_len);
- if (in_ip_len < IP_HEADER_LEN) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl,
- "ICMP action on IP packet with invalid length (%u)",
- in_ip_len);
- return;
- }
-
- struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-
- eh->eth_type = htons(ETH_TYPE_IP);
- dp_packet_set_l3(&packet, nh);
- nh->ip_ihl_ver = IP_IHL_VER(5, 4);
- nh->ip_tot_len = htons(sizeof(struct ip_header) +
- sizeof(struct icmp_header));
- nh->ip_proto = IPPROTO_ICMP;
- nh->ip_frag_off = htons(IP_DF);
- packet_set_ipv4(&packet, ip_flow->nw_src, ip_flow->nw_dst,
- ip_flow->nw_tos, 255);
-
- struct icmp_header *ih = dp_packet_put_zeros(&packet, sizeof *ih);
- dp_packet_set_l4(&packet, ih);
- packet_set_icmp(&packet, ICMP4_DST_UNREACH, 1);
-
- if (include_orig_ip_datagram) {
- /* RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes
- * of header. MAY send more.
- * RFC says return as much as we can without exceeding 576
- * bytes.
- * So, lets return as much as we can. */
-
- /* Calculate available room to include the original IP + data. */
- nh = dp_packet_l3(&packet);
- uint16_t room = 576 - (sizeof *eh + ntohs(nh->ip_tot_len));
- if (in_ip_len > room) {
- in_ip_len = room;
- }
- dp_packet_put(&packet, in_ip, in_ip_len);
-
- /* dp_packet_put may reallocate the buffer. Get the l3 and l4
- * header pointers again. */
- nh = dp_packet_l3(&packet);
- ih = dp_packet_l4(&packet);
- uint16_t ip_total_len = ntohs(nh->ip_tot_len) + in_ip_len;
- nh->ip_tot_len = htons(ip_total_len);
- ih->icmp_csum = 0;
- ih->icmp_csum = csum(ih, sizeof *ih + in_ip_len);
- nh->ip_csum = 0;
- nh->ip_csum = csum(nh, sizeof *nh);
- }
- } else {
- struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh);
- struct icmp6_error_header *ih;
- uint32_t icmpv6_csum;
-
- eh->eth_type = htons(ETH_TYPE_IPV6);
- dp_packet_set_l3(&packet, nh);
- nh->ip6_vfc = 0x60;
- nh->ip6_nxt = IPPROTO_ICMPV6;
- nh->ip6_plen = htons(sizeof(*nh) + ICMP6_ERROR_HEADER_LEN);
- packet_set_ipv6(&packet, &ip_flow->ipv6_src, &ip_flow->ipv6_dst,
- ip_flow->nw_tos, ip_flow->ipv6_label, 255);
-
- ih = dp_packet_put_zeros(&packet, sizeof *ih);
- dp_packet_set_l4(&packet, ih);
- ih->icmp6_base.icmp6_type = ICMP6_DST_UNREACH;
- ih->icmp6_base.icmp6_code = 1;
- ih->icmp6_base.icmp6_cksum = 0;
-
- uint8_t *data = dp_packet_put_zeros(&packet, sizeof *nh);
- memcpy(data, dp_packet_l3(pkt_in), sizeof(*nh));
-
- icmpv6_csum = packet_csum_pseudoheader6(dp_packet_l3(&packet));
- ih->icmp6_base.icmp6_cksum = csum_finish(
- csum_continue(icmpv6_csum, ih,
- sizeof(*nh) + ICMP6_ERROR_HEADER_LEN));
- }
-
- if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
- eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
- ip_flow->vlans[0].tci);
- }
-
- set_actions_and_enqueue_msg(swconn, &packet, md, userdata);
- dp_packet_uninit(&packet);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md, struct ofpbuf *userdata)
-{
- /* This action only works for TCP segments, and the switch should only send
- * us TCP segments this way, but check here just to be sure. */
- if (ip_flow->nw_proto != IPPROTO_TCP) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "TCP_RESET action on non-TCP packet");
- return;
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
-
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- dp_packet_clear(&packet);
- packet.packet_type = htonl(PT_ETH);
-
- struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
- eh->eth_dst = ip_flow->dl_dst;
- eh->eth_src = ip_flow->dl_src;
-
- if (get_dl_type(ip_flow) == htons(ETH_TYPE_IPV6)) {
- struct ip6_hdr *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-
- eh->eth_type = htons(ETH_TYPE_IPV6);
- dp_packet_set_l3(&packet, nh);
- nh->ip6_vfc = 0x60;
- nh->ip6_nxt = IPPROTO_TCP;
- nh->ip6_plen = htons(TCP_HEADER_LEN);
- packet_set_ipv6(&packet, &ip_flow->ipv6_src, &ip_flow->ipv6_dst,
- ip_flow->nw_tos, ip_flow->ipv6_label, 255);
- } else {
- struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-
- eh->eth_type = htons(ETH_TYPE_IP);
- dp_packet_set_l3(&packet, nh);
- nh->ip_ihl_ver = IP_IHL_VER(5, 4);
- nh->ip_tot_len = htons(IP_HEADER_LEN + TCP_HEADER_LEN);
- nh->ip_proto = IPPROTO_TCP;
- nh->ip_frag_off = htons(IP_DF);
- packet_set_ipv4(&packet, ip_flow->nw_src, ip_flow->nw_dst,
- ip_flow->nw_tos, 255);
- }
-
- struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
- struct tcp_header *tcp_in = dp_packet_l4(pkt_in);
- dp_packet_set_l4(&packet, th);
- th->tcp_ctl = TCP_CTL(TCP_RST, 5);
- if (ip_flow->tcp_flags & htons(TCP_ACK)) {
- th->tcp_seq = tcp_in->tcp_ack;
- } else {
- uint32_t tcp_seq, ack_seq, tcp_len;
-
- tcp_seq = ntohl(get_16aligned_be32(&tcp_in->tcp_seq));
- tcp_len = TCP_OFFSET(tcp_in->tcp_ctl) * 4;
- ack_seq = tcp_seq + dp_packet_l4_size(pkt_in) - tcp_len;
- put_16aligned_be32(&th->tcp_ack, htonl(ack_seq));
- put_16aligned_be32(&th->tcp_seq, 0);
- }
- packet_set_tcp_port(&packet, ip_flow->tp_dst, ip_flow->tp_src);
-
- if (ip_flow->vlans[0].tci & htons(VLAN_CFI)) {
- eth_push_vlan(&packet, htons(ETH_TYPE_VLAN_8021Q),
- ip_flow->vlans[0].tci);
- }
-
- set_actions_and_enqueue_msg(swconn, &packet, md, userdata);
- dp_packet_uninit(&packet);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_put_dhcp_opts(
- struct rconn *swconn,
- struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
- struct ofpbuf *userdata, struct ofpbuf *continuation)
-{
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- struct dp_packet *pkt_out_ptr = NULL;
- uint32_t success = 0;
-
- /* Parse result field. */
- const struct mf_field *f;
- enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
- if (ofperr) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- /* Parse result offset and offer IP. */
- ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
- ovs_be32 *offer_ip = ofpbuf_try_pull(userdata, sizeof *offer_ip);
- if (!ofsp || !offer_ip) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "offset or offer_ip not present in the userdata");
- goto exit;
- }
-
- /* Check that the result is valid and writable. */
- struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 };
- ofperr = mf_check_dst(&dst, NULL);
- if (ofperr) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- if (!userdata->size) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "DHCP options not present in the userdata");
- goto exit;
- }
-
- /* Validate the DHCP request packet.
- * Format of the DHCP packet is
- * ------------------------------------------------------------------------
- *| UDP HEADER | DHCP HEADER | 4 Byte DHCP Cookie | DHCP OPTIONS(var len)|
- * ------------------------------------------------------------------------
- */
-
- const char *end = (char *)dp_packet_l4(pkt_in) + dp_packet_l4_size(pkt_in);
- const char *in_dhcp_ptr = dp_packet_get_udp_payload(pkt_in);
- if (!in_dhcp_ptr) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid or incomplete DHCP packet received");
- goto exit;
- }
-
- const struct dhcp_header *in_dhcp_data
- = (const struct dhcp_header *) in_dhcp_ptr;
- in_dhcp_ptr += sizeof *in_dhcp_data;
- if (in_dhcp_ptr > end) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid or incomplete DHCP packet received, "
- "bad data length");
- goto exit;
- }
- if (in_dhcp_data->op != DHCP_OP_REQUEST) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid opcode in the DHCP packet: %d",
- in_dhcp_data->op);
- goto exit;
- }
-
- /* DHCP options follow the DHCP header. The first 4 bytes of the DHCP
- * options is the DHCP magic cookie followed by the actual DHCP options.
- */
- ovs_be32 magic_cookie = htonl(DHCP_MAGIC_COOKIE);
- if (in_dhcp_ptr + sizeof magic_cookie > end ||
- get_unaligned_be32((const void *) in_dhcp_ptr) != magic_cookie) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "DHCP magic cookie not present in the DHCP packet");
- goto exit;
- }
- in_dhcp_ptr += sizeof magic_cookie;
-
- const uint8_t *in_dhcp_msg_type = NULL;
- ovs_be32 request_ip = in_dhcp_data->ciaddr;
- while (in_dhcp_ptr < end) {
- const struct dhcp_opt_header *in_dhcp_opt =
- (const struct dhcp_opt_header *)in_dhcp_ptr;
- if (in_dhcp_opt->code == DHCP_OPT_END) {
- break;
- }
- if (in_dhcp_opt->code == DHCP_OPT_PAD) {
- in_dhcp_ptr += 1;
- continue;
- }
- in_dhcp_ptr += sizeof *in_dhcp_opt;
- if (in_dhcp_ptr > end) {
- break;
- }
- in_dhcp_ptr += in_dhcp_opt->len;
- if (in_dhcp_ptr > end) {
- break;
- }
-
- switch (in_dhcp_opt->code) {
- case DHCP_OPT_MSG_TYPE:
- if (in_dhcp_opt->len == 1) {
- in_dhcp_msg_type = DHCP_OPT_PAYLOAD(in_dhcp_opt);
- }
- break;
- case DHCP_OPT_REQ_IP:
- if (in_dhcp_opt->len == 4) {
- request_ip = get_unaligned_be32(DHCP_OPT_PAYLOAD(in_dhcp_opt));
- }
- break;
- default:
- break;
- }
- }
-
- /* Check that the DHCP Message Type (opt 53) is present or not with
- * valid values - DHCP_MSG_DISCOVER or DHCP_MSG_REQUEST.
- */
- if (!in_dhcp_msg_type) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Missing DHCP message type");
- goto exit;
- }
- if (*in_dhcp_msg_type != DHCP_MSG_DISCOVER &&
- *in_dhcp_msg_type != DHCP_MSG_REQUEST) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type);
- goto exit;
- }
-
- uint8_t msg_type;
- if (*in_dhcp_msg_type == DHCP_MSG_DISCOVER) {
- msg_type = DHCP_MSG_OFFER;
- } else {
- /* This is a DHCPREQUEST. If the client has requested an IP that
- * does not match the offered IP address, reply with a NAK. The
- * requested IP address may be supplied either via Requested IP Address
- * (opt 50) or via ciaddr, depending on the client's state.
- */
- msg_type = DHCP_MSG_ACK;
- if (request_ip != *offer_ip) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "DHCPREQUEST requested IP "IP_FMT" does not "
- "match offer "IP_FMT, IP_ARGS(request_ip),
- IP_ARGS(*offer_ip));
- msg_type = DHCP_MSG_NAK;
- }
- }
-
- /* Frame the DHCP reply packet
- * Total DHCP options length will be options stored in the userdata +
- * 16 bytes. Note that the DHCP options stored in userdata are not included
- * in DHCPNAK messages.
- *
- * --------------------------------------------------------------
- *| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options |
- * --------------------------------------------------------------
- *| 4 Bytes padding | 1 Byte (option end 0xFF ) | 4 Bytes padding|
- * --------------------------------------------------------------
- */
- uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
- if (msg_type != DHCP_MSG_NAK) {
- new_l4_size += userdata->size;
- }
- size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
-
- struct dp_packet pkt_out;
- dp_packet_init(&pkt_out, new_packet_size);
- dp_packet_clear(&pkt_out);
- dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
- pkt_out_ptr = &pkt_out;
-
- /* Copy the L2 and L3 headers from the pkt_in as they would remain same*/
- dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), pkt_in->l4_ofs);
-
- pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
- pkt_out.l2_pad_size = pkt_in->l2_pad_size;
- pkt_out.l3_ofs = pkt_in->l3_ofs;
- pkt_out.l4_ofs = pkt_in->l4_ofs;
-
- struct udp_header *udp = dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN);
-
- struct dhcp_header *dhcp_data = dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN);
- dhcp_data->op = DHCP_OP_REPLY;
- dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip;
- dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32));
-
- uint16_t out_dhcp_opts_size = 12;
- if (msg_type != DHCP_MSG_NAK) {
- out_dhcp_opts_size += userdata->size;
- }
- uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out,
- out_dhcp_opts_size);
- /* DHCP option - type */
- out_dhcp_opts[0] = DHCP_OPT_MSG_TYPE;
- out_dhcp_opts[1] = 1;
- out_dhcp_opts[2] = msg_type;
- out_dhcp_opts += 3;
-
- if (msg_type != DHCP_MSG_NAK) {
- memcpy(out_dhcp_opts, userdata->data, userdata->size);
- out_dhcp_opts += userdata->size;
- }
-
- /* Padding */
- out_dhcp_opts += 4;
- /* End */
- out_dhcp_opts[0] = DHCP_OPT_END;
-
- udp->udp_len = htons(new_l4_size);
-
- struct ip_header *out_ip = dp_packet_l3(&pkt_out);
- out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
- udp->udp_csum = 0;
- /* Checksum needs to be initialized to zero. */
- out_ip->ip_csum = 0;
- out_ip->ip_csum = csum(out_ip, sizeof *out_ip);
-
- pin->packet = dp_packet_data(&pkt_out);
- pin->packet_len = dp_packet_size(&pkt_out);
-
- /* Log the response. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
- const struct eth_header *l2 = dp_packet_eth(&pkt_out);
- VLOG_INFO_RL(&rl, "DHCP%s "ETH_ADDR_FMT" "IP_FMT"",
- msg_type == DHCP_MSG_OFFER ? "OFFER" :
- (msg_type == DHCP_MSG_ACK ? "ACK": "NAK"),
- ETH_ADDR_ARGS(l2->eth_src), IP_ARGS(*offer_ip));
-
- success = 1;
-exit:
- if (!ofperr) {
- union mf_subvalue sv;
- sv.u8_val = success;
- mf_write_subfield(&dst, &sv, &pin->flow_metadata);
- }
- queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
- if (pkt_out_ptr) {
- dp_packet_uninit(pkt_out_ptr);
- }
-}
-
-static bool
-compose_out_dhcpv6_opts(struct ofpbuf *userdata,
- struct ofpbuf *out_dhcpv6_opts, ovs_be32 iaid)
-{
- while (userdata->size) {
- struct dhcp_opt6_header *userdata_opt = ofpbuf_try_pull(
- userdata, sizeof *userdata_opt);
- if (!userdata_opt) {
- return false;
- }
-
- size_t size = ntohs(userdata_opt->size);
- uint8_t *userdata_opt_data = ofpbuf_try_pull(userdata, size);
- if (!userdata_opt_data) {
- return false;
- }
-
- switch (ntohs(userdata_opt->opt_code)) {
- case DHCPV6_OPT_SERVER_ID_CODE:
- {
- /* The Server Identifier option carries a DUID
- * identifying a server between a client and a server.
- * See RFC 3315 Sec 9 and Sec 22.3.
- *
- * We use DUID Based on Link-layer Address [DUID-LL].
- */
-
- struct dhcpv6_opt_server_id *opt_server_id = ofpbuf_put_zeros(
- out_dhcpv6_opts, sizeof *opt_server_id);
-
- opt_server_id->opt.code = htons(DHCPV6_OPT_SERVER_ID_CODE);
- opt_server_id->opt.len = htons(size + 4);
- opt_server_id->duid_type = htons(DHCPV6_DUID_LL);
- opt_server_id->hw_type = htons(DHCPV6_HW_TYPE_ETH);
- memcpy(&opt_server_id->mac, userdata_opt_data,
- sizeof(struct eth_addr));
- break;
- }
-
- case DHCPV6_OPT_IA_ADDR_CODE:
- {
- if (size != sizeof(struct in6_addr)) {
- return false;
- }
-
- if (!iaid) {
- /* If iaid is None, it means its an DHCPv6 information request.
- * Don't put IA_NA option in the response. */
- break;
- }
- /* IA Address option is used to specify IPv6 addresses associated
- * with an IA_NA or IA_TA. The IA Address option must be
- * encapsulated in the Options field of an IA_NA or IA_TA option.
- *
- * We will encapsulate the IA Address within the IA_NA option.
- * Please see RFC 3315 section 22.5 and 22.6
- */
- struct dhcpv6_opt_ia_na *opt_ia_na = ofpbuf_put_zeros(
- out_dhcpv6_opts, sizeof *opt_ia_na);
- opt_ia_na->opt.code = htons(DHCPV6_OPT_IA_NA_CODE);
- /* IA_NA length (in bytes)-
- * IAID - 4
- * T1 - 4
- * T2 - 4
- * IA Address - sizeof(struct dhcpv6_opt_ia_addr)
- */
- opt_ia_na->opt.len = htons(12 + sizeof(struct dhcpv6_opt_ia_addr));
- opt_ia_na->iaid = iaid;
- /* Set the lifetime of the address(es) to infinity */
- opt_ia_na->t1 = OVS_BE32_MAX;
- opt_ia_na->t2 = OVS_BE32_MAX;
-
- struct dhcpv6_opt_ia_addr *opt_ia_addr = ofpbuf_put_zeros(
- out_dhcpv6_opts, sizeof *opt_ia_addr);
- opt_ia_addr->opt.code = htons(DHCPV6_OPT_IA_ADDR_CODE);
- opt_ia_addr->opt.len = htons(size + 8);
- memcpy(opt_ia_addr->ipv6.s6_addr, userdata_opt_data, size);
- opt_ia_addr->t1 = OVS_BE32_MAX;
- opt_ia_addr->t2 = OVS_BE32_MAX;
- break;
- }
-
- case DHCPV6_OPT_DNS_SERVER_CODE:
- {
- struct dhcpv6_opt_header *opt_dns = ofpbuf_put_zeros(
- out_dhcpv6_opts, sizeof *opt_dns);
- opt_dns->code = htons(DHCPV6_OPT_DNS_SERVER_CODE);
- opt_dns->len = htons(size);
- ofpbuf_put(out_dhcpv6_opts, userdata_opt_data, size);
- break;
- }
-
- case DHCPV6_OPT_DOMAIN_SEARCH_CODE:
- {
- struct dhcpv6_opt_header *opt_dsl = ofpbuf_put_zeros(
- out_dhcpv6_opts, sizeof *opt_dsl);
- opt_dsl->code = htons(DHCPV6_OPT_DOMAIN_SEARCH_CODE);
- opt_dsl->len = htons(size + 2);
- uint8_t *data = ofpbuf_put_zeros(out_dhcpv6_opts, size + 2);
- *data = size;
- memcpy(data + 1, userdata_opt_data, size);
- break;
- }
-
- default:
- return false;
- }
- }
- return true;
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_put_dhcpv6_opts(
- struct rconn *swconn,
- struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
- struct ofpbuf *userdata, struct ofpbuf *continuation OVS_UNUSED)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- struct dp_packet *pkt_out_ptr = NULL;
- uint32_t success = 0;
-
- /* Parse result field. */
- const struct mf_field *f;
- enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
- if (ofperr) {
- VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- /* Parse result offset. */
- ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
- if (!ofsp) {
- VLOG_WARN_RL(&rl, "offset not present in the userdata");
- goto exit;
- }
-
- /* Check that the result is valid and writable. */
- struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 };
- ofperr = mf_check_dst(&dst, NULL);
- if (ofperr) {
- VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- if (!userdata->size) {
- VLOG_WARN_RL(&rl, "DHCPv6 options not present in the userdata");
- goto exit;
- }
-
- struct udp_header *in_udp = dp_packet_l4(pkt_in);
- const uint8_t *in_dhcpv6_data = dp_packet_get_udp_payload(pkt_in);
- if (!in_udp || !in_dhcpv6_data) {
- VLOG_WARN_RL(&rl, "truncated dhcpv6 packet");
- goto exit;
- }
-
- uint8_t out_dhcpv6_msg_type;
- uint8_t in_dhcpv6_msg_type = *in_dhcpv6_data;
- switch (in_dhcpv6_msg_type) {
- case DHCPV6_MSG_TYPE_SOLICIT:
- out_dhcpv6_msg_type = DHCPV6_MSG_TYPE_ADVT;
- break;
-
- case DHCPV6_MSG_TYPE_REQUEST:
- case DHCPV6_MSG_TYPE_CONFIRM:
- case DHCPV6_MSG_TYPE_DECLINE:
- case DHCPV6_MSG_TYPE_INFO_REQ:
- out_dhcpv6_msg_type = DHCPV6_MSG_TYPE_REPLY;
- break;
-
- default:
- /* Invalid or unsupported DHCPv6 message type */
- goto exit;
- }
-
- /* Skip 4 bytes (message type (1 byte) + transaction ID (3 bytes). */
- in_dhcpv6_data += 4;
- /* We need to extract IAID from the IA-NA option of the client's DHCPv6
- * solicit/request/confirm packet and copy the same IAID in the Server's
- * response.
- * DHCPv6 information packet (for stateless request will not have IA-NA
- * option. So we don't need to copy that in the Server's response.
- * */
- ovs_be32 iaid = 0;
- struct dhcpv6_opt_header const *in_opt_client_id = NULL;
- size_t udp_len = ntohs(in_udp->udp_len);
- size_t l4_len = dp_packet_l4_size(pkt_in);
- uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
- while (in_dhcpv6_data < end) {
- struct dhcpv6_opt_header const *in_opt =
- (struct dhcpv6_opt_header *)in_dhcpv6_data;
- switch(ntohs(in_opt->code)) {
- case DHCPV6_OPT_IA_NA_CODE:
- {
- struct dhcpv6_opt_ia_na *opt_ia_na = (
- struct dhcpv6_opt_ia_na *)in_opt;
- iaid = opt_ia_na->iaid;
- break;
- }
-
- case DHCPV6_OPT_CLIENT_ID_CODE:
- in_opt_client_id = in_opt;
- break;
-
- default:
- break;
- }
- in_dhcpv6_data += sizeof *in_opt + ntohs(in_opt->len);
- }
-
- if (!in_opt_client_id) {
- VLOG_WARN_RL(&rl, "DHCPv6 option - Client id not present in the "
- "DHCPv6 packet");
- goto exit;
- }
-
- if (!iaid && in_dhcpv6_msg_type != DHCPV6_MSG_TYPE_INFO_REQ) {
- VLOG_WARN_RL(&rl, "DHCPv6 option - IA NA not present in the "
- "DHCPv6 packet");
- goto exit;
- }
-
- uint64_t out_ofpacts_dhcpv6_opts_stub[256 / 8];
- struct ofpbuf out_dhcpv6_opts =
- OFPBUF_STUB_INITIALIZER(out_ofpacts_dhcpv6_opts_stub);
-
- if (!compose_out_dhcpv6_opts(userdata, &out_dhcpv6_opts, iaid)) {
- VLOG_WARN_RL(&rl, "Invalid userdata");
- goto exit;
- }
-
- uint16_t new_l4_size
- = (UDP_HEADER_LEN + 4 + sizeof *in_opt_client_id +
- ntohs(in_opt_client_id->len) + out_dhcpv6_opts.size);
- size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
-
- struct dp_packet pkt_out;
- dp_packet_init(&pkt_out, new_packet_size);
- dp_packet_clear(&pkt_out);
- dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
- pkt_out_ptr = &pkt_out;
-
- /* Copy L2 and L3 headers from pkt_in. */
- dp_packet_put(&pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs),
- pkt_in->l4_ofs);
-
- pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
- pkt_out.l2_pad_size = pkt_in->l2_pad_size;
- pkt_out.l3_ofs = pkt_in->l3_ofs;
- pkt_out.l4_ofs = pkt_in->l4_ofs;
-
- /* Pull the DHCPv6 message type and transaction id from the pkt_in.
- * Need to preserve the transaction id in the DHCPv6 reply packet. */
- struct udp_header *out_udp = dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN);
- uint8_t *out_dhcpv6 = dp_packet_put(&pkt_out, dp_packet_pull(pkt_in, 4), 4);
-
- /* Set the proper DHCPv6 message type. */
- *out_dhcpv6 = out_dhcpv6_msg_type;
-
- /* Copy the Client Identifier. */
- dp_packet_put(&pkt_out, in_opt_client_id,
- sizeof *in_opt_client_id + ntohs(in_opt_client_id->len));
-
- /* Copy the DHCPv6 Options. */
- dp_packet_put(&pkt_out, out_dhcpv6_opts.data, out_dhcpv6_opts.size);
- out_udp->udp_len = htons(new_l4_size);
- out_udp->udp_csum = 0;
-
- struct ovs_16aligned_ip6_hdr *out_ip6 = dp_packet_l3(&pkt_out);
- out_ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = out_udp->udp_len;
-
- uint32_t csum;
- csum = packet_csum_pseudoheader6(dp_packet_l3(&pkt_out));
- csum = csum_continue(csum, out_udp, dp_packet_size(&pkt_out) -
- ((const unsigned char *)out_udp -
- (const unsigned char *)dp_packet_eth(&pkt_out)));
- out_udp->udp_csum = csum_finish(csum);
- if (!out_udp->udp_csum) {
- out_udp->udp_csum = htons(0xffff);
- }
-
- pin->packet = dp_packet_data(&pkt_out);
- pin->packet_len = dp_packet_size(&pkt_out);
- ofpbuf_uninit(&out_dhcpv6_opts);
- success = 1;
-exit:
- if (!ofperr) {
- union mf_subvalue sv;
- sv.u8_val = success;
- mf_write_subfield(&dst, &sv, &pin->flow_metadata);
- }
- queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
- dp_packet_uninit(pkt_out_ptr);
-}
-
-static void
-put_be16(struct ofpbuf *buf, ovs_be16 x)
-{
- ofpbuf_put(buf, &x, sizeof x);
-}
-
-static void
-put_be32(struct ofpbuf *buf, ovs_be32 x)
-{
- ofpbuf_put(buf, &x, sizeof x);
-}
-
-struct dns_data {
- uint64_t *dps;
- size_t n_dps;
- struct smap records;
- bool delete;
-};
-
-static struct shash dns_cache = SHASH_INITIALIZER(&dns_cache);
-
-/* Called by pinctrl_run(). Runs within the main ovn-controller
- * thread context. */
-static void
-sync_dns_cache(const struct sbrec_dns_table *dns_table)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct shash_node *iter;
- SHASH_FOR_EACH (iter, &dns_cache) {
- struct dns_data *d = iter->data;
- d->delete = true;
- }
-
- const struct sbrec_dns *sbrec_dns;
- SBREC_DNS_TABLE_FOR_EACH (sbrec_dns, dns_table) {
- const char *dns_id = smap_get(&sbrec_dns->external_ids, "dns_id");
- if (!dns_id) {
- continue;
- }
-
- struct dns_data *dns_data = shash_find_data(&dns_cache, dns_id);
- if (!dns_data) {
- dns_data = xmalloc(sizeof *dns_data);
- smap_init(&dns_data->records);
- shash_add(&dns_cache, dns_id, dns_data);
- dns_data->n_dps = 0;
- dns_data->dps = NULL;
- } else {
- free(dns_data->dps);
- }
-
- dns_data->delete = false;
-
- if (!smap_equal(&dns_data->records, &sbrec_dns->records)) {
- smap_clear(&dns_data->records);
- smap_clone(&dns_data->records, &sbrec_dns->records);
- }
-
- dns_data->n_dps = sbrec_dns->n_datapaths;
- dns_data->dps = xcalloc(dns_data->n_dps, sizeof(uint64_t));
- for (size_t i = 0; i < sbrec_dns->n_datapaths; i++) {
- dns_data->dps[i] = sbrec_dns->datapaths[i]->tunnel_key;
- }
- }
-
- struct shash_node *next;
- SHASH_FOR_EACH_SAFE (iter, next, &dns_cache) {
- struct dns_data *d = iter->data;
- if (d->delete) {
- shash_delete(&dns_cache, iter);
- free(d);
- }
- }
-}
-
-static void
-destroy_dns_cache(void)
-{
- struct shash_node *iter, *next;
- SHASH_FOR_EACH_SAFE (iter, next, &dns_cache) {
- struct dns_data *d = iter->data;
- shash_delete(&dns_cache, iter);
- free(d);
- }
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_dns_lookup(
- struct rconn *swconn,
- struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
- struct ofpbuf *userdata, struct ofpbuf *continuation)
- OVS_REQUIRES(pinctrl_mutex)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- struct dp_packet *pkt_out_ptr = NULL;
- uint32_t success = 0;
-
- /* Parse result field. */
- const struct mf_field *f;
- enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
- if (ofperr) {
- VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- /* Parse result offset. */
- ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
- if (!ofsp) {
- VLOG_WARN_RL(&rl, "offset not present in the userdata");
- goto exit;
- }
-
- /* Check that the result is valid and writable. */
- struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 };
- ofperr = mf_check_dst(&dst, NULL);
- if (ofperr) {
- VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- /* Check that the packet stores at least the minimal headers. */
- if (dp_packet_l4_size(pkt_in) < (UDP_HEADER_LEN + DNS_HEADER_LEN)) {
- VLOG_WARN_RL(&rl, "truncated dns packet");
- goto exit;
- }
-
- /* Extract the DNS header */
- struct dns_header const *in_dns_header = dp_packet_get_udp_payload(pkt_in);
- if (!in_dns_header) {
- VLOG_WARN_RL(&rl, "truncated dns packet");
- goto exit;
- }
-
- /* Check if it is DNS request or not */
- if (in_dns_header->lo_flag & 0x80) {
- /* It's a DNS response packet which we are not interested in */
- goto exit;
- }
-
- /* Check if at least one query request is present */
- if (!in_dns_header->qdcount) {
- goto exit;
- }
-
- struct udp_header *in_udp = dp_packet_l4(pkt_in);
- size_t udp_len = ntohs(in_udp->udp_len);
- size_t l4_len = dp_packet_l4_size(pkt_in);
- uint8_t *end = (uint8_t *)in_udp + MIN(udp_len, l4_len);
- uint8_t *in_dns_data = (uint8_t *)(in_dns_header + 1);
- uint8_t *in_queryname = in_dns_data;
- uint16_t idx = 0;
- struct ds query_name;
- ds_init(&query_name);
- /* Extract the query_name. If the query name is - 'www.ovn.org' it would be
- * encoded as (in hex) - 03 77 77 77 03 6f 76 63 03 6f 72 67 00.
- */
- while ((in_dns_data + idx) < end && in_dns_data[idx]) {
- uint8_t label_len = in_dns_data[idx++];
- if (in_dns_data + idx + label_len > end) {
- ds_destroy(&query_name);
- goto exit;
- }
- ds_put_buffer(&query_name, (const char *) in_dns_data + idx, label_len);
- idx += label_len;
- ds_put_char(&query_name, '.');
- }
-
- idx++;
- ds_chomp(&query_name, '.');
- in_dns_data += idx;
-
- /* Query should have TYPE and CLASS fields */
- if (in_dns_data + (2 * sizeof(ovs_be16)) > end) {
- ds_destroy(&query_name);
- goto exit;
- }
-
- uint16_t query_type = ntohs(*ALIGNED_CAST(const ovs_be16 *, in_dns_data));
- /* Supported query types - A, AAAA and ANY */
- if (!(query_type == DNS_QUERY_TYPE_A || query_type == DNS_QUERY_TYPE_AAAA
- || query_type == DNS_QUERY_TYPE_ANY)) {
- ds_destroy(&query_name);
- goto exit;
- }
-
- uint64_t dp_key = ntohll(pin->flow_metadata.flow.metadata);
- const char *answer_ips = NULL;
- struct shash_node *iter;
- SHASH_FOR_EACH (iter, &dns_cache) {
- struct dns_data *d = iter->data;
- for (size_t i = 0; i < d->n_dps; i++) {
- if (d->dps[i] == dp_key) {
- answer_ips = smap_get(&d->records, ds_cstr(&query_name));
- if (answer_ips) {
- break;
- }
- }
- }
-
- if (answer_ips) {
- break;
- }
- }
-
- ds_destroy(&query_name);
- if (!answer_ips) {
- goto exit;
- }
-
- struct lport_addresses ip_addrs;
- if (!extract_ip_addresses(answer_ips, &ip_addrs)) {
- goto exit;
- }
-
- uint16_t ancount = 0;
- uint64_t dns_ans_stub[128 / 8];
- struct ofpbuf dns_answer = OFPBUF_STUB_INITIALIZER(dns_ans_stub);
-
- if (query_type == DNS_QUERY_TYPE_A || query_type == DNS_QUERY_TYPE_ANY) {
- for (size_t i = 0; i < ip_addrs.n_ipv4_addrs; i++) {
- /* Copy the answer section */
- /* Format of the answer section is
- * - NAME -> The domain name
- * - TYPE -> 2 octets containing one of the RR type codes
- * - CLASS -> 2 octets which specify the class of the data
- * in the RDATA field.
- * - TTL -> 32 bit unsigned int specifying the time
- * interval (in secs) that the resource record
- * may be cached before it should be discarded.
- * - RDLENGTH -> 16 bit integer specifying the length of the
- * RDATA field.
- * - RDATA -> a variable length string of octets that
- * describes the resource. In our case it will
- * be IP address of the domain name.
- */
- ofpbuf_put(&dns_answer, in_queryname, idx);
- put_be16(&dns_answer, htons(DNS_QUERY_TYPE_A));
- put_be16(&dns_answer, htons(DNS_CLASS_IN));
- put_be32(&dns_answer, htonl(DNS_DEFAULT_RR_TTL));
- put_be16(&dns_answer, htons(sizeof(ovs_be32)));
- put_be32(&dns_answer, ip_addrs.ipv4_addrs[i].addr);
- ancount++;
- }
- }
-
- if (query_type == DNS_QUERY_TYPE_AAAA ||
- query_type == DNS_QUERY_TYPE_ANY) {
- for (size_t i = 0; i < ip_addrs.n_ipv6_addrs; i++) {
- ofpbuf_put(&dns_answer, in_queryname, idx);
- put_be16(&dns_answer, htons(DNS_QUERY_TYPE_AAAA));
- put_be16(&dns_answer, htons(DNS_CLASS_IN));
- put_be32(&dns_answer, htonl(DNS_DEFAULT_RR_TTL));
- const struct in6_addr *ip6 = &ip_addrs.ipv6_addrs[i].addr;
- put_be16(&dns_answer, htons(sizeof *ip6));
- ofpbuf_put(&dns_answer, ip6, sizeof *ip6);
- ancount++;
- }
- }
-
- destroy_lport_addresses(&ip_addrs);
-
- if (!ancount) {
- ofpbuf_uninit(&dns_answer);
- goto exit;
- }
-
- uint16_t new_l4_size = ntohs(in_udp->udp_len) + dns_answer.size;
- size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
- struct dp_packet pkt_out;
- dp_packet_init(&pkt_out, new_packet_size);
- dp_packet_clear(&pkt_out);
- dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
- pkt_out_ptr = &pkt_out;
-
- /* Copy the L2 and L3 headers from the pkt_in as they would remain same.*/
- dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs), pkt_in->l4_ofs);
-
- pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
- pkt_out.l2_pad_size = pkt_in->l2_pad_size;
- pkt_out.l3_ofs = pkt_in->l3_ofs;
- pkt_out.l4_ofs = pkt_in->l4_ofs;
-
- struct udp_header *out_udp = dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, UDP_HEADER_LEN), UDP_HEADER_LEN);
-
- /* Copy the DNS header. */
- struct dns_header *out_dns_header = dp_packet_put(
- &pkt_out, dp_packet_pull(pkt_in, sizeof *out_dns_header),
- sizeof *out_dns_header);
-
- /* Set the response bit to 1 in the flags. */
- out_dns_header->lo_flag |= 0x80;
-
- /* Set the answer RR. */
- out_dns_header->ancount = htons(ancount);
-
- /* Copy the Query section. */
- dp_packet_put(&pkt_out, dp_packet_data(pkt_in), dp_packet_size(pkt_in));
-
- /* Copy the answer sections. */
- dp_packet_put(&pkt_out, dns_answer.data, dns_answer.size);
- ofpbuf_uninit(&dns_answer);
-
- out_udp->udp_len = htons(new_l4_size);
- out_udp->udp_csum = 0;
-
- struct eth_header *eth = dp_packet_data(&pkt_out);
- if (eth->eth_type == htons(ETH_TYPE_IP)) {
- struct ip_header *out_ip = dp_packet_l3(&pkt_out);
- out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs
- + new_l4_size);
- /* Checksum needs to be initialized to zero. */
- out_ip->ip_csum = 0;
- out_ip->ip_csum = csum(out_ip, sizeof *out_ip);
- } else {
- struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
- nh->ip6_plen = htons(new_l4_size);
-
- /* IPv6 needs UDP checksum calculated */
- uint32_t csum;
- csum = packet_csum_pseudoheader6(nh);
- csum = csum_continue(csum, out_udp, dp_packet_size(&pkt_out) -
- ((const unsigned char *)out_udp -
- (const unsigned char *)eth));
- out_udp->udp_csum = csum_finish(csum);
- if (!out_udp->udp_csum) {
- out_udp->udp_csum = htons(0xffff);
- }
- }
-
- pin->packet = dp_packet_data(&pkt_out);
- pin->packet_len = dp_packet_size(&pkt_out);
-
- success = 1;
-exit:
- if (!ofperr) {
- union mf_subvalue sv;
- sv.u8_val = success;
- mf_write_subfield(&dst, &sv, &pin->flow_metadata);
- }
- queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
- dp_packet_uninit(pkt_out_ptr);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- struct ofputil_packet_in pin;
- struct ofpbuf continuation;
- enum ofperr error = ofputil_decode_packet_in(msg, true, NULL, NULL, &pin,
- NULL, NULL, &continuation);
-
- if (error) {
- VLOG_WARN_RL(&rl, "error decoding packet-in: %s",
- ofperr_to_string(error));
- return;
- }
- if (pin.reason != OFPR_ACTION) {
- return;
- }
-
- struct ofpbuf userdata = ofpbuf_const_initializer(pin.userdata,
- pin.userdata_len);
- const struct action_header *ah = ofpbuf_pull(&userdata, sizeof *ah);
- if (!ah) {
- VLOG_WARN_RL(&rl, "packet-in userdata lacks action header");
- return;
- }
-
- struct dp_packet packet;
- dp_packet_use_const(&packet, pin.packet, pin.packet_len);
- struct flow headers;
- flow_extract(&packet, &headers);
-
- switch (ntohl(ah->opcode)) {
- case ACTION_OPCODE_ARP:
- pinctrl_handle_arp(swconn, &headers, &packet, &pin.flow_metadata,
- &userdata);
- break;
- case ACTION_OPCODE_IGMP:
- pinctrl_ip_mcast_handle_igmp(swconn, &headers, &packet,
- &pin.flow_metadata, &userdata);
- break;
-
- case ACTION_OPCODE_PUT_ARP:
- ovs_mutex_lock(&pinctrl_mutex);
- pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
- true);
- ovs_mutex_unlock(&pinctrl_mutex);
- break;
-
- case ACTION_OPCODE_PUT_DHCP_OPTS:
- pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &userdata,
- &continuation);
- break;
-
- case ACTION_OPCODE_ND_NA:
- pinctrl_handle_nd_na(swconn, &headers, &pin.flow_metadata, &userdata,
- false);
- break;
-
- case ACTION_OPCODE_ND_NA_ROUTER:
- pinctrl_handle_nd_na(swconn, &headers, &pin.flow_metadata, &userdata,
- true);
- break;
-
- case ACTION_OPCODE_PUT_ND:
- ovs_mutex_lock(&pinctrl_mutex);
- pinctrl_handle_put_mac_binding(&pin.flow_metadata.flow, &headers,
- false);
- ovs_mutex_unlock(&pinctrl_mutex);
- break;
-
- case ACTION_OPCODE_PUT_DHCPV6_OPTS:
- pinctrl_handle_put_dhcpv6_opts(swconn, &packet, &pin, &userdata,
- &continuation);
- break;
-
- case ACTION_OPCODE_DNS_LOOKUP:
- ovs_mutex_lock(&pinctrl_mutex);
- pinctrl_handle_dns_lookup(swconn, &packet, &pin, &userdata,
- &continuation);
- ovs_mutex_unlock(&pinctrl_mutex);
- break;
-
- case ACTION_OPCODE_LOG:
- handle_acl_log(&headers, &userdata);
- break;
-
- case ACTION_OPCODE_PUT_ND_RA_OPTS:
- pinctrl_handle_put_nd_ra_opts(swconn, &headers, &packet, &pin,
- &userdata, &continuation);
- break;
-
- case ACTION_OPCODE_ND_NS:
- pinctrl_handle_nd_ns(swconn, &headers, &packet, &pin.flow_metadata,
- &userdata);
- break;
-
- case ACTION_OPCODE_ICMP:
- pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata,
- &userdata, false);
- break;
-
- case ACTION_OPCODE_ICMP4_ERROR:
- pinctrl_handle_icmp(swconn, &headers, &packet, &pin.flow_metadata,
- &userdata, true);
- break;
-
- case ACTION_OPCODE_TCP_RESET:
- pinctrl_handle_tcp_reset(swconn, &headers, &packet, &pin.flow_metadata,
- &userdata);
- break;
-
- case ACTION_OPCODE_PUT_ICMP4_FRAG_MTU:
- pinctrl_handle_put_icmp4_frag_mtu(swconn, &headers, &packet,
- &pin, &userdata, &continuation);
- break;
-
- case ACTION_OPCODE_EVENT:
- ovs_mutex_lock(&pinctrl_mutex);
- pinctrl_handle_event(&userdata);
- ovs_mutex_unlock(&pinctrl_mutex);
- break;
-
- default:
- VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
- ntohl(ah->opcode));
- break;
- }
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_recv(struct rconn *swconn, const struct ofp_header *oh,
- enum ofptype type)
-{
- if (type == OFPTYPE_ECHO_REQUEST) {
- queue_msg(swconn, ofputil_encode_echo_reply(oh));
- } else if (type == OFPTYPE_GET_CONFIG_REPLY) {
- /* Enable asynchronous messages */
- struct ofputil_switch_config config;
-
- ofputil_decode_get_config_reply(oh, &config);
- config.miss_send_len = UINT16_MAX;
- set_switch_config(swconn, &config);
- } else if (type == OFPTYPE_PACKET_IN) {
- process_packet_in(swconn, oh);
- } else {
- if (VLOG_IS_DBG_ENABLED()) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
-
- char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 2);
-
- VLOG_DBG_RL(&rl, "OpenFlow packet ignored: %s", s);
- free(s);
- }
- }
-}
-
-/* Called with in the main ovn-controller thread context. */
-static void
-notify_pinctrl_handler(void)
-{
- seq_change(pinctrl_handler_seq);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-notify_pinctrl_main(void)
-{
- seq_change(pinctrl_main_seq);
-}
-
-/* pinctrl_handler pthread function. */
-static void *
-pinctrl_handler(void *arg_)
-{
- struct pinctrl *pctrl = arg_;
- /* OpenFlow connection to the switch. */
- struct rconn *swconn;
- /* Last seen sequence number for 'swconn'. When this differs from
- * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
- unsigned int conn_seq_no = 0;
-
- char *br_int_name = NULL;
- uint64_t new_seq;
-
- /* Next IPV6 RA in seconds. */
- static long long int send_ipv6_ra_time = LLONG_MAX;
- /* Next GARP announcement in ms. */
- static long long int send_garp_time = LLONG_MAX;
- /* Next multicast query (IGMP) in ms. */
- static long long int send_mcast_query_time = LLONG_MAX;
-
- swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
-
- while (!latch_is_set(&pctrl->pinctrl_thread_exit)) {
- if (pctrl->br_int_name) {
- if (!br_int_name || strcmp(br_int_name, pctrl->br_int_name)) {
- free(br_int_name);
- br_int_name = xstrdup(pctrl->br_int_name);
- }
- }
-
- if (br_int_name) {
- char *target;
-
- target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(), br_int_name);
- if (strcmp(target, rconn_get_target(swconn))) {
- VLOG_INFO("%s: connecting to switch", target);
- rconn_connect(swconn, target, target);
- }
- free(target);
- } else {
- rconn_disconnect(swconn);
- }
-
- ovs_mutex_lock(&pinctrl_mutex);
- ip_mcast_snoop_run();
- ovs_mutex_unlock(&pinctrl_mutex);
-
- rconn_run(swconn);
- if (rconn_is_connected(swconn)) {
- if (conn_seq_no != rconn_get_connection_seqno(swconn)) {
- pinctrl_setup(swconn);
- conn_seq_no = rconn_get_connection_seqno(swconn);
- }
-
- for (int i = 0; i < 50; i++) {
- struct ofpbuf *msg = rconn_recv(swconn);
- if (!msg) {
- break;
- }
-
- const struct ofp_header *oh = msg->data;
- enum ofptype type;
-
- ofptype_decode(&type, oh);
- pinctrl_recv(swconn, oh, type);
- ofpbuf_delete(msg);
- }
-
- if (may_inject_pkts()) {
- ovs_mutex_lock(&pinctrl_mutex);
- send_garp_run(swconn, &send_garp_time);
- send_ipv6_ras(swconn, &send_ipv6_ra_time);
- send_mac_binding_buffered_pkts(swconn);
- ovs_mutex_unlock(&pinctrl_mutex);
-
- ip_mcast_querier_run(swconn, &send_mcast_query_time);
- }
- }
-
- rconn_run_wait(swconn);
- rconn_recv_wait(swconn);
- send_garp_wait(send_garp_time);
- ipv6_ra_wait(send_ipv6_ra_time);
- ip_mcast_querier_wait(send_mcast_query_time);
-
- new_seq = seq_read(pinctrl_handler_seq);
- seq_wait(pinctrl_handler_seq, new_seq);
-
- latch_wait(&pctrl->pinctrl_thread_exit);
- poll_block();
- }
-
- free(br_int_name);
- rconn_destroy(swconn);
- return NULL;
-}
-
-/* Called by ovn-controller. */
-void
-pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
- struct ovsdb_idl_index *sbrec_igmp_groups,
- struct ovsdb_idl_index *sbrec_ip_multicast_opts,
- const struct sbrec_dns_table *dns_table,
- const struct sbrec_controller_event_table *ce_table,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- const struct sset *active_tunnels)
-{
- ovs_mutex_lock(&pinctrl_mutex);
- if (br_int && (!pinctrl.br_int_name || strcmp(pinctrl.br_int_name,
- br_int->name))) {
- if (pinctrl.br_int_name) {
- free(pinctrl.br_int_name);
- }
- pinctrl.br_int_name = xstrdup(br_int->name);
- /* Notify pinctrl_handler that integration bridge is
- * set/changed. */
- notify_pinctrl_handler();
- }
- run_put_mac_bindings(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_key,
- sbrec_mac_binding_by_lport_ip);
- send_garp_prepare(sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name, br_int, chassis,
- local_datapaths, active_tunnels);
- prepare_ipv6_ras(sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name, local_datapaths);
- sync_dns_cache(dns_table);
- controller_event_run(ovnsb_idl_txn, ce_table, chassis);
- ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths,
- sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_key,
- sbrec_igmp_groups,
- sbrec_ip_multicast_opts);
- run_buffered_binding(sbrec_port_binding_by_datapath,
- sbrec_mac_binding_by_lport_ip,
- local_datapaths);
- ovs_mutex_unlock(&pinctrl_mutex);
-}
-
-/* Table of ipv6_ra_state structures, keyed on logical port name.
- * Protected by pinctrl_mutex. */
-static struct shash ipv6_ras;
-
-struct ipv6_ra_config {
- time_t min_interval;
- time_t max_interval;
- struct eth_addr eth_src;
- struct eth_addr eth_dst;
- struct in6_addr ipv6_src;
- struct in6_addr ipv6_dst;
- int32_t mtu;
- uint8_t mo_flags; /* Managed/Other flags for RAs */
- uint8_t la_flags; /* On-link/autonomous flags for address prefixes */
- struct lport_addresses prefixes;
-};
-
-struct ipv6_ra_state {
- long long int next_announce;
- struct ipv6_ra_config *config;
- int64_t port_key;
- int64_t metadata;
- bool delete_me;
-};
-
-static void
-init_ipv6_ras(void)
-{
- shash_init(&ipv6_ras);
-}
-
-static void
-ipv6_ra_config_delete(struct ipv6_ra_config *config)
-{
- if (config) {
- destroy_lport_addresses(&config->prefixes);
- free(config);
- }
-}
-
-static void
-ipv6_ra_delete(struct ipv6_ra_state *ra)
-{
- if (ra) {
- ipv6_ra_config_delete(ra->config);
- free(ra);
- }
-}
-
-static void
-destroy_ipv6_ras(void)
-{
- struct shash_node *iter, *next;
- SHASH_FOR_EACH_SAFE (iter, next, &ipv6_ras) {
- struct ipv6_ra_state *ra = iter->data;
- ipv6_ra_delete(ra);
- shash_delete(&ipv6_ras, iter);
- }
- shash_destroy(&ipv6_ras);
-}
-
-static struct ipv6_ra_config *
-ipv6_ra_update_config(const struct sbrec_port_binding *pb)
-{
- struct ipv6_ra_config *config;
-
- config = xzalloc(sizeof *config);
-
- config->max_interval = smap_get_int(&pb->options, "ipv6_ra_max_interval",
- ND_RA_MAX_INTERVAL_DEFAULT);
- config->min_interval = smap_get_int(&pb->options, "ipv6_ra_min_interval",
- nd_ra_min_interval_default(config->max_interval));
- config->mtu = smap_get_int(&pb->options, "ipv6_ra_mtu", ND_MTU_DEFAULT);
- config->la_flags = IPV6_ND_RA_OPT_PREFIX_ON_LINK;
-
- const char *address_mode = smap_get(&pb->options, "ipv6_ra_address_mode");
- if (!address_mode) {
- VLOG_WARN("No address mode specified");
- goto fail;
- }
- if (!strcmp(address_mode, "dhcpv6_stateless")) {
- config->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
- config->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS;
- } else if (!strcmp(address_mode, "dhcpv6_stateful")) {
- config->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
- } else if (!strcmp(address_mode, "slaac")) {
- config->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS;
- } else {
- VLOG_WARN("Invalid address mode %s", address_mode);
- goto fail;
- }
-
- const char *prefixes = smap_get(&pb->options, "ipv6_ra_prefixes");
- if (prefixes && !extract_ip_addresses(prefixes, &config->prefixes)) {
- VLOG_WARN("Invalid IPv6 prefixes: %s", prefixes);
- goto fail;
- }
-
- /* All nodes multicast addresses */
- config->eth_dst = (struct eth_addr) ETH_ADDR_C(33,33,00,00,00,01);
- ipv6_parse("ff02::1", &config->ipv6_dst);
-
- const char *eth_addr = smap_get(&pb->options, "ipv6_ra_src_eth");
- if (!eth_addr || !eth_addr_from_string(eth_addr, &config->eth_src)) {
- VLOG_WARN("Invalid ethernet source %s", eth_addr);
- goto fail;
- }
- const char *ip_addr = smap_get(&pb->options, "ipv6_ra_src_addr");
- if (!ip_addr || !ipv6_parse(ip_addr, &config->ipv6_src)) {
- VLOG_WARN("Invalid IP source %s", ip_addr);
- goto fail;
- }
-
- return config;
-
-fail:
- ipv6_ra_config_delete(config);
- return NULL;
-}
-
-static long long int
-ipv6_ra_calc_next_announce(time_t min_interval, time_t max_interval)
-{
- long long int min_interval_ms = min_interval * 1000LL;
- long long int max_interval_ms = max_interval * 1000LL;
-
- return time_msec() + min_interval_ms +
- random_range(max_interval_ms - min_interval_ms);
-}
-
-static void
-put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
- mf_from_id(dst), NULL,
- NULL);
- ovs_be64 n_value = htonll(value);
- bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
- bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static long long int
-ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra)
-{
- if (time_msec() < ra->next_announce) {
- return ra->next_announce;
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- compose_nd_ra(&packet, ra->config->eth_src, ra->config->eth_dst,
- &ra->config->ipv6_src, &ra->config->ipv6_dst,
- 255, ra->config->mo_flags, htons(IPV6_ND_RA_LIFETIME), 0, 0,
- ra->config->mtu);
-
- for (int i = 0; i < ra->config->prefixes.n_ipv6_addrs; i++) {
- ovs_be128 addr;
- memcpy(&addr, &ra->config->prefixes.ipv6_addrs[i].addr, sizeof addr);
- packet_put_ra_prefix_opt(&packet,
- ra->config->prefixes.ipv6_addrs[i].plen,
- ra->config->la_flags, htonl(IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME),
- htonl(IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME), addr);
- }
-
- uint64_t ofpacts_stub[4096 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
-
- /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
- uint32_t dp_key = ra->metadata;
- uint32_t port_key = ra->port_key;
- put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
- put_load(port_key, MFF_LOG_INPORT, 0, 32, &ofpacts);
- put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY_BIT, 1, &ofpacts);
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
- resubmit->in_port = OFPP_CONTROLLER;
- resubmit->table_id = OFTABLE_LOG_INGRESS_PIPELINE;
-
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(&packet),
- .packet_len = dp_packet_size(&packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
-
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
- dp_packet_uninit(&packet);
- ofpbuf_uninit(&ofpacts);
-
- ra->next_announce = ipv6_ra_calc_next_announce(ra->config->min_interval,
- ra->config->max_interval);
-
- return ra->next_announce;
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-ipv6_ra_wait(long long int send_ipv6_ra_time)
-{
- /* Set the poll timer for next IPv6 RA only if IPv6 RAs needs to
- * be sent. */
- if (!shash_is_empty(&ipv6_ras)) {
- poll_timer_wait_until(send_ipv6_ra_time);
- }
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-send_ipv6_ras(struct rconn *swconn, long long int *send_ipv6_ra_time)
- OVS_REQUIRES(pinctrl_mutex)
-{
- *send_ipv6_ra_time = LLONG_MAX;
- struct shash_node *iter;
- SHASH_FOR_EACH (iter, &ipv6_ras) {
- struct ipv6_ra_state *ra = iter->data;
- long long int next_ra = ipv6_ra_send(swconn, ra);
- if (*send_ipv6_ra_time > next_ra) {
- *send_ipv6_ra_time = next_ra;
- }
- }
-}
-
-/* Called by pinctrl_run(). Runs with in the main ovn-controller
- * thread context. */
-static void
-prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct hmap *local_datapaths)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct shash_node *iter, *iter_next;
-
- SHASH_FOR_EACH (iter, &ipv6_ras) {
- struct ipv6_ra_state *ra = iter->data;
- ra->delete_me = true;
- }
-
- bool changed = false;
- const struct local_datapath *ld;
- HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
- struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
- sbrec_port_binding_by_datapath);
- sbrec_port_binding_index_set_datapath(target, ld->datapath);
-
- struct sbrec_port_binding *pb;
- SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
- sbrec_port_binding_by_datapath) {
- if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) {
- continue;
- }
-
- const char *peer_s = smap_get(&pb->options, "peer");
- if (!peer_s) {
- continue;
- }
-
- const struct sbrec_port_binding *peer
- = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
- if (!peer) {
- continue;
- }
-
- struct ipv6_ra_config *config = ipv6_ra_update_config(pb);
- if (!config) {
- continue;
- }
-
- struct ipv6_ra_state *ra
- = shash_find_data(&ipv6_ras, pb->logical_port);
- if (!ra) {
- ra = xzalloc(sizeof *ra);
- ra->config = config;
- ra->next_announce = ipv6_ra_calc_next_announce(
- ra->config->min_interval,
- ra->config->max_interval);
- shash_add(&ipv6_ras, pb->logical_port, ra);
- changed = true;
- } else {
- if (config->min_interval != ra->config->min_interval ||
- config->max_interval != ra->config->max_interval)
- ra->next_announce = ipv6_ra_calc_next_announce(
- config->min_interval,
- config->max_interval);
- ipv6_ra_config_delete(ra->config);
- ra->config = config;
- }
-
- /* Peer is the logical switch port that the logical
- * router port is connected to. The RA is injected
- * into that logical switch port.
- */
- ra->port_key = peer->tunnel_key;
- ra->metadata = peer->datapath->tunnel_key;
- ra->delete_me = false;
-
- /* pinctrl_handler thread will send the IPv6 RAs. */
- }
- sbrec_port_binding_index_destroy_row(target);
- }
-
- /* Remove those that are no longer in the SB database */
- SHASH_FOR_EACH_SAFE (iter, iter_next, &ipv6_ras) {
- struct ipv6_ra_state *ra = iter->data;
- if (ra->delete_me) {
- shash_delete(&ipv6_ras, iter);
- ipv6_ra_delete(ra);
- }
- }
-
- if (changed) {
- notify_pinctrl_handler();
- }
-
-}
-
-/* Called by pinctrl_run(). Runs with in the main ovn-controller
- * thread context. */
-void
-pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn)
-{
- wait_put_mac_bindings(ovnsb_idl_txn);
- wait_controller_event(ovnsb_idl_txn);
- int64_t new_seq = seq_read(pinctrl_main_seq);
- seq_wait(pinctrl_main_seq, new_seq);
-}
-
-/* Called by ovn-controller. */
-void
-pinctrl_destroy(void)
-{
- latch_set(&pinctrl.pinctrl_thread_exit);
- pthread_join(pinctrl.pinctrl_thread, NULL);
- latch_destroy(&pinctrl.pinctrl_thread_exit);
- free(pinctrl.br_int_name);
- destroy_send_garps();
- destroy_ipv6_ras();
- destroy_buffered_packets_map();
- event_table_destroy();
- destroy_put_mac_bindings();
- destroy_dns_cache();
- ip_mcast_snoop_destroy();
- seq_destroy(pinctrl_main_seq);
- seq_destroy(pinctrl_handler_seq);
-}
-
-/* Implementation of the "put_arp" and "put_nd" OVN actions. These
- * actions send a packet to ovn-controller, using the flow as an API
- * (see actions.h for details). This code implements the actions by
- * updating the MAC_Binding table in the southbound database.
- *
- * This code could be a lot simpler if the database could always be updated,
- * but in fact we can only update it when 'ovnsb_idl_txn' is nonnull. Thus,
- * we buffer up a few put_mac_bindings (but we don't keep them longer
- * than 1 second) and apply them whenever a database transaction is
- * available. */
-
-/* Buffered "put_mac_binding" operation. */
-struct put_mac_binding {
- struct hmap_node hmap_node; /* In 'put_mac_bindings'. */
-
- /* Key. */
- uint32_t dp_key;
- uint32_t port_key;
- struct in6_addr ip_key;
-
- /* Value. */
- struct eth_addr mac;
-};
-
-/* Contains "struct put_mac_binding"s. */
-static struct hmap put_mac_bindings;
-
-static void
-init_put_mac_bindings(void)
-{
- hmap_init(&put_mac_bindings);
-}
-
-static void
-destroy_put_mac_bindings(void)
-{
- flush_put_mac_bindings();
- hmap_destroy(&put_mac_bindings);
-}
-
-static struct put_mac_binding *
-pinctrl_find_put_mac_binding(uint32_t dp_key, uint32_t port_key,
- const struct in6_addr *ip_key, uint32_t hash)
-{
- struct put_mac_binding *pa;
- HMAP_FOR_EACH_WITH_HASH (pa, hmap_node, hash, &put_mac_bindings) {
- if (pa->dp_key == dp_key
- && pa->port_key == port_key
- && IN6_ARE_ADDR_EQUAL(&pa->ip_key, ip_key)) {
- return pa;
- }
- }
- return NULL;
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_put_mac_binding(const struct flow *md,
- const struct flow *headers,
- bool is_arp)
- OVS_REQUIRES(pinctrl_mutex)
-{
- uint32_t dp_key = ntohll(md->metadata);
- uint32_t port_key = md->regs[MFF_LOG_INPORT - MFF_REG0];
- struct in6_addr ip_key;
-
- if (is_arp) {
- ip_key = in6_addr_mapped_ipv4(htonl(md->regs[0]));
- } else {
- ovs_be128 ip6 = hton128(flow_get_xxreg(md, 0));
- memcpy(&ip_key, &ip6, sizeof ip_key);
- }
- uint32_t hash = hash_bytes(&ip_key, sizeof ip_key,
- hash_2words(dp_key, port_key));
- struct put_mac_binding *pmb
- = pinctrl_find_put_mac_binding(dp_key, port_key, &ip_key, hash);
- if (!pmb) {
- if (hmap_count(&put_mac_bindings) >= 1000) {
- COVERAGE_INC(pinctrl_drop_put_mac_binding);
- return;
- }
-
- pmb = xmalloc(sizeof *pmb);
- hmap_insert(&put_mac_bindings, &pmb->hmap_node, hash);
- pmb->dp_key = dp_key;
- pmb->port_key = port_key;
- pmb->ip_key = ip_key;
- }
- pmb->mac = headers->dl_src;
-
- /* We can send the buffered packet once the main ovn-controller
- * thread calls pinctrl_run() and it writes the mac_bindings stored
- * in 'put_mac_bindings' hmap into the Southbound MAC_Binding table. */
- notify_pinctrl_main();
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-send_mac_binding_buffered_pkts(struct rconn *swconn)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct buffered_packets *bp;
- LIST_FOR_EACH_POP (bp, list, &buffered_mac_bindings) {
- buffered_send_packets(swconn, bp, &bp->ea);
- free(bp);
- }
- ovs_list_init(&buffered_mac_bindings);
-}
-
-static const struct sbrec_mac_binding *
-mac_binding_lookup(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
- const char *logical_port,
- const char *ip)
-{
- struct sbrec_mac_binding *mb = sbrec_mac_binding_index_init_row(
- sbrec_mac_binding_by_lport_ip);
- sbrec_mac_binding_index_set_logical_port(mb, logical_port);
- sbrec_mac_binding_index_set_ip(mb, ip);
-
- const struct sbrec_mac_binding *retval
- = sbrec_mac_binding_index_find(sbrec_mac_binding_by_lport_ip,
- mb);
-
- sbrec_mac_binding_index_destroy_row(mb);
-
- return retval;
-}
-
-static void
-run_put_mac_binding(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
- const struct put_mac_binding *pmb)
-{
- /* Convert logical datapath and logical port key into lport. */
- const struct sbrec_port_binding *pb = lport_lookup_by_key(
- sbrec_datapath_binding_by_key, sbrec_port_binding_by_key,
- pmb->dp_key, pmb->port_key);
- if (!pb) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- VLOG_WARN_RL(&rl, "unknown logical port with datapath %"PRIu32" "
- "and port %"PRIu32, pmb->dp_key, pmb->port_key);
- return;
- }
-
- /* Convert ethernet argument to string form for database. */
- char mac_string[ETH_ADDR_STRLEN + 1];
- snprintf(mac_string, sizeof mac_string,
- ETH_ADDR_FMT, ETH_ADDR_ARGS(pmb->mac));
-
- struct ds ip_s = DS_EMPTY_INITIALIZER;
- ipv6_format_mapped(&pmb->ip_key, &ip_s);
-
- /* Update or add an IP-MAC binding for this logical port. */
- const struct sbrec_mac_binding *b =
- mac_binding_lookup(sbrec_mac_binding_by_lport_ip, pb->logical_port,
- ds_cstr(&ip_s));
- if (!b) {
- b = sbrec_mac_binding_insert(ovnsb_idl_txn);
- sbrec_mac_binding_set_logical_port(b, pb->logical_port);
- sbrec_mac_binding_set_ip(b, ds_cstr(&ip_s));
- sbrec_mac_binding_set_mac(b, mac_string);
- sbrec_mac_binding_set_datapath(b, pb->datapath);
- } else if (strcmp(b->mac, mac_string)) {
- sbrec_mac_binding_set_mac(b, mac_string);
- }
- ds_destroy(&ip_s);
-}
-
-/* Called by pinctrl_run(). Runs with in the main ovn-controller
- * thread context. */
-static void
-run_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip)
- OVS_REQUIRES(pinctrl_mutex)
-{
- if (!ovnsb_idl_txn) {
- return;
- }
-
- const struct put_mac_binding *pmb;
- HMAP_FOR_EACH (pmb, hmap_node, &put_mac_bindings) {
- run_put_mac_binding(ovnsb_idl_txn, sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_key,
- sbrec_mac_binding_by_lport_ip,
- pmb);
- }
- flush_put_mac_bindings();
-}
-
-static void
-run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
- const struct hmap *local_datapaths)
- OVS_REQUIRES(pinctrl_mutex)
-{
- const struct local_datapath *ld;
- bool notify = false;
-
- HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
- struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
- sbrec_port_binding_by_datapath);
- sbrec_port_binding_index_set_datapath(target, ld->datapath);
-
- const struct sbrec_port_binding *pb;
- SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
- sbrec_port_binding_by_datapath) {
- struct buffered_packets *cur_qp, *next_qp;
- HMAP_FOR_EACH_SAFE (cur_qp, next_qp, hmap_node,
- &buffered_packets_map) {
- struct ds ip_s = DS_EMPTY_INITIALIZER;
- ipv6_format_mapped(&cur_qp->ip, &ip_s);
- const struct sbrec_mac_binding *b = mac_binding_lookup(
- sbrec_mac_binding_by_lport_ip, pb->logical_port,
- ds_cstr(&ip_s));
- if (b && ovs_scan(b->mac, ETH_ADDR_SCAN_FMT,
- ETH_ADDR_SCAN_ARGS(cur_qp->ea))) {
- hmap_remove(&buffered_packets_map, &cur_qp->hmap_node);
- ovs_list_push_back(&buffered_mac_bindings, &cur_qp->list);
- notify = true;
- }
- ds_destroy(&ip_s);
- }
- }
- sbrec_port_binding_index_destroy_row(target);
- }
- buffered_packets_map_gc();
-
- if (notify) {
- notify_pinctrl_handler();
- }
-}
-
-static void
-wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn)
-{
- if (ovnsb_idl_txn && !hmap_is_empty(&put_mac_bindings)) {
- poll_immediate_wake();
- }
-}
-
-static void
-flush_put_mac_bindings(void)
-{
- struct put_mac_binding *pmb;
- HMAP_FOR_EACH_POP (pmb, hmap_node, &put_mac_bindings) {
- free(pmb);
- }
-}
-
-/*
- * Send gratuitous ARP for vif on localnet.
- *
- * When a new vif on localnet is added, gratuitous ARPs are sent announcing
- * the port's mac,ip mapping. On localnet, such announcements are needed for
- * switches and routers on the broadcast segment to update their port-mac
- * and ARP tables.
- */
-struct garp_data {
- struct eth_addr ea; /* Ethernet address of port. */
- ovs_be32 ipv4; /* Ipv4 address of port. */
- long long int announce_time; /* Next announcement in ms. */
- int backoff; /* Backoff for the next announcement. */
- uint32_t dp_key; /* Datapath used to output this GARP. */
- uint32_t port_key; /* Port to inject the GARP into. */
-};
-
-/* Contains GARPs to be sent. Protected by pinctrl_mutex*/
-static struct shash send_garp_data;
-
-static void
-init_send_garps(void)
-{
- shash_init(&send_garp_data);
-}
-
-static void
-destroy_send_garps(void)
-{
- shash_destroy_free_data(&send_garp_data);
-}
-
-/* Runs with in the main ovn-controller thread context. */
-static void
-add_garp(const char *name, const struct eth_addr ea, ovs_be32 ip,
- uint32_t dp_key, uint32_t port_key)
-{
- struct garp_data *garp = xmalloc(sizeof *garp);
- garp->ea = ea;
- garp->ipv4 = ip;
- garp->announce_time = time_msec() + 1000;
- garp->backoff = 1;
- garp->dp_key = dp_key;
- garp->port_key = port_key;
- shash_add(&send_garp_data, name, garp);
-
- /* Notify pinctrl_handler so that it can wakeup and process
- * these GARP requests. */
- notify_pinctrl_handler();
-}
-
-/* Add or update a vif for which GARPs need to be announced. */
-static void
-send_garp_update(const struct sbrec_port_binding *binding_rec,
- struct shash *nat_addresses)
-{
- volatile struct garp_data *garp = NULL;
- /* Update GARP for NAT IP if it exists. Consider port bindings with type
- * "l3gateway" for logical switch ports attached to gateway routers, and
- * port bindings with type "patch" for logical switch ports attached to
- * distributed gateway ports. */
- if (!strcmp(binding_rec->type, "l3gateway")
- || !strcmp(binding_rec->type, "patch")) {
- struct lport_addresses *laddrs = NULL;
- while ((laddrs = shash_find_and_delete(nat_addresses,
- binding_rec->logical_port))) {
- int i;
- for (i = 0; i < laddrs->n_ipv4_addrs; i++) {
- char *name = xasprintf("%s-%s", binding_rec->logical_port,
- laddrs->ipv4_addrs[i].addr_s);
- garp = shash_find_data(&send_garp_data, name);
- if (garp) {
- garp->dp_key = binding_rec->datapath->tunnel_key;
- garp->port_key = binding_rec->tunnel_key;
- } else {
- add_garp(name, laddrs->ea,
- laddrs->ipv4_addrs[i].addr,
- binding_rec->datapath->tunnel_key,
- binding_rec->tunnel_key);
- }
- free(name);
- }
- destroy_lport_addresses(laddrs);
- free(laddrs);
- }
- return;
- }
-
- /* Update GARP for vif if it exists. */
- garp = shash_find_data(&send_garp_data, binding_rec->logical_port);
- if (garp) {
- garp->dp_key = binding_rec->datapath->tunnel_key;
- garp->port_key = binding_rec->tunnel_key;
- return;
- }
-
- /* Add GARP for new vif. */
- int i;
- for (i = 0; i < binding_rec->n_mac; i++) {
- struct lport_addresses laddrs;
- if (!extract_lsp_addresses(binding_rec->mac[i], &laddrs)
- || !laddrs.n_ipv4_addrs) {
- continue;
- }
-
- add_garp(binding_rec->logical_port,
- laddrs.ea, laddrs.ipv4_addrs[0].addr,
- binding_rec->datapath->tunnel_key, binding_rec->tunnel_key);
-
- destroy_lport_addresses(&laddrs);
- break;
- }
-}
-
-/* Remove a vif from GARP announcements. */
-static void
-send_garp_delete(const char *lport)
-{
- struct garp_data *garp = shash_find_and_delete(&send_garp_data, lport);
- free(garp);
- notify_pinctrl_handler();
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static long long int
-send_garp(struct rconn *swconn, struct garp_data *garp,
- long long int current_time)
- OVS_REQUIRES(pinctrl_mutex)
-{
- if (current_time < garp->announce_time) {
- return garp->announce_time;
- }
-
- /* Compose a GARP request packet. */
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- compose_arp(&packet, ARP_OP_REQUEST, garp->ea, eth_addr_zero,
- true, garp->ipv4, garp->ipv4);
-
- /* Inject GARP request. */
- uint64_t ofpacts_stub[4096 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- enum ofp_version version = rconn_get_version(swconn);
- put_load(garp->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
- put_load(garp->port_key, MFF_LOG_INPORT, 0, 32, &ofpacts);
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
- resubmit->in_port = OFPP_CONTROLLER;
- resubmit->table_id = OFTABLE_LOG_INGRESS_PIPELINE;
-
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(&packet),
- .packet_len = dp_packet_size(&packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
- dp_packet_uninit(&packet);
- ofpbuf_uninit(&ofpacts);
-
- /* Set the next announcement. At most 5 announcements are sent for a
- * vif. */
- if (garp->backoff < 16) {
- garp->backoff *= 2;
- garp->announce_time = current_time + garp->backoff * 1000;
- } else {
- garp->announce_time = LLONG_MAX;
- }
- return garp->announce_time;
-}
-
-/*
- * Multicast snooping configuration.
- */
-struct ip_mcast_snoop_cfg {
- bool enabled;
- bool querier_enabled;
-
- uint32_t table_size; /* Max number of allowed multicast groups. */
- uint32_t idle_time_s; /* Idle timeout for multicast groups. */
- uint32_t query_interval_s; /* Multicast query interval. */
- uint32_t query_max_resp_s; /* Multicast query max-response field. */
- uint32_t seq_no; /* Used for flushing learnt groups. */
-
- struct eth_addr query_eth_src; /* Src ETH address used for queries. */
- struct eth_addr query_eth_dst; /* Dst ETH address used for queries. */
- ovs_be32 query_ipv4_src; /* Src IPv4 address used for queries. */
- ovs_be32 query_ipv4_dst; /* Dsc IPv4 address used for queries. */
-};
-
-/*
- * Holds per-datapath information about multicast snooping. Maintained by
- * pinctrl_handler().
- */
-struct ip_mcast_snoop {
- struct hmap_node hmap_node; /* Linkage in the hash map. */
- struct ovs_list query_node; /* Linkage in the query list. */
- struct ip_mcast_snoop_cfg cfg; /* Multicast configuration. */
- struct mcast_snooping *ms; /* Multicast group state. */
- int64_t dp_key; /* Datapath running the snooping. */
-
- long long int query_time_ms; /* Next query time in ms. */
-};
-
-/*
- * Holds the per-datapath multicast configuration state. Maintained by
- * pinctrl_run().
- */
-struct ip_mcast_snoop_state {
- struct hmap_node hmap_node;
- int64_t dp_key;
- struct ip_mcast_snoop_cfg cfg;
-};
-
-/* Only default vlan supported for now. */
-#define IP_MCAST_VLAN 1
-
-/* Multicast snooping information stored independently by datapath key.
- * Protected by pinctrl_mutex. pinctrl_handler has RW access and pinctrl_main
- * has RO access.
- */
-static struct hmap mcast_snoop_map OVS_GUARDED_BY(pinctrl_mutex);
-
-/* Contains multicast queries to be sent. Only used by pinctrl_handler so no
- * locking needed.
- */
-static struct ovs_list mcast_query_list;
-
-/* Multicast config information stored independently by datapath key.
- * Protected by pinctrl_mutex. pinctrl_handler has RO access and pinctrl_main
- * has RW access. Read accesses from pinctrl_ip_mcast_handle_igmp() can be
- * performed without taking the lock as they are executed in the pinctrl_main
- * thread.
- */
-static struct hmap mcast_cfg_map OVS_GUARDED_BY(pinctrl_mutex);
-
-static void
-ip_mcast_snoop_cfg_load(struct ip_mcast_snoop_cfg *cfg,
- const struct sbrec_ip_multicast *ip_mcast)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- memset(cfg, 0, sizeof *cfg);
- cfg->enabled =
- (ip_mcast->enabled && ip_mcast->enabled[0]);
- cfg->querier_enabled =
- (cfg->enabled && ip_mcast->querier && ip_mcast->querier[0]);
-
- if (ip_mcast->table_size) {
- cfg->table_size = ip_mcast->table_size[0];
- } else {
- cfg->table_size = OVN_MCAST_DEFAULT_MAX_ENTRIES;
- }
-
- if (ip_mcast->idle_timeout) {
- cfg->idle_time_s = ip_mcast->idle_timeout[0];
- } else {
- cfg->idle_time_s = OVN_MCAST_DEFAULT_IDLE_TIMEOUT_S;
- }
-
- if (ip_mcast->query_interval) {
- cfg->query_interval_s = ip_mcast->query_interval[0];
- } else {
- cfg->query_interval_s = cfg->idle_time_s / 2;
- if (cfg->query_interval_s < OVN_MCAST_MIN_QUERY_INTERVAL_S) {
- cfg->query_interval_s = OVN_MCAST_MIN_QUERY_INTERVAL_S;
- }
- }
-
- if (ip_mcast->query_max_resp) {
- cfg->query_max_resp_s = ip_mcast->query_max_resp[0];
- } else {
- cfg->query_max_resp_s = OVN_MCAST_DEFAULT_QUERY_MAX_RESPONSE_S;
- }
-
- cfg->seq_no = ip_mcast->seq_no;
-
- if (cfg->querier_enabled) {
- /* Try to parse the source ETH address. */
- if (!ip_mcast->eth_src ||
- !eth_addr_from_string(ip_mcast->eth_src,
- &cfg->query_eth_src)) {
- VLOG_WARN_RL(&rl,
- "IGMP Querier enabled with invalid ETH src address");
- /* Failed to parse the IPv4 source address. Disable the querier. */
- cfg->querier_enabled = false;
- }
-
- /* Try to parse the source IP address. */
- if (!ip_mcast->ip4_src ||
- !ip_parse(ip_mcast->ip4_src, &cfg->query_ipv4_src)) {
- VLOG_WARN_RL(&rl,
- "IGMP Querier enabled with invalid IPv4 src address");
- /* Failed to parse the IPv4 source address. Disable the querier. */
- cfg->querier_enabled = false;
- }
-
- /* IGMP queries must be sent to 224.0.0.1. */
- cfg->query_eth_dst =
- (struct eth_addr)ETH_ADDR_C(01, 00, 5E, 00, 00, 01);
- cfg->query_ipv4_dst = htonl(0xe0000001);
- }
-}
-
-static uint32_t
-ip_mcast_snoop_hash(int64_t dp_key)
-{
- return hash_uint64(dp_key);
-}
-
-static struct ip_mcast_snoop_state *
-ip_mcast_snoop_state_add(int64_t dp_key)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct ip_mcast_snoop_state *ms_state = xmalloc(sizeof *ms_state);
-
- ms_state->dp_key = dp_key;
- hmap_insert(&mcast_cfg_map, &ms_state->hmap_node,
- ip_mcast_snoop_hash(dp_key));
- return ms_state;
-}
-
-static struct ip_mcast_snoop_state *
-ip_mcast_snoop_state_find(int64_t dp_key)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct ip_mcast_snoop_state *ms_state;
- uint32_t hash = ip_mcast_snoop_hash(dp_key);
-
- HMAP_FOR_EACH_WITH_HASH (ms_state, hmap_node, hash, &mcast_cfg_map) {
- if (ms_state->dp_key == dp_key) {
- return ms_state;
- }
- }
- return NULL;
-}
-
-static bool
-ip_mcast_snoop_state_update(int64_t dp_key,
- const struct ip_mcast_snoop_cfg *cfg)
- OVS_REQUIRES(pinctrl_mutex)
-{
- bool notify = false;
- struct ip_mcast_snoop_state *ms_state = ip_mcast_snoop_state_find(dp_key);
-
- if (!ms_state) {
- ms_state = ip_mcast_snoop_state_add(dp_key);
- notify = true;
- } else if (memcmp(cfg, &ms_state->cfg, sizeof *cfg)) {
- notify = true;
- }
-
- ms_state->cfg = *cfg;
- return notify;
-}
-
-static void
-ip_mcast_snoop_state_remove(struct ip_mcast_snoop_state *ms_state)
- OVS_REQUIRES(pinctrl_mutex)
-{
- hmap_remove(&mcast_cfg_map, &ms_state->hmap_node);
- free(ms_state);
-}
-
-static bool
-ip_mcast_snoop_enable(struct ip_mcast_snoop *ip_ms)
-{
- if (ip_ms->cfg.enabled) {
- return true;
- }
-
- ip_ms->ms = mcast_snooping_create();
- return ip_ms->ms != NULL;
-}
-
-static void
-ip_mcast_snoop_flush(struct ip_mcast_snoop *ip_ms)
-{
- if (!ip_ms->cfg.enabled) {
- return;
- }
-
- mcast_snooping_flush(ip_ms->ms);
-}
-
-static void
-ip_mcast_snoop_disable(struct ip_mcast_snoop *ip_ms)
-{
- if (!ip_ms->cfg.enabled) {
- return;
- }
-
- mcast_snooping_unref(ip_ms->ms);
- ip_ms->ms = NULL;
-}
-
-static bool
-ip_mcast_snoop_configure(struct ip_mcast_snoop *ip_ms,
- const struct ip_mcast_snoop_cfg *cfg)
-{
- if (cfg->enabled) {
- if (!ip_mcast_snoop_enable(ip_ms)) {
- return false;
- }
- if (ip_ms->cfg.seq_no != cfg->seq_no) {
- ip_mcast_snoop_flush(ip_ms);
- }
-
- if (ip_ms->cfg.querier_enabled && !cfg->querier_enabled) {
- ovs_list_remove(&ip_ms->query_node);
- } else if (!ip_ms->cfg.querier_enabled && cfg->querier_enabled) {
- ovs_list_push_back(&mcast_query_list, &ip_ms->query_node);
- }
- } else {
- ip_mcast_snoop_disable(ip_ms);
- goto set_fields;
- }
-
- ovs_rwlock_wrlock(&ip_ms->ms->rwlock);
- if (cfg->table_size != ip_ms->cfg.table_size) {
- mcast_snooping_set_max_entries(ip_ms->ms, cfg->table_size);
- }
-
- if (cfg->idle_time_s != ip_ms->cfg.idle_time_s) {
- mcast_snooping_set_idle_time(ip_ms->ms, cfg->idle_time_s);
- }
- ovs_rwlock_unlock(&ip_ms->ms->rwlock);
-
- if (cfg->query_interval_s != ip_ms->cfg.query_interval_s) {
- long long int now = time_msec();
-
- if (ip_ms->query_time_ms > now + cfg->query_interval_s * 1000) {
- ip_ms->query_time_ms = now;
- }
- }
-
-set_fields:
- memcpy(&ip_ms->cfg, cfg, sizeof ip_ms->cfg);
- return true;
-}
-
-static struct ip_mcast_snoop *
-ip_mcast_snoop_add(int64_t dp_key, const struct ip_mcast_snoop_cfg *cfg)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct ip_mcast_snoop *ip_ms = xzalloc(sizeof *ip_ms);
-
- ip_ms->dp_key = dp_key;
- if (!ip_mcast_snoop_configure(ip_ms, cfg)) {
- free(ip_ms);
- return NULL;
- }
-
- hmap_insert(&mcast_snoop_map, &ip_ms->hmap_node,
- ip_mcast_snoop_hash(dp_key));
- return ip_ms;
-}
-
-static struct ip_mcast_snoop *
-ip_mcast_snoop_find(int64_t dp_key)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct ip_mcast_snoop *ip_ms;
-
- HMAP_FOR_EACH_WITH_HASH (ip_ms, hmap_node, ip_mcast_snoop_hash(dp_key),
- &mcast_snoop_map) {
- if (ip_ms->dp_key == dp_key) {
- return ip_ms;
- }
- }
- return NULL;
-}
-
-static void
-ip_mcast_snoop_remove(struct ip_mcast_snoop *ip_ms)
- OVS_REQUIRES(pinctrl_mutex)
-{
- hmap_remove(&mcast_snoop_map, &ip_ms->hmap_node);
-
- if (ip_ms->cfg.querier_enabled) {
- ovs_list_remove(&ip_ms->query_node);
- }
-
- ip_mcast_snoop_disable(ip_ms);
- free(ip_ms);
-}
-
-static void
-ip_mcast_snoop_init(void)
- OVS_NO_THREAD_SAFETY_ANALYSIS
-{
- hmap_init(&mcast_snoop_map);
- ovs_list_init(&mcast_query_list);
- hmap_init(&mcast_cfg_map);
-}
-
-static void
-ip_mcast_snoop_destroy(void)
- OVS_NO_THREAD_SAFETY_ANALYSIS
-{
- struct ip_mcast_snoop *ip_ms, *ip_ms_next;
-
- HMAP_FOR_EACH_SAFE (ip_ms, ip_ms_next, hmap_node, &mcast_snoop_map) {
- ip_mcast_snoop_remove(ip_ms);
- }
- hmap_destroy(&mcast_snoop_map);
-
- struct ip_mcast_snoop_state *ip_ms_state;
-
- HMAP_FOR_EACH_POP (ip_ms_state, hmap_node, &mcast_cfg_map) {
- free(ip_ms_state);
- }
-}
-
-static void
-ip_mcast_snoop_run(void)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct ip_mcast_snoop *ip_ms, *ip_ms_next;
-
- /* First read the config updated by pinctrl_main. If there's any new or
- * updated config then apply it.
- */
- struct ip_mcast_snoop_state *ip_ms_state;
-
- HMAP_FOR_EACH (ip_ms_state, hmap_node, &mcast_cfg_map) {
- ip_ms = ip_mcast_snoop_find(ip_ms_state->dp_key);
-
- if (!ip_ms) {
- ip_mcast_snoop_add(ip_ms_state->dp_key, &ip_ms_state->cfg);
- } else if (memcmp(&ip_ms_state->cfg, &ip_ms->cfg,
- sizeof ip_ms_state->cfg)) {
- ip_mcast_snoop_configure(ip_ms, &ip_ms_state->cfg);
- }
- }
-
- bool notify = false;
-
- /* Then walk the multicast snoop instances. */
- HMAP_FOR_EACH_SAFE (ip_ms, ip_ms_next, hmap_node, &mcast_snoop_map) {
-
- /* Delete the stale ones. */
- if (!ip_mcast_snoop_state_find(ip_ms->dp_key)) {
- ip_mcast_snoop_remove(ip_ms);
- continue;
- }
-
- /* If enabled run the snooping instance to timeout old groups. */
- if (ip_ms->cfg.enabled) {
- if (mcast_snooping_run(ip_ms->ms)) {
- notify = true;
- }
-
- mcast_snooping_wait(ip_ms->ms);
- }
- }
-
- if (notify) {
- notify_pinctrl_main();
- }
-}
-
-/*
- * This runs in the pinctrl main thread, so it has access to the southbound
- * database. It reads the IP_Multicast table and updates the local multicast
- * configuration. Then writes to the southbound database the updated
- * IGMP_Groups.
- */
-static void
-ip_mcast_sync(struct ovsdb_idl_txn *ovnsb_idl_txn,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_igmp_groups,
- struct ovsdb_idl_index *sbrec_ip_multicast)
- OVS_REQUIRES(pinctrl_mutex)
-{
- bool notify = false;
-
- if (!ovnsb_idl_txn || !chassis) {
- return;
- }
-
- struct sbrec_ip_multicast *ip_mcast;
- struct ip_mcast_snoop_state *ip_ms_state, *ip_ms_state_next;
-
- /* First read and update our own local multicast configuration for the
- * local datapaths.
- */
- SBREC_IP_MULTICAST_FOR_EACH_BYINDEX (ip_mcast, sbrec_ip_multicast) {
-
- int64_t dp_key = ip_mcast->datapath->tunnel_key;
- struct ip_mcast_snoop_cfg cfg;
-
- ip_mcast_snoop_cfg_load(&cfg, ip_mcast);
- if (ip_mcast_snoop_state_update(dp_key, &cfg)) {
- notify = true;
- }
- }
-
- /* Then delete the old entries. */
- HMAP_FOR_EACH_SAFE (ip_ms_state, ip_ms_state_next, hmap_node,
- &mcast_cfg_map) {
- if (!get_local_datapath(local_datapaths, ip_ms_state->dp_key)) {
- ip_mcast_snoop_state_remove(ip_ms_state);
- notify = true;
- }
- }
-
- const struct sbrec_igmp_group *sbrec_igmp;
-
- /* Then flush any IGMP_Group entries that are not needed anymore:
- * - either multicast snooping was disabled on the datapath
- * - or the group has expired.
- */
- SBREC_IGMP_GROUP_FOR_EACH_BYINDEX (sbrec_igmp, sbrec_igmp_groups) {
- ovs_be32 group_addr;
-
- if (!sbrec_igmp->datapath) {
- continue;
- }
-
- int64_t dp_key = sbrec_igmp->datapath->tunnel_key;
- struct ip_mcast_snoop *ip_ms = ip_mcast_snoop_find(dp_key);
-
- /* If the datapath doesn't exist anymore or IGMP snooping was disabled
- * on it then delete the IGMP_Group entry.
- */
- if (!ip_ms || !ip_ms->cfg.enabled) {
- igmp_group_delete(sbrec_igmp);
- continue;
- }
-
- if (!ip_parse(sbrec_igmp->address, &group_addr)) {
- continue;
- }
-
- ovs_rwlock_rdlock(&ip_ms->ms->rwlock);
- struct mcast_group *mc_group =
- mcast_snooping_lookup4(ip_ms->ms, group_addr,
- IP_MCAST_VLAN);
-
- if (!mc_group || ovs_list_is_empty(&mc_group->bundle_lru)) {
- igmp_group_delete(sbrec_igmp);
- }
- ovs_rwlock_unlock(&ip_ms->ms->rwlock);
- }
-
- struct ip_mcast_snoop *ip_ms, *ip_ms_next;
-
- /* Last: write new IGMP_Groups to the southbound DB and update existing
- * ones (if needed). We also flush any old per-datapath multicast snoop
- * structures.
- */
- HMAP_FOR_EACH_SAFE (ip_ms, ip_ms_next, hmap_node, &mcast_snoop_map) {
- /* Flush any non-local snooping datapaths (e.g., stale). */
- struct local_datapath *local_dp =
- get_local_datapath(local_datapaths, ip_ms->dp_key);
-
- if (!local_dp) {
- continue;
- }
-
- /* Skip datapaths on which snooping is disabled. */
- if (!ip_ms->cfg.enabled) {
- continue;
- }
-
- struct mcast_group *mc_group;
-
- ovs_rwlock_rdlock(&ip_ms->ms->rwlock);
- LIST_FOR_EACH (mc_group, group_node, &ip_ms->ms->group_lru) {
- if (ovs_list_is_empty(&mc_group->bundle_lru)) {
- continue;
- }
- sbrec_igmp = igmp_group_lookup(sbrec_igmp_groups, &mc_group->addr,
- local_dp->datapath, chassis);
- if (!sbrec_igmp) {
- sbrec_igmp = igmp_group_create(ovnsb_idl_txn, &mc_group->addr,
- local_dp->datapath, chassis);
- }
-
- igmp_group_update_ports(sbrec_igmp, sbrec_datapath_binding_by_key,
- sbrec_port_binding_by_key, ip_ms->ms,
- mc_group);
- }
- ovs_rwlock_unlock(&ip_ms->ms->rwlock);
- }
-
- if (notify) {
- notify_pinctrl_handler();
- }
-}
-
-static void
-pinctrl_ip_mcast_handle_igmp(struct rconn *swconn OVS_UNUSED,
- const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md,
- struct ofpbuf *userdata OVS_UNUSED)
- OVS_NO_THREAD_SAFETY_ANALYSIS
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
-
- /* This action only works for IP packets, and the switch should only send
- * us IP packets this way, but check here just to be sure.
- */
- if (ip_flow->dl_type != htons(ETH_TYPE_IP)) {
- VLOG_WARN_RL(&rl,
- "IGMP action on non-IP packet (eth_type 0x%"PRIx16")",
- ntohs(ip_flow->dl_type));
- return;
- }
-
- int64_t dp_key = ntohll(md->flow.metadata);
- uint32_t port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0];
-
- const struct igmp_header *igmp;
- size_t offset;
-
- offset = (char *) dp_packet_l4(pkt_in) - (char *) dp_packet_data(pkt_in);
- igmp = dp_packet_at(pkt_in, offset, IGMP_HEADER_LEN);
- if (!igmp || csum(igmp, dp_packet_l4_size(pkt_in)) != 0) {
- VLOG_WARN_RL(&rl, "multicast snooping received bad IGMP checksum");
- return;
- }
-
- ovs_be32 ip4 = ip_flow->igmp_group_ip4;
-
- struct ip_mcast_snoop *ip_ms = ip_mcast_snoop_find(dp_key);
- if (!ip_ms || !ip_ms->cfg.enabled) {
- /* IGMP snooping is not configured or is disabled. */
- return;
- }
-
- void *port_key_data = (void *)(uintptr_t)port_key;
-
- bool group_change = false;
-
- ovs_rwlock_wrlock(&ip_ms->ms->rwlock);
- switch (ntohs(ip_flow->tp_src)) {
- /* Only default VLAN is supported for now. */
- case IGMP_HOST_MEMBERSHIP_REPORT:
- case IGMPV2_HOST_MEMBERSHIP_REPORT:
- group_change =
- mcast_snooping_add_group4(ip_ms->ms, ip4, IP_MCAST_VLAN,
- port_key_data);
- break;
- case IGMP_HOST_LEAVE_MESSAGE:
- group_change =
- mcast_snooping_leave_group4(ip_ms->ms, ip4, IP_MCAST_VLAN,
- port_key_data);
- break;
- case IGMP_HOST_MEMBERSHIP_QUERY:
- /* Shouldn't be receiving any of these since we are the multicast
- * router. Store them for now.
- */
- group_change =
- mcast_snooping_add_mrouter(ip_ms->ms, IP_MCAST_VLAN,
- port_key_data);
- break;
- case IGMPV3_HOST_MEMBERSHIP_REPORT:
- group_change =
- mcast_snooping_add_report(ip_ms->ms, pkt_in, IP_MCAST_VLAN,
- port_key_data);
- break;
- }
- ovs_rwlock_unlock(&ip_ms->ms->rwlock);
-
- if (group_change) {
- notify_pinctrl_main();
- }
-}
-
-static long long int
-ip_mcast_querier_send(struct rconn *swconn, struct ip_mcast_snoop *ip_ms,
- long long int current_time)
-{
- if (current_time < ip_ms->query_time_ms) {
- return ip_ms->query_time_ms;
- }
-
- /* Compose a multicast query. */
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
-
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-
- uint8_t ip_tos = 0;
- uint8_t igmp_ttl = 1;
-
- dp_packet_clear(&packet);
- packet.packet_type = htonl(PT_ETH);
-
- struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
- eh->eth_dst = ip_ms->cfg.query_eth_dst;
- eh->eth_src = ip_ms->cfg.query_eth_src;
-
- struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
-
- eh->eth_type = htons(ETH_TYPE_IP);
- dp_packet_set_l3(&packet, nh);
- nh->ip_ihl_ver = IP_IHL_VER(5, 4);
- nh->ip_tot_len = htons(sizeof(struct ip_header) +
- sizeof(struct igmpv3_query_header));
- nh->ip_tos = IP_DSCP_CS6;
- nh->ip_proto = IPPROTO_IGMP;
- nh->ip_frag_off = htons(IP_DF);
- packet_set_ipv4(&packet, ip_ms->cfg.query_ipv4_src,
- ip_ms->cfg.query_ipv4_dst, ip_tos, igmp_ttl);
-
- nh->ip_csum = 0;
- nh->ip_csum = csum(nh, sizeof *nh);
-
- struct igmpv3_query_header *igh =
- dp_packet_put_zeros(&packet, sizeof *igh);
- dp_packet_set_l4(&packet, igh);
-
- /* IGMP query max-response in tenths of seconds. */
- uint8_t max_response = ip_ms->cfg.query_max_resp_s * 10;
- uint8_t qqic = max_response;
- packet_set_igmp3_query(&packet, max_response, 0, false, 0, qqic);
-
- /* Inject multicast query. */
- uint64_t ofpacts_stub[4096 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
- enum ofp_version version = rconn_get_version(swconn);
- put_load(ip_ms->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
- put_load(OVN_MCAST_FLOOD_TUNNEL_KEY, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
- put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts);
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
- resubmit->in_port = OFPP_CONTROLLER;
- resubmit->table_id = OFTABLE_LOCAL_OUTPUT;
-
- struct ofputil_packet_out po = {
- .packet = dp_packet_data(&packet),
- .packet_len = dp_packet_size(&packet),
- .buffer_id = UINT32_MAX,
- .ofpacts = ofpacts.data,
- .ofpacts_len = ofpacts.size,
- };
- match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
- dp_packet_uninit(&packet);
- ofpbuf_uninit(&ofpacts);
-
- /* Set the next query time. */
- ip_ms->query_time_ms = current_time + ip_ms->cfg.query_interval_s * 1000;
- return ip_ms->query_time_ms;
-}
-
-static void
-ip_mcast_querier_run(struct rconn *swconn, long long int *query_time)
-{
- if (ovs_list_is_empty(&mcast_query_list)) {
- return;
- }
-
- /* Send multicast queries and update the next query time. */
- long long int current_time = time_msec();
- *query_time = LLONG_MAX;
-
- struct ip_mcast_snoop *ip_ms;
-
- LIST_FOR_EACH (ip_ms, query_node, &mcast_query_list) {
- long long int next_query_time =
- ip_mcast_querier_send(swconn, ip_ms, current_time);
- if (*query_time > next_query_time) {
- *query_time = next_query_time;
- }
- }
-}
-
-static void
-ip_mcast_querier_wait(long long int query_time)
-{
- if (!ovs_list_is_empty(&mcast_query_list)) {
- poll_timer_wait_until(query_time);
- }
-}
-
-/* Get localnet vifs, local l3gw ports and ofport for localnet patch ports. */
-static void
-get_localnet_vifs_l3gwports(
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- struct sset *localnet_vifs,
- struct sset *local_l3gw_ports)
-{
- for (int i = 0; i < br_int->n_ports; i++) {
- const struct ovsrec_port *port_rec = br_int->ports[i];
- if (!strcmp(port_rec->name, br_int->name)) {
- continue;
- }
- const char *tunnel_id = smap_get(&port_rec->external_ids,
- "ovn-chassis-id");
- if (tunnel_id &&
- encaps_tunnel_id_match(tunnel_id, chassis->name, NULL)) {
- continue;
- }
- const char *localnet = smap_get(&port_rec->external_ids,
- "ovn-localnet-port");
- if (localnet) {
- continue;
- }
- for (int j = 0; j < port_rec->n_interfaces; j++) {
- const struct ovsrec_interface *iface_rec = port_rec->interfaces[j];
- if (!iface_rec->n_ofport) {
- continue;
- }
- /* Get localnet vif. */
- const char *iface_id = smap_get(&iface_rec->external_ids,
- "iface-id");
- if (!iface_id) {
- continue;
- }
- const struct sbrec_port_binding *pb
- = lport_lookup_by_name(sbrec_port_binding_by_name, iface_id);
- if (!pb) {
- continue;
- }
- struct local_datapath *ld
- = get_local_datapath(local_datapaths,
- pb->datapath->tunnel_key);
- if (ld && ld->localnet_port) {
- sset_add(localnet_vifs, iface_id);
- }
- }
- }
-
- struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
- sbrec_port_binding_by_datapath);
-
- const struct local_datapath *ld;
- HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
- const struct sbrec_port_binding *pb;
-
- if (!ld->localnet_port) {
- continue;
- }
-
- /* Get l3gw ports. Consider port bindings with type "l3gateway"
- * that connect to gateway routers (if local), and consider port
- * bindings of type "patch" since they might connect to
- * distributed gateway ports with NAT addresses. */
-
- sbrec_port_binding_index_set_datapath(target, ld->datapath);
- SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
- sbrec_port_binding_by_datapath) {
- if ((ld->has_local_l3gateway && !strcmp(pb->type, "l3gateway"))
- || !strcmp(pb->type, "patch")) {
- sset_add(local_l3gw_ports, pb->logical_port);
- }
- }
- }
- sbrec_port_binding_index_destroy_row(target);
-}
-
-static bool
-pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct sbrec_chassis *chassis,
- const struct sset *active_tunnels,
- const char *port_name)
-{
- const struct sbrec_port_binding *pb
- = lport_lookup_by_name(sbrec_port_binding_by_name, port_name);
- if (!pb || !pb->chassis) {
- return false;
- }
- if (strcmp(pb->type, "chassisredirect")) {
- return pb->chassis == chassis;
- } else {
- return ha_chassis_group_is_active(pb->ha_chassis_group,
- active_tunnels, chassis);
- }
-}
-
-/* Extracts the mac, IPv4 and IPv6 addresses, and logical port from
- * 'addresses' which should be of the format 'MAC [IP1 IP2 ..]
- * [is_chassis_resident("LPORT_NAME")]', where IPn should be a valid IPv4
- * or IPv6 address, and stores them in the 'ipv4_addrs' and 'ipv6_addrs'
- * fields of 'laddrs'. The logical port name is stored in 'lport'.
- *
- * Returns true if at least 'MAC' is found in 'address', false otherwise.
- *
- * The caller must call destroy_lport_addresses() and free(*lport). */
-static bool
-extract_addresses_with_port(const char *addresses,
- struct lport_addresses *laddrs,
- char **lport)
-{
- int ofs;
- if (!extract_addresses(addresses, laddrs, &ofs)) {
- return false;
- } else if (ofs >= strlen(addresses)) {
- return true;
- }
-
- struct lexer lexer;
- lexer_init(&lexer, addresses + ofs);
- lexer_get(&lexer);
-
- if (lexer.error || lexer.token.type != LEX_T_ID
- || !lexer_match_id(&lexer, "is_chassis_resident")) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "invalid syntax '%s' in address", addresses);
- lexer_destroy(&lexer);
- return true;
- }
-
- if (!lexer_match(&lexer, LEX_T_LPAREN)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "Syntax error: expecting '(' after "
- "'is_chassis_resident' in address '%s'", addresses);
- lexer_destroy(&lexer);
- return false;
- }
-
- if (lexer.token.type != LEX_T_STRING) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl,
- "Syntax error: expecting quoted string after "
- "'is_chassis_resident' in address '%s'", addresses);
- lexer_destroy(&lexer);
- return false;
- }
-
- *lport = xstrdup(lexer.token.s);
-
- lexer_get(&lexer);
- if (!lexer_match(&lexer, LEX_T_RPAREN)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "Syntax error: expecting ')' after quoted string in "
- "'is_chassis_resident()' in address '%s'",
- addresses);
- lexer_destroy(&lexer);
- return false;
- }
-
- lexer_destroy(&lexer);
- return true;
-}
-
-static void
-consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const char *nat_address,
- const struct sbrec_port_binding *pb,
- struct sset *nat_address_keys,
- const struct sbrec_chassis *chassis,
- const struct sset *active_tunnels,
- struct shash *nat_addresses)
-{
- struct lport_addresses *laddrs = xmalloc(sizeof *laddrs);
- char *lport = NULL;
- if (!extract_addresses_with_port(nat_address, laddrs, &lport)
- || (!lport && !strcmp(pb->type, "patch"))
- || (lport && !pinctrl_is_chassis_resident(
- sbrec_port_binding_by_name, chassis,
- active_tunnels, lport))) {
- destroy_lport_addresses(laddrs);
- free(laddrs);
- free(lport);
- return;
- }
- free(lport);
-
- int i;
- for (i = 0; i < laddrs->n_ipv4_addrs; i++) {
- char *name = xasprintf("%s-%s", pb->logical_port,
- laddrs->ipv4_addrs[i].addr_s);
- sset_add(nat_address_keys, name);
- free(name);
- }
- shash_add(nat_addresses, pb->logical_port, laddrs);
-}
-
-static void
-get_nat_addresses_and_keys(struct ovsdb_idl_index *sbrec_port_binding_by_name,
- struct sset *nat_address_keys,
- struct sset *local_l3gw_ports,
- const struct sbrec_chassis *chassis,
- const struct sset *active_tunnels,
- struct shash *nat_addresses)
-{
- const char *gw_port;
- SSET_FOR_EACH(gw_port, local_l3gw_ports) {
- const struct sbrec_port_binding *pb;
-
- pb = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
- if (!pb) {
- continue;
- }
-
- if (pb->n_nat_addresses) {
- for (int i = 0; i < pb->n_nat_addresses; i++) {
- consider_nat_address(sbrec_port_binding_by_name,
- pb->nat_addresses[i], pb,
- nat_address_keys, chassis,
- active_tunnels,
- nat_addresses);
- }
- } else {
- /* Continue to support options:nat-addresses for version
- * upgrade. */
- const char *nat_addresses_options = smap_get(&pb->options,
- "nat-addresses");
- if (nat_addresses_options) {
- consider_nat_address(sbrec_port_binding_by_name,
- nat_addresses_options, pb,
- nat_address_keys, chassis,
- active_tunnels,
- nat_addresses);
- }
- }
- }
-}
-
-static void
-send_garp_wait(long long int send_garp_time)
-{
- /* Set the poll timer for next garp only if there is garp data to
- * be sent. */
- if (!shash_is_empty(&send_garp_data)) {
- poll_timer_wait_until(send_garp_time);
- }
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-send_garp_run(struct rconn *swconn, long long int *send_garp_time)
- OVS_REQUIRES(pinctrl_mutex)
-{
- if (shash_is_empty(&send_garp_data)) {
- return;
- }
-
- /* Send GARPs, and update the next announcement. */
- struct shash_node *iter;
- long long int current_time = time_msec();
- *send_garp_time = LLONG_MAX;
- SHASH_FOR_EACH (iter, &send_garp_data) {
- long long int next_announce = send_garp(swconn, iter->data,
- current_time);
- if (*send_garp_time > next_announce) {
- *send_garp_time = next_announce;
- }
- }
-}
-
-/* Called by pinctrl_run(). Runs with in the main ovn-controller
- * thread context. */
-static void
-send_garp_prepare(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- const struct ovsrec_bridge *br_int,
- const struct sbrec_chassis *chassis,
- const struct hmap *local_datapaths,
- const struct sset *active_tunnels)
- OVS_REQUIRES(pinctrl_mutex)
-{
- struct sset localnet_vifs = SSET_INITIALIZER(&localnet_vifs);
- struct sset local_l3gw_ports = SSET_INITIALIZER(&local_l3gw_ports);
- struct sset nat_ip_keys = SSET_INITIALIZER(&nat_ip_keys);
- struct shash nat_addresses;
-
- shash_init(&nat_addresses);
-
- get_localnet_vifs_l3gwports(sbrec_port_binding_by_datapath,
- sbrec_port_binding_by_name,
- br_int, chassis, local_datapaths,
- &localnet_vifs, &local_l3gw_ports);
-
- get_nat_addresses_and_keys(sbrec_port_binding_by_name,
- &nat_ip_keys, &local_l3gw_ports,
- chassis, active_tunnels,
- &nat_addresses);
- /* For deleted ports and deleted nat ips, remove from send_garp_data. */
- struct shash_node *iter, *next;
- SHASH_FOR_EACH_SAFE (iter, next, &send_garp_data) {
- if (!sset_contains(&localnet_vifs, iter->name) &&
- !sset_contains(&nat_ip_keys, iter->name)) {
- send_garp_delete(iter->name);
- }
- }
-
- /* Update send_garp_data. */
- const char *iface_id;
- SSET_FOR_EACH (iface_id, &localnet_vifs) {
- const struct sbrec_port_binding *pb = lport_lookup_by_name(
- sbrec_port_binding_by_name, iface_id);
- if (pb) {
- send_garp_update(pb, &nat_addresses);
- }
- }
-
- /* Update send_garp_data for nat-addresses. */
- const char *gw_port;
- SSET_FOR_EACH (gw_port, &local_l3gw_ports) {
- const struct sbrec_port_binding *pb
- = lport_lookup_by_name(sbrec_port_binding_by_name, gw_port);
- if (pb) {
- send_garp_update(pb, &nat_addresses);
- }
- }
-
- /* pinctrl_handler thread will send the GARPs. */
-
- sset_destroy(&localnet_vifs);
- sset_destroy(&local_l3gw_ports);
-
- SHASH_FOR_EACH_SAFE (iter, next, &nat_addresses) {
- struct lport_addresses *laddrs = iter->data;
- destroy_lport_addresses(laddrs);
- shash_delete(&nat_addresses, iter);
- free(laddrs);
- }
- shash_destroy(&nat_addresses);
-
- sset_destroy(&nat_ip_keys);
-}
-
-static bool
-may_inject_pkts(void)
-{
- return (!shash_is_empty(&ipv6_ras) ||
- !shash_is_empty(&send_garp_data) ||
- !ovs_list_is_empty(&mcast_query_list) ||
- !ovs_list_is_empty(&buffered_mac_bindings));
-}
-
-static void
-reload_metadata(struct ofpbuf *ofpacts, const struct match *md)
-{
- enum mf_field_id md_fields[] = {
-#if FLOW_N_REGS == 16
- MFF_REG0,
- MFF_REG1,
- MFF_REG2,
- MFF_REG3,
- MFF_REG4,
- MFF_REG5,
- MFF_REG6,
- MFF_REG7,
- MFF_REG8,
- MFF_REG9,
- MFF_REG10,
- MFF_REG11,
- MFF_REG12,
- MFF_REG13,
- MFF_REG14,
- MFF_REG15,
-#else
-#error
-#endif
- MFF_METADATA,
- };
- for (size_t i = 0; i < ARRAY_SIZE(md_fields); i++) {
- const struct mf_field *field = mf_from_id(md_fields[i]);
- if (!mf_is_all_wild(field, &md->wc)) {
- union mf_value value;
- mf_get_value(field, &md->flow, &value);
- ofpact_put_set_field(ofpacts, field, &value, NULL);
- }
- }
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_nd_na(struct rconn *swconn, const struct flow *ip_flow,
- const struct match *md,
- struct ofpbuf *userdata, bool is_router)
-{
- /* This action only works for IPv6 ND packets, and the switch should only
- * send us ND packets this way, but check here just to be sure. */
- if (!is_nd(ip_flow, NULL)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "NA action on non-ND packet");
- return;
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-
- /* These flags are not exactly correct. Look at section 7.2.4
- * of RFC 4861. */
- uint32_t rso_flags = ND_RSO_SOLICITED | ND_RSO_OVERRIDE;
- if (is_router) {
- rso_flags |= ND_RSO_ROUTER;
- }
- compose_nd_na(&packet, ip_flow->dl_dst, ip_flow->dl_src,
- &ip_flow->nd_target, &ip_flow->ipv6_src,
- htonl(rso_flags));
-
- /* Reload previous packet metadata and set actions from userdata. */
- set_actions_and_enqueue_msg(swconn, &packet, md, userdata);
- dp_packet_uninit(&packet);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_nd_ns(struct rconn *swconn, const struct flow *ip_flow,
- struct dp_packet *pkt_in,
- const struct match *md, struct ofpbuf *userdata)
-{
- /* This action only works for IPv6 packets. */
- if (get_dl_type(ip_flow) != htons(ETH_TYPE_IPV6)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "NS action on non-IPv6 packet");
- return;
- }
-
- ovs_mutex_lock(&pinctrl_mutex);
- pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, false);
- ovs_mutex_unlock(&pinctrl_mutex);
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
-
- compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src,
- &ip_flow->ipv6_dst);
-
- /* Reload previous packet metadata and set actions from userdata. */
- set_actions_and_enqueue_msg(swconn, &packet, md, userdata);
- dp_packet_uninit(&packet);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_put_nd_ra_opts(
- struct rconn *swconn,
- const struct flow *in_flow, struct dp_packet *pkt_in,
- struct ofputil_packet_in *pin, struct ofpbuf *userdata,
- struct ofpbuf *continuation)
-{
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- struct dp_packet *pkt_out_ptr = NULL;
- uint32_t success = 0;
-
- /* Parse result field. */
- const struct mf_field *f;
- enum ofperr ofperr = nx_pull_header(userdata, NULL, &f, NULL);
- if (ofperr) {
- VLOG_WARN_RL(&rl, "bad result OXM (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- /* Parse result offset. */
- ovs_be32 *ofsp = ofpbuf_try_pull(userdata, sizeof *ofsp);
- if (!ofsp) {
- VLOG_WARN_RL(&rl, "offset not present in the userdata");
- goto exit;
- }
-
- /* Check that the result is valid and writable. */
- struct mf_subfield dst = { .field = f, .ofs = ntohl(*ofsp), .n_bits = 1 };
- ofperr = mf_check_dst(&dst, NULL);
- if (ofperr) {
- VLOG_WARN_RL(&rl, "bad result bit (%s)", ofperr_to_string(ofperr));
- goto exit;
- }
-
- if (!userdata->size) {
- VLOG_WARN_RL(&rl, "IPv6 ND RA options not present in the userdata");
- goto exit;
- }
-
- if (!is_icmpv6(in_flow, NULL) || in_flow->tp_dst != htons(0) ||
- in_flow->tp_src != htons(ND_ROUTER_SOLICIT)) {
- VLOG_WARN_RL(&rl, "put_nd_ra action on invalid or unsupported packet");
- goto exit;
- }
-
- size_t new_packet_size = pkt_in->l4_ofs + userdata->size;
- struct dp_packet pkt_out;
- dp_packet_init(&pkt_out, new_packet_size);
- dp_packet_clear(&pkt_out);
- dp_packet_prealloc_tailroom(&pkt_out, new_packet_size);
- pkt_out_ptr = &pkt_out;
-
- /* Copy L2 and L3 headers from pkt_in. */
- dp_packet_put(&pkt_out, dp_packet_pull(pkt_in, pkt_in->l4_ofs),
- pkt_in->l4_ofs);
-
- pkt_out.l2_5_ofs = pkt_in->l2_5_ofs;
- pkt_out.l2_pad_size = pkt_in->l2_pad_size;
- pkt_out.l3_ofs = pkt_in->l3_ofs;
- pkt_out.l4_ofs = pkt_in->l4_ofs;
-
- /* Copy the ICMPv6 Router Advertisement data from 'userdata' field. */
- dp_packet_put(&pkt_out, userdata->data, userdata->size);
-
- /* Set the IPv6 payload length and calculate the ICMPv6 checksum. */
- struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(&pkt_out);
- nh->ip6_plen = htons(userdata->size);
- struct ovs_ra_msg *ra = dp_packet_l4(&pkt_out);
- ra->icmph.icmp6_cksum = 0;
- uint32_t icmp_csum = packet_csum_pseudoheader6(nh);
- ra->icmph.icmp6_cksum = csum_finish(csum_continue(
- icmp_csum, ra, userdata->size));
- pin->packet = dp_packet_data(&pkt_out);
- pin->packet_len = dp_packet_size(&pkt_out);
- success = 1;
-
-exit:
- if (!ofperr) {
- union mf_subvalue sv;
- sv.u8_val = success;
- mf_write_subfield(&dst, &sv, &pin->flow_metadata);
- }
- queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
- dp_packet_uninit(pkt_out_ptr);
-}
-
-/* Called with in the pinctrl_handler thread context. */
-static void
-pinctrl_handle_put_icmp4_frag_mtu(struct rconn *swconn,
- const struct flow *in_flow,
- struct dp_packet *pkt_in,
- struct ofputil_packet_in *pin,
- struct ofpbuf *userdata,
- struct ofpbuf *continuation)
-{
- enum ofp_version version = rconn_get_version(swconn);
- enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
- struct dp_packet *pkt_out = NULL;
-
- /* This action only works for ICMPv4 packets. */
- if (!is_icmpv4(in_flow, NULL)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "put_icmp4_frag_mtu action on non-ICMPv4 packet");
- goto exit;
- }
-
- ovs_be16 *mtu = ofpbuf_try_pull(userdata, sizeof *mtu);
- if (!mtu) {
- goto exit;
- }
-
- pkt_out = dp_packet_clone(pkt_in);
- pkt_out->l2_5_ofs = pkt_in->l2_5_ofs;
- pkt_out->l2_pad_size = pkt_in->l2_pad_size;
- pkt_out->l3_ofs = pkt_in->l3_ofs;
- pkt_out->l4_ofs = pkt_in->l4_ofs;
-
- struct ip_header *nh = dp_packet_l3(pkt_out);
- struct icmp_header *ih = dp_packet_l4(pkt_out);
- ovs_be16 old_frag_mtu = ih->icmp_fields.frag.mtu;
- ih->icmp_fields.frag.mtu = *mtu;
- ih->icmp_csum = recalc_csum16(ih->icmp_csum, old_frag_mtu, *mtu);
- nh->ip_csum = 0;
- nh->ip_csum = csum(nh, sizeof *nh);
-
- pin->packet = dp_packet_data(pkt_out);
- pin->packet_len = dp_packet_size(pkt_out);
-
-exit:
- queue_msg(swconn, ofputil_encode_resume(pin, continuation, proto));
- if (pkt_out) {
- dp_packet_delete(pkt_out);
- }
-}
-
-static void
-wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn)
-{
- if (!ovnsb_idl_txn) {
- return;
- }
-
- for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
- if (!hmap_is_empty(&event_table[i])) {
- poll_immediate_wake();
- break;
- }
- }
-}
-
-static bool
-pinctrl_handle_empty_lb_backends_opts(struct ofpbuf *userdata)
-{
- struct controller_event_opt_header *userdata_opt;
- uint32_t hash = 0;
- char *vip = NULL;
- char *protocol = NULL;
- char *load_balancer = NULL;
-
- while (userdata->size) {
- userdata_opt = ofpbuf_try_pull(userdata, sizeof *userdata_opt);
- if (!userdata_opt) {
- return false;
- }
- size_t size = ntohs(userdata_opt->size);
- char *userdata_opt_data = ofpbuf_try_pull(userdata, size);
- if (!userdata_opt_data) {
- return false;
- }
- switch (ntohs(userdata_opt->opt_code)) {
- case EMPTY_LB_VIP:
- vip = xmemdup0(userdata_opt_data, size);
- break;
- case EMPTY_LB_PROTOCOL:
- protocol = xmemdup0(userdata_opt_data, size);
- break;
- case EMPTY_LB_LOAD_BALANCER:
- load_balancer = xmemdup0(userdata_opt_data, size);
- break;
- default:
- OVS_NOT_REACHED();
- }
- hash = hash_bytes(userdata_opt_data, size, hash);
- }
- if (!vip || !protocol || !load_balancer) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "missing lb parameters in userdata");
- return false;
- }
-
- struct empty_lb_backends_event *event;
-
- event = pinctrl_find_empty_lb_backends_event(vip, protocol,
- load_balancer, hash);
- if (!event) {
- if (hmap_count(&event_table[OVN_EVENT_EMPTY_LB_BACKENDS]) >= 1000) {
- COVERAGE_INC(pinctrl_drop_controller_event);
- return false;
- }
-
- event = xzalloc(sizeof *event);
- hmap_insert(&event_table[OVN_EVENT_EMPTY_LB_BACKENDS],
- &event->hmap_node, hash);
- event->vip = vip;
- event->protocol = protocol;
- event->load_balancer = load_balancer;
- event->timestamp = time_msec();
- notify_pinctrl_main();
- } else {
- free(vip);
- free(protocol);
- free(load_balancer);
- }
- return true;
-}
-
-static void
-pinctrl_handle_event(struct ofpbuf *userdata)
- OVS_REQUIRES(pinctrl_mutex)
-{
- ovs_be32 *pevent;
-
- pevent = ofpbuf_try_pull(userdata, sizeof *pevent);
- if (!pevent) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "event not present in the userdata");
- return;
- }
-
- switch (ntohl(*pevent)) {
- case OVN_EVENT_EMPTY_LB_BACKENDS:
- pinctrl_handle_empty_lb_backends_opts(userdata);
- break;
- default:
- return;
- }
-}
diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
deleted file mode 100644
index fcfce6bcf..000000000
--- a/ovn/controller/pinctrl.h
+++ /dev/null
@@ -1,51 +0,0 @@
-
-/* Copyright (c) 2015, 2016 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef PINCTRL_H
-#define PINCTRL_H 1
-
-#include <stdint.h>
-
-#include "lib/sset.h"
-#include "openvswitch/meta-flow.h"
-
-struct hmap;
-struct lport_index;
-struct ovsdb_idl_index;
-struct ovsdb_idl_txn;
-struct ovsrec_bridge;
-struct sbrec_chassis;
-struct sbrec_dns_table;
-struct sbrec_controller_event_table;
-
-void pinctrl_init(void);
-void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
- struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
- struct ovsdb_idl_index *sbrec_port_binding_by_key,
- struct ovsdb_idl_index *sbrec_port_binding_by_name,
- struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
- struct ovsdb_idl_index *sbrec_igmp_groups,
- struct ovsdb_idl_index *sbrec_ip_multicast_opts,
- const struct sbrec_dns_table *,
- const struct sbrec_controller_event_table *,
- const struct ovsrec_bridge *, const struct sbrec_chassis *,
- const struct hmap *local_datapaths,
- const struct sset *active_tunnels);
-void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
-void pinctrl_destroy(void);
-
-#endif /* ovn/pinctrl.h */
diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
deleted file mode 100644
index 4eacc44ed..000000000
--- a/ovn/lib/actions.c
+++ /dev/null
@@ -1,2902 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include "bitmap.h"
-#include "byte-order.h"
-#include "compiler.h"
-#include "ovn-l7.h"
-#include "hash.h"
-#include "lib/packets.h"
-#include "nx-match.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/json.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofpbuf.h"
-#include "openvswitch/vlog.h"
-#include "ovn/actions.h"
-#include "ovn/expr.h"
-#include "ovn/lex.h"
-#include "ovn/lib/acl-log.h"
-#include "ovn/lib/extend-table.h"
-#include "packets.h"
-#include "openvswitch/shash.h"
-#include "simap.h"
-#include "uuid.h"
-#include "socket-util.h"
-
-VLOG_DEFINE_THIS_MODULE(actions);
-
-/* Prototypes for functions to be defined by each action. */
-#define OVNACT(ENUM, STRUCT) \
- static void format_##ENUM(const struct STRUCT *, struct ds *); \
- static void encode_##ENUM(const struct STRUCT *, \
- const struct ovnact_encode_params *, \
- struct ofpbuf *ofpacts); \
- static void STRUCT##_free(struct STRUCT *a);
-OVNACTS
-#undef OVNACT
-
-/* Helpers. */
-
-/* Implementation of ovnact_put_<ENUM>(). */
-void *
-ovnact_put(struct ofpbuf *ovnacts, enum ovnact_type type, size_t len)
-{
- ovs_assert(len == OVNACT_ALIGN(len));
-
- ovnacts->header = ofpbuf_put_uninit(ovnacts, len);
- struct ovnact *ovnact = ovnacts->header;
- ovnact_init(ovnact, type, len);
- return ovnact;
-}
-
-/* Implementation of ovnact_init_<ENUM>(). */
-void
-ovnact_init(struct ovnact *ovnact, enum ovnact_type type, size_t len)
-{
- ovs_assert(len == OVNACT_ALIGN(len));
- memset(ovnact, 0, len);
- ovnact->type = type;
- ovnact->len = len;
-}
-
-static size_t
-encode_start_controller_op(enum action_opcode opcode, bool pause,
- uint32_t meter_id, struct ofpbuf *ofpacts)
-{
- size_t ofs = ofpacts->size;
-
- struct ofpact_controller *oc = ofpact_put_CONTROLLER(ofpacts);
- oc->max_len = UINT16_MAX;
- oc->reason = OFPR_ACTION;
- oc->pause = pause;
- oc->meter_id = meter_id;
-
- struct action_header ah = { .opcode = htonl(opcode) };
- ofpbuf_put(ofpacts, &ah, sizeof ah);
-
- return ofs;
-}
-
-static void
-encode_finish_controller_op(size_t ofs, struct ofpbuf *ofpacts)
-{
- struct ofpact_controller *oc = ofpbuf_at_assert(ofpacts, ofs, sizeof *oc);
- ofpacts->header = oc;
- oc->userdata_len = ofpacts->size - (ofs + sizeof *oc);
- ofpact_finish_CONTROLLER(ofpacts, &oc);
-}
-
-static void
-encode_controller_op(enum action_opcode opcode, struct ofpbuf *ofpacts)
-{
- size_t ofs = encode_start_controller_op(opcode, false, NX_CTLR_NO_METER,
- ofpacts);
- encode_finish_controller_op(ofs, ofpacts);
-}
-
-static void
-init_stack(struct ofpact_stack *stack, enum mf_field_id field)
-{
- stack->subfield.field = mf_from_id(field);
- stack->subfield.ofs = 0;
- stack->subfield.n_bits = stack->subfield.field->n_bits;
-}
-
-struct arg {
- const struct mf_subfield src;
- enum mf_field_id dst;
-};
-
-static void
-encode_setup_args(const struct arg args[], size_t n_args,
- struct ofpbuf *ofpacts)
-{
- /* 1. Save all of the destinations that will be modified. */
- for (const struct arg *a = args; a < &args[n_args]; a++) {
- ovs_assert(a->src.n_bits == mf_from_id(a->dst)->n_bits);
- if (a->src.field->id != a->dst) {
- init_stack(ofpact_put_STACK_PUSH(ofpacts), a->dst);
- }
- }
-
- /* 2. Push the sources, in reverse order. */
- for (size_t i = n_args - 1; i < n_args; i--) {
- const struct arg *a = &args[i];
- if (a->src.field->id != a->dst) {
- ofpact_put_STACK_PUSH(ofpacts)->subfield = a->src;
- }
- }
-
- /* 3. Pop the sources into the destinations. */
- for (const struct arg *a = args; a < &args[n_args]; a++) {
- if (a->src.field->id != a->dst) {
- init_stack(ofpact_put_STACK_POP(ofpacts), a->dst);
- }
- }
-}
-
-static void
-encode_restore_args(const struct arg args[], size_t n_args,
- struct ofpbuf *ofpacts)
-{
- for (size_t i = n_args - 1; i < n_args; i--) {
- const struct arg *a = &args[i];
- if (a->src.field->id != a->dst) {
- init_stack(ofpact_put_STACK_POP(ofpacts), a->dst);
- }
- }
-}
-
-static void
-put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
- mf_from_id(dst), NULL,
- NULL);
- ovs_be64 n_value = htonll(value);
- bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs, n_bits);
- bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
-}
-
-static uint8_t
-first_ptable(const struct ovnact_encode_params *ep,
- enum ovnact_pipeline pipeline)
-{
- return (pipeline == OVNACT_P_INGRESS
- ? ep->ingress_ptable
- : ep->egress_ptable);
-}
-
-#define MAX_NESTED_ACTION_DEPTH 32
-
-/* Context maintained during ovnacts_parse(). */
-struct action_context {
- const struct ovnact_parse_params *pp; /* Parameters. */
- struct lexer *lexer; /* Lexer for pulling more tokens. */
- struct ofpbuf *ovnacts; /* Actions. */
- struct expr *prereqs; /* Prerequisites to apply to match. */
- int depth; /* Current nested action depth. */
-};
-
-static void parse_actions(struct action_context *, enum lex_type sentinel);
-
-static bool
-action_parse_field(struct action_context *ctx,
- int n_bits, bool rw, struct expr_field *f)
-{
- if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, f, &ctx->prereqs)) {
- return false;
- }
-
- char *error = expr_type_check(f, n_bits, rw);
- if (error) {
- lexer_error(ctx->lexer, "%s", error);
- free(error);
- return false;
- }
-
- return true;
-}
-
-static bool
-action_parse_port(struct action_context *ctx, uint16_t *port)
-{
- if (lexer_is_int(ctx->lexer)) {
- int value = ntohll(ctx->lexer->token.value.integer);
- if (value <= UINT16_MAX) {
- *port = value;
- lexer_get(ctx->lexer);
- return true;
- }
- }
- lexer_syntax_error(ctx->lexer, "expecting port number");
- return false;
-}
-
-/* Parses 'prerequisite' as an expression in the context of 'ctx', then adds it
- * as a conjunction with the existing 'ctx->prereqs'. */
-static void
-add_prerequisite(struct action_context *ctx, const char *prerequisite)
-{
- struct expr *expr;
- char *error;
-
- expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL, NULL,
- &error);
- ovs_assert(!error);
- ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
-}
-
-static void
-ovnact_null_free(struct ovnact_null *a OVS_UNUSED)
-{
-}
-
-static void
-format_OUTPUT(const struct ovnact_null *a OVS_UNUSED, struct ds *s)
-{
- ds_put_cstr(s, "output;");
-}
-
-static void
-emit_resubmit(struct ofpbuf *ofpacts, uint8_t ptable)
-{
- struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts);
- resubmit->in_port = OFPP_IN_PORT;
- resubmit->table_id = ptable;
-}
-
-static void
-encode_OUTPUT(const struct ovnact_null *a OVS_UNUSED,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- emit_resubmit(ofpacts, ep->output_ptable);
-}
-
-static void
-parse_NEXT(struct action_context *ctx)
-{
- if (!ctx->pp->n_tables) {
- lexer_error(ctx->lexer, "\"next\" action not allowed here.");
- return;
- }
-
- int pipeline = ctx->pp->pipeline;
- int table = ctx->pp->cur_ltable + 1;
- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
- if (lexer_is_int(ctx->lexer)) {
- lexer_get_int(ctx->lexer, &table);
- } else {
- do {
- if (lexer_match_id(ctx->lexer, "pipeline")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- if (lexer_match_id(ctx->lexer, "ingress")) {
- pipeline = OVNACT_P_INGRESS;
- } else if (lexer_match_id(ctx->lexer, "egress")) {
- pipeline = OVNACT_P_EGRESS;
- } else {
- lexer_syntax_error(
- ctx->lexer, "expecting \"ingress\" or \"egress\"");
- return;
- }
- } else if (lexer_match_id(ctx->lexer, "table")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS) ||
- !lexer_force_int(ctx->lexer, &table)) {
- return;
- }
- } else {
- lexer_syntax_error(ctx->lexer,
- "expecting \"pipeline\" or \"table\"");
- return;
- }
- } while (lexer_match(ctx->lexer, LEX_T_COMMA));
- }
- if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
- return;
- }
- }
-
- if (pipeline == OVNACT_P_EGRESS && ctx->pp->pipeline == OVNACT_P_INGRESS) {
- lexer_error(ctx->lexer,
- "\"next\" action cannot advance from ingress to egress "
- "pipeline (use \"output\" action instead)");
- } else if (table >= ctx->pp->n_tables) {
- lexer_error(ctx->lexer,
- "\"next\" action cannot advance beyond table %d.",
- ctx->pp->n_tables - 1);
- return;
- }
-
- struct ovnact_next *next = ovnact_put_NEXT(ctx->ovnacts);
- next->pipeline = pipeline;
- next->ltable = table;
- next->src_pipeline = ctx->pp->pipeline;
- next->src_ltable = ctx->pp->cur_ltable;
-}
-
-static void
-format_NEXT(const struct ovnact_next *next, struct ds *s)
-{
- if (next->pipeline != next->src_pipeline) {
- ds_put_format(s, "next(pipeline=%s, table=%d);",
- (next->pipeline == OVNACT_P_INGRESS
- ? "ingress" : "egress"),
- next->ltable);
- } else if (next->ltable != next->src_ltable + 1) {
- ds_put_format(s, "next(%d);", next->ltable);
- } else {
- ds_put_cstr(s, "next;");
- }
-}
-
-static void
-encode_NEXT(const struct ovnact_next *next,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- emit_resubmit(ofpacts, first_ptable(ep, next->pipeline) + next->ltable);
-}
-
-static void
-ovnact_next_free(struct ovnact_next *a OVS_UNUSED)
-{
-}
-
-static void
-parse_LOAD(struct action_context *ctx, const struct expr_field *lhs)
-{
- size_t ofs = ctx->ovnacts->size;
- struct ovnact_load *load;
- if (lhs->symbol->ovn_field) {
- load = ovnact_put_OVNFIELD_LOAD(ctx->ovnacts);
- } else {
- load = ovnact_put_LOAD(ctx->ovnacts);
- }
-
- load->dst = *lhs;
-
- char *error = expr_type_check(lhs, lhs->n_bits, true);
- if (error) {
- ctx->ovnacts->size = ofs;
- lexer_error(ctx->lexer, "%s", error);
- free(error);
- return;
- }
- if (!expr_constant_parse(ctx->lexer, lhs, &load->imm)) {
- ctx->ovnacts->size = ofs;
- return;
- }
-}
-
-static enum expr_constant_type
-load_type(const struct ovnact_load *load)
-{
- return load->dst.symbol->width > 0 ? EXPR_C_INTEGER : EXPR_C_STRING;
-}
-
-static void
-format_LOAD(const struct ovnact_load *load, struct ds *s)
-{
- expr_field_format(&load->dst, s);
- ds_put_cstr(s, " = ");
- expr_constant_format(&load->imm, load_type(load), s);
- ds_put_char(s, ';');
-}
-
-static void
-encode_LOAD(const struct ovnact_load *load,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- const union expr_constant *c = &load->imm;
- struct mf_subfield dst = expr_resolve_field(&load->dst);
- struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts, dst.field,
- NULL, NULL);
-
- if (load->dst.symbol->width) {
- bitwise_copy(&c->value, sizeof c->value, 0,
- sf->value, dst.field->n_bytes, dst.ofs,
- dst.n_bits);
- if (c->masked) {
- bitwise_copy(&c->mask, sizeof c->mask, 0,
- ofpact_set_field_mask(sf), dst.field->n_bytes,
- dst.ofs, dst.n_bits);
- } else {
- bitwise_one(ofpact_set_field_mask(sf), dst.field->n_bytes,
- dst.ofs, dst.n_bits);
- }
- } else {
- uint32_t port;
- if (!ep->lookup_port(ep->aux, load->imm.string, &port)) {
- port = 0;
- }
- bitwise_put(port, sf->value,
- sf->field->n_bytes, 0, sf->field->n_bits);
- bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, 0,
- sf->field->n_bits);
- }
-}
-
-static void
-ovnact_load_free(struct ovnact_load *load)
-{
- expr_constant_destroy(&load->imm, load_type(load));
-}
-
-static void
-format_assignment(const struct ovnact_move *move, const char *operator,
- struct ds *s)
-{
- expr_field_format(&move->lhs, s);
- ds_put_format(s, " %s ", operator);
- expr_field_format(&move->rhs, s);
- ds_put_char(s, ';');
-}
-
-static void
-format_MOVE(const struct ovnact_move *move, struct ds *s)
-{
- format_assignment(move, "=", s);
-}
-
-static void
-format_EXCHANGE(const struct ovnact_move *move, struct ds *s)
-{
- format_assignment(move, "<->", s);
-}
-
-static void
-parse_assignment_action(struct action_context *ctx, bool exchange,
- const struct expr_field *lhs)
-{
- struct expr_field rhs;
- if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &rhs, &ctx->prereqs)) {
- return;
- }
-
- const struct expr_symbol *ls = lhs->symbol;
- const struct expr_symbol *rs = rhs.symbol;
- if ((ls->width != 0) != (rs->width != 0)) {
- if (exchange) {
- lexer_error(ctx->lexer,
- "Can't exchange %s field (%s) with %s field (%s).",
- ls->width ? "integer" : "string",
- ls->name,
- rs->width ? "integer" : "string",
- rs->name);
- } else {
- lexer_error(ctx->lexer,
- "Can't assign %s field (%s) to %s field (%s).",
- rs->width ? "integer" : "string",
- rs->name,
- ls->width ? "integer" : "string",
- ls->name);
- }
- return;
- }
-
- if (lhs->n_bits != rhs.n_bits) {
- if (exchange) {
- lexer_error(ctx->lexer,
- "Can't exchange %d-bit field with %d-bit field.",
- lhs->n_bits, rhs.n_bits);
- } else {
- lexer_error(ctx->lexer,
- "Can't assign %d-bit value to %d-bit destination.",
- rhs.n_bits, lhs->n_bits);
- }
- return;
- } else if (!lhs->n_bits &&
- ls->field->n_bits != rs->field->n_bits) {
- lexer_error(ctx->lexer, "String fields %s and %s are incompatible for "
- "%s.", ls->name, rs->name,
- exchange ? "exchange" : "assignment");
- return;
- }
-
- char *error = expr_type_check(lhs, lhs->n_bits, true);
- if (!error) {
- error = expr_type_check(&rhs, rhs.n_bits, true);
- }
- if (error) {
- lexer_error(ctx->lexer, "%s", error);
- free(error);
- return;
- }
-
- struct ovnact_move *move;
- move = (exchange
- ? ovnact_put_EXCHANGE(ctx->ovnacts)
- : ovnact_put_MOVE(ctx->ovnacts));
- move->lhs = *lhs;
- move->rhs = rhs;
-}
-
-static void
-encode_MOVE(const struct ovnact_move *move,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
- orm->src = expr_resolve_field(&move->rhs);
- orm->dst = expr_resolve_field(&move->lhs);
-}
-
-static void
-encode_EXCHANGE(const struct ovnact_move *xchg,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- ofpact_put_STACK_PUSH(ofpacts)->subfield = expr_resolve_field(&xchg->rhs);
- ofpact_put_STACK_PUSH(ofpacts)->subfield = expr_resolve_field(&xchg->lhs);
- ofpact_put_STACK_POP(ofpacts)->subfield = expr_resolve_field(&xchg->rhs);
- ofpact_put_STACK_POP(ofpacts)->subfield = expr_resolve_field(&xchg->lhs);
-}
-
-static void
-ovnact_move_free(struct ovnact_move *move OVS_UNUSED)
-{
-}
-
-static void
-parse_DEC_TTL(struct action_context *ctx)
-{
- lexer_force_match(ctx->lexer, LEX_T_DECREMENT);
- ovnact_put_DEC_TTL(ctx->ovnacts);
- add_prerequisite(ctx, "ip");
-}
-
-static void
-format_DEC_TTL(const struct ovnact_null *null OVS_UNUSED, struct ds *s)
-{
- ds_put_cstr(s, "ip.ttl--;");
-}
-
-static void
-encode_DEC_TTL(const struct ovnact_null *null OVS_UNUSED,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- ofpact_put_DEC_TTL(ofpacts);
-}
-
-static void
-parse_CT_NEXT(struct action_context *ctx)
-{
- if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
- lexer_error(ctx->lexer,
- "\"ct_next\" action not allowed in last table.");
- return;
- }
-
- add_prerequisite(ctx, "ip");
- ovnact_put_CT_NEXT(ctx->ovnacts)->ltable = ctx->pp->cur_ltable + 1;
-}
-
-static void
-format_CT_NEXT(const struct ovnact_ct_next *ct_next OVS_UNUSED, struct ds *s)
-{
- ds_put_cstr(s, "ct_next;");
-}
-
-static void
-encode_CT_NEXT(const struct ovnact_ct_next *ct_next,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
- ct->recirc_table = first_ptable(ep, ep->pipeline) + ct_next->ltable;
- ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE)
- : mf_from_id(MFF_LOG_DNAT_ZONE);
- ct->zone_src.ofs = 0;
- ct->zone_src.n_bits = 16;
- ofpact_finish(ofpacts, &ct->ofpact);
-}
-
-static void
-ovnact_ct_next_free(struct ovnact_ct_next *a OVS_UNUSED)
-{
-}
-
-static void
-parse_ct_commit_arg(struct action_context *ctx,
- struct ovnact_ct_commit *cc)
-{
- if (lexer_match_id(ctx->lexer, "ct_mark")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- if (ctx->lexer->token.type == LEX_T_INTEGER) {
- cc->ct_mark = ntohll(ctx->lexer->token.value.integer);
- cc->ct_mark_mask = UINT32_MAX;
- } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) {
- cc->ct_mark = ntohll(ctx->lexer->token.value.integer);
- cc->ct_mark_mask = ntohll(ctx->lexer->token.mask.integer);
- } else {
- lexer_syntax_error(ctx->lexer, "expecting integer");
- return;
- }
- lexer_get(ctx->lexer);
- } else if (lexer_match_id(ctx->lexer, "ct_label")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- if (ctx->lexer->token.type == LEX_T_INTEGER) {
- cc->ct_label = ctx->lexer->token.value.be128_int;
- cc->ct_label_mask = OVS_BE128_MAX;
- } else if (ctx->lexer->token.type == LEX_T_MASKED_INTEGER) {
- cc->ct_label = ctx->lexer->token.value.be128_int;
- cc->ct_label_mask = ctx->lexer->token.mask.be128_int;
- } else {
- lexer_syntax_error(ctx->lexer, "expecting integer");
- return;
- }
- lexer_get(ctx->lexer);
- } else {
- lexer_syntax_error(ctx->lexer, NULL);
- }
-}
-
-static void
-parse_CT_COMMIT(struct action_context *ctx)
-{
- add_prerequisite(ctx, "ip");
-
- struct ovnact_ct_commit *ct_commit = ovnact_put_CT_COMMIT(ctx->ovnacts);
- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
- while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- parse_ct_commit_arg(ctx, ct_commit);
- if (ctx->lexer->error) {
- return;
- }
- lexer_match(ctx->lexer, LEX_T_COMMA);
- }
- }
-}
-
-static void
-format_CT_COMMIT(const struct ovnact_ct_commit *cc, struct ds *s)
-{
- ds_put_cstr(s, "ct_commit(");
- if (cc->ct_mark_mask) {
- ds_put_format(s, "ct_mark=%#"PRIx32, cc->ct_mark);
- if (cc->ct_mark_mask != UINT32_MAX) {
- ds_put_format(s, "/%#"PRIx32, cc->ct_mark_mask);
- }
- }
- if (!ovs_be128_is_zero(cc->ct_label_mask)) {
- if (ds_last(s) != '(') {
- ds_put_cstr(s, ", ");
- }
-
- ds_put_format(s, "ct_label=");
- ds_put_hex(s, &cc->ct_label, sizeof cc->ct_label);
- if (!ovs_be128_equals(cc->ct_label_mask, OVS_BE128_MAX)) {
- ds_put_char(s, '/');
- ds_put_hex(s, &cc->ct_label_mask, sizeof cc->ct_label_mask);
- }
- }
- if (!ds_chomp(s, '(')) {
- ds_put_char(s, ')');
- }
- ds_put_char(s, ';');
-}
-
-static void
-encode_CT_COMMIT(const struct ovnact_ct_commit *cc,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
- ct->flags = NX_CT_F_COMMIT;
- ct->recirc_table = NX_CT_RECIRC_NONE;
- ct->zone_src.field = mf_from_id(MFF_LOG_CT_ZONE);
- ct->zone_src.ofs = 0;
- ct->zone_src.n_bits = 16;
-
- size_t set_field_offset = ofpacts->size;
- ofpbuf_pull(ofpacts, set_field_offset);
-
- if (cc->ct_mark_mask) {
- const ovs_be32 value = htonl(cc->ct_mark);
- const ovs_be32 mask = htonl(cc->ct_mark_mask);
- ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_MARK), &value, &mask);
- }
-
- if (!ovs_be128_is_zero(cc->ct_label_mask)) {
- ofpact_put_set_field(ofpacts, mf_from_id(MFF_CT_LABEL), &cc->ct_label,
- &cc->ct_label_mask);
- }
-
- ofpacts->header = ofpbuf_push_uninit(ofpacts, set_field_offset);
- ct = ofpacts->header;
- ofpact_finish(ofpacts, &ct->ofpact);
-}
-
-static void
-ovnact_ct_commit_free(struct ovnact_ct_commit *cc OVS_UNUSED)
-{
-}
-
-static void
-parse_ct_nat(struct action_context *ctx, const char *name,
- struct ovnact_ct_nat *cn)
-{
- add_prerequisite(ctx, "ip");
-
- if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
- lexer_error(ctx->lexer,
- "\"%s\" action not allowed in last table.", name);
- return;
- }
- cn->ltable = ctx->pp->cur_ltable + 1;
-
- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
- if (ctx->lexer->token.type != LEX_T_INTEGER
- || ctx->lexer->token.format != LEX_F_IPV4) {
- lexer_syntax_error(ctx->lexer, "expecting IPv4 address");
- return;
- }
- cn->ip = ctx->lexer->token.value.ipv4;
- lexer_get(ctx->lexer);
-
- if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
- return;
- }
- }
-}
-
-static void
-parse_CT_DNAT(struct action_context *ctx)
-{
- parse_ct_nat(ctx, "ct_dnat", ovnact_put_CT_DNAT(ctx->ovnacts));
-}
-
-static void
-parse_CT_SNAT(struct action_context *ctx)
-{
- parse_ct_nat(ctx, "ct_snat", ovnact_put_CT_SNAT(ctx->ovnacts));
-}
-
-static void
-format_ct_nat(const struct ovnact_ct_nat *cn, const char *name, struct ds *s)
-{
- ds_put_cstr(s, name);
- if (cn->ip) {
- ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ip));
- }
- ds_put_char(s, ';');
-}
-
-static void
-format_CT_DNAT(const struct ovnact_ct_nat *cn, struct ds *s)
-{
- format_ct_nat(cn, "ct_dnat", s);
-}
-
-static void
-format_CT_SNAT(const struct ovnact_ct_nat *cn, struct ds *s)
-{
- format_ct_nat(cn, "ct_snat", s);
-}
-
-static void
-encode_ct_nat(const struct ovnact_ct_nat *cn,
- const struct ovnact_encode_params *ep,
- bool snat, struct ofpbuf *ofpacts)
-{
- const size_t ct_offset = ofpacts->size;
- ofpbuf_pull(ofpacts, ct_offset);
-
- struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
- ct->recirc_table = cn->ltable + first_ptable(ep, ep->pipeline);
- if (snat) {
- ct->zone_src.field = mf_from_id(MFF_LOG_SNAT_ZONE);
- } else {
- ct->zone_src.field = mf_from_id(MFF_LOG_DNAT_ZONE);
- }
- ct->zone_src.ofs = 0;
- ct->zone_src.n_bits = 16;
- ct->flags = 0;
- ct->alg = 0;
-
- struct ofpact_nat *nat;
- size_t nat_offset;
- nat_offset = ofpacts->size;
- ofpbuf_pull(ofpacts, nat_offset);
-
- nat = ofpact_put_NAT(ofpacts);
- nat->flags = 0;
- nat->range_af = AF_UNSPEC;
-
- if (cn->ip) {
- nat->range_af = AF_INET;
- nat->range.addr.ipv4.min = cn->ip;
- if (snat) {
- nat->flags |= NX_NAT_F_SRC;
- } else {
- nat->flags |= NX_NAT_F_DST;
- }
- }
-
- ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
- ct = ofpacts->header;
- if (cn->ip) {
- ct->flags |= NX_CT_F_COMMIT;
- }
- ofpact_finish(ofpacts, &ct->ofpact);
- ofpbuf_push_uninit(ofpacts, ct_offset);
-}
-
-static void
-encode_CT_DNAT(const struct ovnact_ct_nat *cn,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_ct_nat(cn, ep, false, ofpacts);
-}
-
-static void
-encode_CT_SNAT(const struct ovnact_ct_nat *cn,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_ct_nat(cn, ep, true, ofpacts);
-}
-
-static void
-ovnact_ct_nat_free(struct ovnact_ct_nat *ct_nat OVS_UNUSED)
-{
-}
-
-static void
-parse_ct_lb_action(struct action_context *ctx)
-{
- if (ctx->pp->cur_ltable >= ctx->pp->n_tables) {
- lexer_error(ctx->lexer, "\"ct_lb\" action not allowed in last table.");
- return;
- }
-
- add_prerequisite(ctx, "ip");
-
- struct ovnact_ct_lb_dst *dsts = NULL;
- size_t allocated_dsts = 0;
- size_t n_dsts = 0;
-
- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
- while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- struct ovnact_ct_lb_dst dst;
- if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) {
- /* IPv6 address and port */
- if (ctx->lexer->token.type != LEX_T_INTEGER
- || ctx->lexer->token.format != LEX_F_IPV6) {
- free(dsts);
- lexer_syntax_error(ctx->lexer, "expecting IPv6 address");
- return;
- }
- dst.family = AF_INET6;
- dst.ipv6 = ctx->lexer->token.value.ipv6;
-
- lexer_get(ctx->lexer);
- if (!lexer_match(ctx->lexer, LEX_T_RSQUARE)) {
- free(dsts);
- lexer_syntax_error(ctx->lexer, "no closing square "
- "bracket");
- return;
- }
- dst.port = 0;
- if (lexer_match(ctx->lexer, LEX_T_COLON)
- && !action_parse_port(ctx, &dst.port)) {
- free(dsts);
- return;
- }
- } else {
- if (ctx->lexer->token.type != LEX_T_INTEGER
- || (ctx->lexer->token.format != LEX_F_IPV4
- && ctx->lexer->token.format != LEX_F_IPV6)) {
- free(dsts);
- lexer_syntax_error(ctx->lexer, "expecting IP address");
- return;
- }
-
- /* Parse IP. */
- if (ctx->lexer->token.format == LEX_F_IPV4) {
- dst.family = AF_INET;
- dst.ipv4 = ctx->lexer->token.value.ipv4;
- } else {
- dst.family = AF_INET6;
- dst.ipv6 = ctx->lexer->token.value.ipv6;
- }
-
- lexer_get(ctx->lexer);
- dst.port = 0;
- if (lexer_match(ctx->lexer, LEX_T_COLON)) {
- if (dst.family == AF_INET6) {
- free(dsts);
- lexer_syntax_error(ctx->lexer, "IPv6 address needs "
- "square brackets if port is included");
- return;
- } else if (!action_parse_port(ctx, &dst.port)) {
- free(dsts);
- return;
- }
- }
- }
- lexer_match(ctx->lexer, LEX_T_COMMA);
-
- /* Append to dsts. */
- if (n_dsts >= allocated_dsts) {
- dsts = x2nrealloc(dsts, &allocated_dsts, sizeof *dsts);
- }
- dsts[n_dsts++] = dst;
- }
- }
-
- struct ovnact_ct_lb *cl = ovnact_put_CT_LB(ctx->ovnacts);
- cl->ltable = ctx->pp->cur_ltable + 1;
- cl->dsts = dsts;
- cl->n_dsts = n_dsts;
-}
-
-static void
-format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s)
-{
- ds_put_cstr(s, "ct_lb");
- if (cl->n_dsts) {
- ds_put_char(s, '(');
- for (size_t i = 0; i < cl->n_dsts; i++) {
- if (i) {
- ds_put_cstr(s, ", ");
- }
-
- const struct ovnact_ct_lb_dst *dst = &cl->dsts[i];
- if (dst->family == AF_INET) {
- ds_put_format(s, IP_FMT, IP_ARGS(dst->ipv4));
- if (dst->port) {
- ds_put_format(s, ":%"PRIu16, dst->port);
- }
- } else {
- if (dst->port) {
- ds_put_char(s, '[');
- }
- ipv6_format_addr(&dst->ipv6, s);
- if (dst->port) {
- ds_put_format(s, "]:%"PRIu16, dst->port);
- }
- }
- }
- ds_put_char(s, ')');
- }
- ds_put_char(s, ';');
-}
-
-static void
-encode_CT_LB(const struct ovnact_ct_lb *cl,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- uint8_t recirc_table = cl->ltable + first_ptable(ep, ep->pipeline);
- if (!cl->n_dsts) {
- /* ct_lb without any destinations means that this is an established
- * connection and we just need to do a NAT. */
- const size_t ct_offset = ofpacts->size;
- ofpbuf_pull(ofpacts, ct_offset);
-
- struct ofpact_conntrack *ct = ofpact_put_CT(ofpacts);
- struct ofpact_nat *nat;
- size_t nat_offset;
- ct->zone_src.field = ep->is_switch ? mf_from_id(MFF_LOG_CT_ZONE)
- : mf_from_id(MFF_LOG_DNAT_ZONE);
- ct->zone_src.ofs = 0;
- ct->zone_src.n_bits = 16;
- ct->flags = 0;
- ct->recirc_table = recirc_table;
- ct->alg = 0;
-
- nat_offset = ofpacts->size;
- ofpbuf_pull(ofpacts, nat_offset);
-
- nat = ofpact_put_NAT(ofpacts);
- nat->flags = 0;
- nat->range_af = AF_UNSPEC;
-
- ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
- ct = ofpacts->header;
- ofpact_finish(ofpacts, &ct->ofpact);
- ofpbuf_push_uninit(ofpacts, ct_offset);
- return;
- }
-
- uint32_t table_id = 0;
- struct ofpact_group *og;
- uint32_t zone_reg = ep->is_switch ? MFF_LOG_CT_ZONE - MFF_REG0
- : MFF_LOG_DNAT_ZONE - MFF_REG0;
-
- struct ds ds = DS_EMPTY_INITIALIZER;
- ds_put_format(&ds, "type=select,selection_method=dp_hash");
-
- BUILD_ASSERT(MFF_LOG_CT_ZONE >= MFF_REG0);
- BUILD_ASSERT(MFF_LOG_CT_ZONE < MFF_REG0 + FLOW_N_REGS);
- BUILD_ASSERT(MFF_LOG_DNAT_ZONE >= MFF_REG0);
- BUILD_ASSERT(MFF_LOG_DNAT_ZONE < MFF_REG0 + FLOW_N_REGS);
- for (size_t bucket_id = 0; bucket_id < cl->n_dsts; bucket_id++) {
- const struct ovnact_ct_lb_dst *dst = &cl->dsts[bucket_id];
- char ip_addr[INET6_ADDRSTRLEN];
- if (dst->family == AF_INET) {
- inet_ntop(AF_INET, &dst->ipv4, ip_addr, sizeof ip_addr);
- } else {
- inet_ntop(AF_INET6, &dst->ipv6, ip_addr, sizeof ip_addr);
- }
- ds_put_format(&ds, ",bucket=bucket_id=%"PRIuSIZE",weight:100,actions="
- "ct(nat(dst=%s%s%s", bucket_id,
- dst->family == AF_INET6 && dst->port ? "[" : "",
- ip_addr,
- dst->family == AF_INET6 && dst->port ? "]" : "");
- if (dst->port) {
- ds_put_format(&ds, ":%"PRIu16, dst->port);
- }
- ds_put_format(&ds, "),commit,table=%d,zone=NXM_NX_REG%d[0..15])",
- recirc_table, zone_reg);
- }
-
- table_id = ovn_extend_table_assign_id(ep->group_table, ds_cstr(&ds),
- ep->lflow_uuid);
- ds_destroy(&ds);
- if (table_id == EXT_TABLE_ID_INVALID) {
- return;
- }
-
- /* Create an action to set the group. */
- og = ofpact_put_GROUP(ofpacts);
- og->group_id = table_id;
-}
-
-static void
-ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb)
-{
- free(ct_lb->dsts);
-}
-
-static void
-format_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED, struct ds *s)
-{
- ds_put_cstr(s, "ct_clear;");
-}
-
-static void
-encode_CT_CLEAR(const struct ovnact_null *null OVS_UNUSED,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- ofpact_put_CT_CLEAR(ofpacts);
-}
-
-/* Implements the "arp", "nd_na", and "clone" actions, which execute nested
- * actions on a packet derived from the one being processed. */
-static void
-parse_nested_action(struct action_context *ctx, enum ovnact_type type,
- const char *prereq)
-{
- if (!lexer_force_match(ctx->lexer, LEX_T_LCURLY)) {
- return;
- }
-
- if (ctx->depth + 1 == MAX_NESTED_ACTION_DEPTH) {
- lexer_error(ctx->lexer, "maximum depth of nested actions reached");
- return;
- }
-
- uint64_t stub[1024 / 8];
- struct ofpbuf nested = OFPBUF_STUB_INITIALIZER(stub);
-
- struct action_context inner_ctx = {
- .pp = ctx->pp,
- .lexer = ctx->lexer,
- .ovnacts = &nested,
- .prereqs = NULL,
- .depth = ctx->depth + 1,
- };
- parse_actions(&inner_ctx, LEX_T_RCURLY);
-
- if (prereq) {
- /* XXX Not really sure what we should do with prerequisites for "arp"
- * and "nd_na" actions. */
- expr_destroy(inner_ctx.prereqs);
- add_prerequisite(ctx, prereq);
- } else {
- /* For "clone", the inner prerequisites should just add to the outer
- * ones. */
- ctx->prereqs = expr_combine(EXPR_T_AND,
- inner_ctx.prereqs, ctx->prereqs);
- }
-
- if (inner_ctx.lexer->error) {
- ovnacts_free(nested.data, nested.size);
- ofpbuf_uninit(&nested);
- return;
- }
-
- struct ovnact_nest *on = ovnact_put(ctx->ovnacts, type,
- OVNACT_ALIGN(sizeof *on));
- on->nested_len = nested.size;
- on->nested = ofpbuf_steal_data(&nested);
-}
-
-static void
-parse_ARP(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ARP, "ip4");
-}
-
-static void
-parse_ICMP4(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ICMP4, "ip4");
-}
-
-static void
-parse_ICMP4_ERROR(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ICMP4_ERROR, "ip4");
-}
-
-static void
-parse_ICMP6(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ICMP6, "ip6");
-}
-
-static void
-parse_TCP_RESET(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_TCP_RESET, "tcp");
-}
-
-static void
-parse_ND_NA(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ND_NA, "nd_ns");
-}
-
-static void
-parse_ND_NA_ROUTER(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ND_NA_ROUTER, "nd_ns");
-}
-
-static void
-parse_ND_NS(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_ND_NS, "ip6");
-}
-
-static void
-parse_CLONE(struct action_context *ctx)
-{
- parse_nested_action(ctx, OVNACT_CLONE, NULL);
-}
-
-static void
-format_nested_action(const struct ovnact_nest *on, const char *name,
- struct ds *s)
-{
- ds_put_format(s, "%s { ", name);
- ovnacts_format(on->nested, on->nested_len, s);
- ds_put_format(s, " };");
-}
-
-static void
-format_ARP(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "arp", s);
-}
-
-static void
-format_ICMP4(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "icmp4", s);
-}
-
-static void
-format_ICMP4_ERROR(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "icmp4_error", s);
-}
-
-static void
-format_ICMP6(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "icmp6", s);
-}
-
-static void
-format_IGMP(const struct ovnact_null *a OVS_UNUSED, struct ds *s)
-{
- ds_put_cstr(s, "igmp;");
-}
-
-static void
-format_TCP_RESET(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "tcp_reset", s);
-}
-
-static void
-format_ND_NA(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "nd_na", s);
-}
-
-static void
-format_ND_NA_ROUTER(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "nd_na_router", s);
-}
-
-static void
-format_ND_NS(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "nd_ns", s);
-}
-
-static void
-format_CLONE(const struct ovnact_nest *nest, struct ds *s)
-{
- format_nested_action(nest, "clone", s);
-}
-
-static void
-format_TRIGGER_EVENT(const struct ovnact_controller_event *event,
- struct ds *s)
-{
- ds_put_format(s, "trigger_event(event = \"%s\"",
- event_to_string(event->event_type));
- for (const struct ovnact_gen_option *o = event->options;
- o < &event->options[event->n_options]; o++) {
- ds_put_cstr(s, ", ");
- ds_put_format(s, "%s = ", o->option->name);
- expr_constant_set_format(&o->value, s);
- }
- ds_put_cstr(s, ");");
-}
-
-static void
-encode_nested_actions(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- enum action_opcode opcode,
- struct ofpbuf *ofpacts)
-{
- /* Convert nested actions into ofpacts. */
- uint64_t inner_ofpacts_stub[1024 / 8];
- struct ofpbuf inner_ofpacts = OFPBUF_STUB_INITIALIZER(inner_ofpacts_stub);
- ovnacts_encode(on->nested, on->nested_len, ep, &inner_ofpacts);
-
- /* Add a "controller" action with the actions nested inside "{...}",
- * converted to OpenFlow, as its userdata. ovn-controller will convert the
- * packet to ARP or NA and then send the packet and actions back to the
- * switch inside an OFPT_PACKET_OUT message. */
- size_t oc_offset = encode_start_controller_op(opcode, false,
- NX_CTLR_NO_METER, ofpacts);
- ofpacts_put_openflow_actions(inner_ofpacts.data, inner_ofpacts.size,
- ofpacts, OFP13_VERSION);
- encode_finish_controller_op(oc_offset, ofpacts);
-
- /* Free memory. */
- ofpbuf_uninit(&inner_ofpacts);
-}
-
-static void
-encode_ARP(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ARP, ofpacts);
-}
-
-static void
-encode_ICMP4(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts);
-}
-
-static void
-encode_ICMP4_ERROR(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ICMP4_ERROR, ofpacts);
-}
-
-static void
-encode_ICMP6(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ICMP, ofpacts);
-}
-
-static void
-encode_IGMP(const struct ovnact_null *a OVS_UNUSED,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- encode_controller_op(ACTION_OPCODE_IGMP, ofpacts);
-}
-
-static void
-encode_TCP_RESET(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_TCP_RESET, ofpacts);
-}
-
-static void
-encode_ND_NA(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA, ofpacts);
-}
-
-static void
-encode_ND_NA_ROUTER(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ND_NA_ROUTER, ofpacts);
-}
-
-static void
-encode_ND_NS(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_nested_actions(on, ep, ACTION_OPCODE_ND_NS, ofpacts);
-}
-
-static void
-encode_CLONE(const struct ovnact_nest *on,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- size_t ofs = ofpacts->size;
- ofpact_put_CLONE(ofpacts);
- ovnacts_encode(on->nested, on->nested_len, ep, ofpacts);
-
- struct ofpact_nest *clone = ofpbuf_at_assert(ofpacts, ofs, sizeof *clone);
- ofpacts->header = clone;
- ofpact_finish_CLONE(ofpacts, &clone);
-}
-
-static void
-encode_event_empty_lb_backends_opts(struct ofpbuf *ofpacts,
- const struct ovnact_controller_event *event)
-{
- for (const struct ovnact_gen_option *o = event->options;
- o < &event->options[event->n_options]; o++) {
- struct controller_event_opt_header *hdr =
- ofpbuf_put_uninit(ofpacts, sizeof *hdr);
- const union expr_constant *c = o->value.values;
- size_t size;
- hdr->opt_code = htons(o->option->code);
- if (!strcmp(o->option->type, "str")) {
- size = strlen(c->string);
- hdr->size = htons(size);
- ofpbuf_put(ofpacts, c->string, size);
- } else {
- /* All empty_lb_backends fields are of type 'str' */
- OVS_NOT_REACHED();
- }
- }
-}
-
-static void
-encode_TRIGGER_EVENT(const struct ovnact_controller_event *event,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- size_t oc_offset;
-
- oc_offset = encode_start_controller_op(ACTION_OPCODE_EVENT, false,
- NX_CTLR_NO_METER, ofpacts);
- ovs_be32 ofs = htonl(event->event_type);
- ofpbuf_put(ofpacts, &ofs, sizeof ofs);
-
- switch (event->event_type) {
- case OVN_EVENT_EMPTY_LB_BACKENDS:
- encode_event_empty_lb_backends_opts(ofpacts, event);
- break;
- case OVN_EVENT_MAX:
- default:
- OVS_NOT_REACHED();
- }
-
- encode_finish_controller_op(oc_offset, ofpacts);
-}
-
-static void
-ovnact_nest_free(struct ovnact_nest *on)
-{
- ovnacts_free(on->nested, on->nested_len);
- free(on->nested);
-}
-
-static void
-parse_get_mac_bind(struct action_context *ctx, int width,
- struct ovnact_get_mac_bind *get_mac)
-{
- lexer_force_match(ctx->lexer, LEX_T_LPAREN);
- action_parse_field(ctx, 0, false, &get_mac->port);
- lexer_force_match(ctx->lexer, LEX_T_COMMA);
- action_parse_field(ctx, width, false, &get_mac->ip);
- lexer_force_match(ctx->lexer, LEX_T_RPAREN);
-}
-
-static void
-format_get_mac_bind(const struct ovnact_get_mac_bind *get_mac,
- const char *name, struct ds *s)
-{
- ds_put_format(s, "%s(", name);
- expr_field_format(&get_mac->port, s);
- ds_put_cstr(s, ", ");
- expr_field_format(&get_mac->ip, s);
- ds_put_cstr(s, ");");
-}
-
-static void
-format_GET_ARP(const struct ovnact_get_mac_bind *get_mac, struct ds *s)
-{
- format_get_mac_bind(get_mac, "get_arp", s);
-}
-
-static void
-format_GET_ND(const struct ovnact_get_mac_bind *get_mac, struct ds *s)
-{
- format_get_mac_bind(get_mac, "get_nd", s);
-}
-
-static void
-encode_get_mac(const struct ovnact_get_mac_bind *get_mac,
- enum mf_field_id ip_field,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- const struct arg args[] = {
- { expr_resolve_field(&get_mac->port), MFF_LOG_OUTPORT },
- { expr_resolve_field(&get_mac->ip), ip_field },
- };
- encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
-
- put_load(0, MFF_ETH_DST, 0, 48, ofpacts);
- emit_resubmit(ofpacts, ep->mac_bind_ptable);
-
- encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
-}
-
-static void
-encode_GET_ARP(const struct ovnact_get_mac_bind *get_mac,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_get_mac(get_mac, MFF_REG0, ep, ofpacts);
-}
-
-static void
-encode_GET_ND(const struct ovnact_get_mac_bind *get_mac,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- encode_get_mac(get_mac, MFF_XXREG0, ep, ofpacts);
-}
-
-static void
-ovnact_get_mac_bind_free(struct ovnact_get_mac_bind *get_mac OVS_UNUSED)
-{
-}
-
-static void
-parse_put_mac_bind(struct action_context *ctx, int width,
- struct ovnact_put_mac_bind *put_mac)
-{
- lexer_force_match(ctx->lexer, LEX_T_LPAREN);
- action_parse_field(ctx, 0, false, &put_mac->port);
- lexer_force_match(ctx->lexer, LEX_T_COMMA);
- action_parse_field(ctx, width, false, &put_mac->ip);
- lexer_force_match(ctx->lexer, LEX_T_COMMA);
- action_parse_field(ctx, 48, false, &put_mac->mac);
- lexer_force_match(ctx->lexer, LEX_T_RPAREN);
-}
-
-static void
-format_put_mac_bind(const struct ovnact_put_mac_bind *put_mac,
- const char *name, struct ds *s)
-{
- ds_put_format(s, "%s(", name);
- expr_field_format(&put_mac->port, s);
- ds_put_cstr(s, ", ");
- expr_field_format(&put_mac->ip, s);
- ds_put_cstr(s, ", ");
- expr_field_format(&put_mac->mac, s);
- ds_put_cstr(s, ");");
-}
-
-static void
-format_PUT_ARP(const struct ovnact_put_mac_bind *put_mac, struct ds *s)
-{
- format_put_mac_bind(put_mac, "put_arp", s);
-}
-
-static void
-format_PUT_ND(const struct ovnact_put_mac_bind *put_mac, struct ds *s)
-{
- format_put_mac_bind(put_mac, "put_nd", s);
-}
-
-static void
-encode_put_mac(const struct ovnact_put_mac_bind *put_mac,
- enum mf_field_id ip_field, enum action_opcode opcode,
- struct ofpbuf *ofpacts)
-{
- const struct arg args[] = {
- { expr_resolve_field(&put_mac->port), MFF_LOG_INPORT },
- { expr_resolve_field(&put_mac->ip), ip_field },
- { expr_resolve_field(&put_mac->mac), MFF_ETH_SRC }
- };
- encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
- encode_controller_op(opcode, ofpacts);
- encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
-}
-
-static void
-encode_PUT_ARP(const struct ovnact_put_mac_bind *put_mac,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- encode_put_mac(put_mac, MFF_REG0, ACTION_OPCODE_PUT_ARP, ofpacts);
-}
-
-static void
-encode_PUT_ND(const struct ovnact_put_mac_bind *put_mac,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- encode_put_mac(put_mac, MFF_XXREG0, ACTION_OPCODE_PUT_ND, ofpacts);
-}
-
-static void
-ovnact_put_mac_bind_free(struct ovnact_put_mac_bind *put_mac OVS_UNUSED)
-{
-}
-
-static void
-parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
- const struct hmap *gen_opts, const char *opts_type)
-{
- if (ctx->lexer->token.type != LEX_T_ID) {
- lexer_syntax_error(ctx->lexer, NULL);
- return;
- }
-
- o->option = gen_opts ? gen_opts_find(gen_opts, ctx->lexer->token.s) : NULL;
- if (!o->option) {
- lexer_syntax_error(ctx->lexer, "expecting %s option name", opts_type);
- return;
- }
- lexer_get(ctx->lexer);
-
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
-
- if (!expr_constant_set_parse(ctx->lexer, &o->value)) {
- memset(&o->value, 0, sizeof o->value);
- return;
- }
-
- if (!strcmp(o->option->type, "str")) {
- if (o->value.type != EXPR_C_STRING) {
- lexer_error(ctx->lexer, "%s option %s requires string value.",
- opts_type, o->option->name);
- return;
- }
- } else {
- if (o->value.type != EXPR_C_INTEGER) {
- lexer_error(ctx->lexer, "%s option %s requires numeric value.",
- opts_type, o->option->name);
- return;
- }
- }
-}
-
-static const struct ovnact_gen_option *
-find_offerip(const struct ovnact_gen_option *options, size_t n)
-{
- for (const struct ovnact_gen_option *o = options; o < &options[n]; o++) {
- if (o->option->code == 0) {
- return o;
- }
- }
- return NULL;
-}
-
-static void
-free_gen_options(struct ovnact_gen_option *options, size_t n)
-{
- for (struct ovnact_gen_option *o = options; o < &options[n]; o++) {
- expr_constant_set_destroy(&o->value);
- }
- free(options);
-}
-
-static void
-validate_empty_lb_backends(struct action_context *ctx,
- const struct ovnact_gen_option *options,
- size_t n_options)
-{
- for (const struct ovnact_gen_option *o = options;
- o < &options[n_options]; o++) {
- const union expr_constant *c = o->value.values;
- struct sockaddr_storage ss;
- struct uuid uuid;
-
- if (o->value.n_values > 1 || !c->string) {
- lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
- o->option->name);
- return;
- }
-
- switch (o->option->code) {
- case EMPTY_LB_VIP:
- if (!inet_parse_active(c->string, 0, &ss, false)) {
- lexer_error(ctx->lexer, "Invalid load balancer VIP '%s'",
- c->string);
- return;
- }
- break;
- case EMPTY_LB_PROTOCOL:
- if (strcmp(c->string, "tcp") && strcmp(c->string, "udp")) {
- lexer_error(ctx->lexer,
- "Load balancer protocol '%s' is not 'tcp' or 'udp'",
- c->string);
- return;
- }
- break;
- case EMPTY_LB_LOAD_BALANCER:
- if (!uuid_from_string(&uuid, c->string)) {
- lexer_error(ctx->lexer, "Load balancer '%s' is not a UUID",
- c->string);
- return;
- }
- break;
- }
- }
-}
-
-static void
-parse_trigger_event(struct action_context *ctx,
- struct ovnact_controller_event *event)
-{
- int event_type = 0;
-
- lexer_force_match(ctx->lexer, LEX_T_LPAREN);
-
- /* Event type must be listed first */
- if (!lexer_match_id(ctx->lexer, "event")) {
- lexer_syntax_error(ctx->lexer, "Expecting 'event' option");
- return;
- }
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
-
- if (ctx->lexer->token.type != LEX_T_STRING ||
- strlen(ctx->lexer->token.s) >= 64) {
- lexer_syntax_error(ctx->lexer, "Expecting string");
- return;
- }
-
- event_type = string_to_event(ctx->lexer->token.s);
- if (event_type < 0 || event_type >= OVN_EVENT_MAX) {
- lexer_syntax_error(ctx->lexer, "Unknown event '%d'", event_type);
- return;
- }
-
- event->event_type = event_type;
- lexer_get(ctx->lexer);
-
- lexer_match(ctx->lexer, LEX_T_COMMA);
-
- size_t allocated_options = 0;
- while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- if (event->n_options >= allocated_options) {
- event->options = x2nrealloc(event->options, &allocated_options,
- sizeof *event->options);
- }
-
- struct ovnact_gen_option *o = &event->options[event->n_options++];
- memset(o, 0, sizeof *o);
- parse_gen_opt(ctx, o,
- &ctx->pp->controller_event_opts->event_opts[event_type],
- event_to_string(event_type));
- if (ctx->lexer->error) {
- return;
- }
-
- lexer_match(ctx->lexer, LEX_T_COMMA);
- }
-
- switch (event_type) {
- case OVN_EVENT_EMPTY_LB_BACKENDS:
- validate_empty_lb_backends(ctx, event->options, event->n_options);
- break;
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static void
-ovnact_controller_event_free(struct ovnact_controller_event *event OVS_UNUSED)
-{
-}
-
-static void
-parse_put_opts(struct action_context *ctx, const struct expr_field *dst,
- struct ovnact_put_opts *po, const struct hmap *gen_opts,
- const char *opts_type)
-{
- lexer_get(ctx->lexer); /* Skip put_dhcp[v6]_opts / put_nd_ra_opts. */
- lexer_get(ctx->lexer); /* Skip '('. */
-
- /* Validate that the destination is a 1-bit, modifiable field. */
- char *error = expr_type_check(dst, 1, true);
- if (error) {
- lexer_error(ctx->lexer, "%s", error);
- free(error);
- return;
- }
- po->dst = *dst;
-
- size_t allocated_options = 0;
- while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- if (po->n_options >= allocated_options) {
- po->options = x2nrealloc(po->options, &allocated_options,
- sizeof *po->options);
- }
-
- struct ovnact_gen_option *o = &po->options[po->n_options++];
- memset(o, 0, sizeof *o);
- parse_gen_opt(ctx, o, gen_opts, opts_type);
- if (ctx->lexer->error) {
- return;
- }
-
- lexer_match(ctx->lexer, LEX_T_COMMA);
- }
-}
-
-/* Parses the "put_dhcp_opts" and "put_dhcpv6_opts" actions.
- *
- * The caller has already consumed "<dst> =", so this just parses the rest. */
-static void
-parse_put_dhcp_opts(struct action_context *ctx,
- const struct expr_field *dst,
- struct ovnact_put_opts *po)
-{
- const struct hmap *dhcp_opts =
- (po->ovnact.type == OVNACT_PUT_DHCPV6_OPTS) ?
- ctx->pp->dhcpv6_opts : ctx->pp->dhcp_opts;
- const char *opts_type =
- (po->ovnact.type == OVNACT_PUT_DHCPV6_OPTS) ? "DHCPv6" : "DHCPv4";
-
- parse_put_opts(ctx, dst, po, dhcp_opts, opts_type);
-
- if (!ctx->lexer->error && po->ovnact.type == OVNACT_PUT_DHCPV4_OPTS
- && !find_offerip(po->options, po->n_options)) {
- lexer_error(ctx->lexer,
- "put_dhcp_opts requires offerip to be specified.");
- return;
- }
-}
-
-static void
-format_put_opts(const char *name, const struct ovnact_put_opts *pdo,
- struct ds *s)
-{
- expr_field_format(&pdo->dst, s);
- ds_put_format(s, " = %s(", name);
- for (const struct ovnact_gen_option *o = pdo->options;
- o < &pdo->options[pdo->n_options]; o++) {
- if (o != pdo->options) {
- ds_put_cstr(s, ", ");
- }
- ds_put_format(s, "%s = ", o->option->name);
- expr_constant_set_format(&o->value, s);
- }
- ds_put_cstr(s, ");");
-}
-
-static void
-format_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo, struct ds *s)
-{
- format_put_opts("put_dhcp_opts", pdo, s);
-}
-
-static void
-format_PUT_DHCPV6_OPTS(const struct ovnact_put_opts *pdo, struct ds *s)
-{
- format_put_opts("put_dhcpv6_opts", pdo, s);
-}
-
-static void
-encode_put_dhcpv4_option(const struct ovnact_gen_option *o,
- struct ofpbuf *ofpacts)
-{
- uint8_t *opt_header = ofpbuf_put_zeros(ofpacts, 2);
- opt_header[0] = o->option->code;
-
- const union expr_constant *c = o->value.values;
- size_t n_values = o->value.n_values;
- if (!strcmp(o->option->type, "bool") ||
- !strcmp(o->option->type, "uint8")) {
- opt_header[1] = 1;
- ofpbuf_put(ofpacts, &c->value.u8_val, 1);
- } else if (!strcmp(o->option->type, "uint16")) {
- opt_header[1] = 2;
- ofpbuf_put(ofpacts, &c->value.be16_int, 2);
- } else if (!strcmp(o->option->type, "uint32")) {
- opt_header[1] = 4;
- ofpbuf_put(ofpacts, &c->value.be32_int, 4);
- } else if (!strcmp(o->option->type, "ipv4")) {
- opt_header[1] = n_values * sizeof(ovs_be32);
- for (size_t i = 0; i < n_values; i++) {
- ofpbuf_put(ofpacts, &c[i].value.ipv4, sizeof(ovs_be32));
- }
- } else if (!strcmp(o->option->type, "static_routes")) {
- size_t no_of_routes = n_values;
- if (no_of_routes % 2) {
- no_of_routes -= 1;
- }
- opt_header[1] = 0;
-
- /* Calculating the length of this option first because when
- * we call ofpbuf_put, it might reallocate the buffer if the
- * tail room is short making "opt_header" pointer invalid.
- * So running the for loop twice.
- */
- for (size_t i = 0; i < no_of_routes; i += 2) {
- uint8_t plen = 32;
- if (c[i].masked) {
- plen = (uint8_t) ip_count_cidr_bits(c[i].mask.ipv4);
- }
- opt_header[1] += (1 + DIV_ROUND_UP(plen, 8) + sizeof(ovs_be32));
- }
-
- /* Copied from RFC 3442. Please refer to this RFC for the format of
- * the classless static route option.
- *
- * The following table contains some examples of how various subnet
- * number/mask combinations can be encoded:
- *
- * Subnet number Subnet mask Destination descriptor
- * 0 0 0
- * 10.0.0.0 255.0.0.0 8.10
- * 10.0.0.0 255.255.255.0 24.10.0.0
- * 10.17.0.0 255.255.0.0 16.10.17
- * 10.27.129.0 255.255.255.0 24.10.27.129
- * 10.229.0.128 255.255.255.128 25.10.229.0.128
- * 10.198.122.47 255.255.255.255 32.10.198.122.47
- */
-
- for (size_t i = 0; i < no_of_routes; i += 2) {
- uint8_t plen = 32;
- if (c[i].masked) {
- plen = ip_count_cidr_bits(c[i].mask.ipv4);
- }
- ofpbuf_put(ofpacts, &plen, 1);
- ofpbuf_put(ofpacts, &c[i].value.ipv4, DIV_ROUND_UP(plen, 8));
- ofpbuf_put(ofpacts, &c[i + 1].value.ipv4,
- sizeof(ovs_be32));
- }
- } else if (!strcmp(o->option->type, "str")) {
- opt_header[1] = strlen(c->string);
- ofpbuf_put(ofpacts, c->string, opt_header[1]);
- }
-}
-
-static void
-encode_put_dhcpv6_option(const struct ovnact_gen_option *o,
- struct ofpbuf *ofpacts)
-{
- struct dhcp_opt6_header *opt = ofpbuf_put_uninit(ofpacts, sizeof *opt);
- const union expr_constant *c = o->value.values;
- size_t n_values = o->value.n_values;
- size_t size;
-
- opt->opt_code = htons(o->option->code);
-
- if (!strcmp(o->option->type, "ipv6")) {
- size = n_values * sizeof(struct in6_addr);
- opt->size = htons(size);
- for (size_t i = 0; i < n_values; i++) {
- ofpbuf_put(ofpacts, &c[i].value.ipv6, sizeof(struct in6_addr));
- }
- } else if (!strcmp(o->option->type, "mac")) {
- size = sizeof(struct eth_addr);
- opt->size = htons(size);
- ofpbuf_put(ofpacts, &c->value.mac, size);
- } else if (!strcmp(o->option->type, "str")) {
- size = strlen(c->string);
- opt->size = htons(size);
- ofpbuf_put(ofpacts, c->string, size);
- }
-}
-
-static void
-encode_PUT_DHCPV4_OPTS(const struct ovnact_put_opts *pdo,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct mf_subfield dst = expr_resolve_field(&pdo->dst);
-
- size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_PUT_DHCP_OPTS,
- true, NX_CTLR_NO_METER,
- ofpacts);
- nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
- ovs_be32 ofs = htonl(dst.ofs);
- ofpbuf_put(ofpacts, &ofs, sizeof ofs);
-
- /* Encode the offerip option first, because it's a special case and needs
- * to be first in the actual DHCP response, and then encode the rest
- * (skipping offerip the second time around). */
- const struct ovnact_gen_option *offerip_opt = find_offerip(
- pdo->options, pdo->n_options);
- ovs_be32 offerip = offerip_opt->value.values[0].value.ipv4;
- ofpbuf_put(ofpacts, &offerip, sizeof offerip);
-
- for (const struct ovnact_gen_option *o = pdo->options;
- o < &pdo->options[pdo->n_options]; o++) {
- if (o != offerip_opt) {
- encode_put_dhcpv4_option(o, ofpacts);
- }
- }
-
- encode_finish_controller_op(oc_offset, ofpacts);
-}
-
-static void
-encode_PUT_DHCPV6_OPTS(const struct ovnact_put_opts *pdo,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct mf_subfield dst = expr_resolve_field(&pdo->dst);
-
- size_t oc_offset = encode_start_controller_op(
- ACTION_OPCODE_PUT_DHCPV6_OPTS, true, NX_CTLR_NO_METER, ofpacts);
- nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
- ovs_be32 ofs = htonl(dst.ofs);
- ofpbuf_put(ofpacts, &ofs, sizeof ofs);
-
- for (const struct ovnact_gen_option *o = pdo->options;
- o < &pdo->options[pdo->n_options]; o++) {
- encode_put_dhcpv6_option(o, ofpacts);
- }
-
- encode_finish_controller_op(oc_offset, ofpacts);
-}
-
-static void
-ovnact_put_opts_free(struct ovnact_put_opts *pdo)
-{
- free_gen_options(pdo->options, pdo->n_options);
-}
-
-static void
-parse_SET_QUEUE(struct action_context *ctx)
-{
- int queue_id;
-
- if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN)
- || !lexer_get_int(ctx->lexer, &queue_id)
- || !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
- return;
- }
-
- if (queue_id < QDISC_MIN_QUEUE_ID || queue_id > QDISC_MAX_QUEUE_ID) {
- lexer_error(ctx->lexer, "Queue ID %d for set_queue is "
- "not in valid range %d to %d.",
- queue_id, QDISC_MIN_QUEUE_ID, QDISC_MAX_QUEUE_ID);
- return;
- }
-
- ovnact_put_SET_QUEUE(ctx->ovnacts)->queue_id = queue_id;
-}
-
-static void
-format_SET_QUEUE(const struct ovnact_set_queue *set_queue, struct ds *s)
-{
- ds_put_format(s, "set_queue(%d);", set_queue->queue_id);
-}
-
-static void
-encode_SET_QUEUE(const struct ovnact_set_queue *set_queue,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- ofpact_put_SET_QUEUE(ofpacts)->queue_id = set_queue->queue_id;
-}
-
-static void
-ovnact_set_queue_free(struct ovnact_set_queue *a OVS_UNUSED)
-{
-}
-
-static void
-parse_dns_lookup(struct action_context *ctx, const struct expr_field *dst,
- struct ovnact_dns_lookup *dl)
-{
- lexer_get(ctx->lexer); /* Skip dns_lookup. */
- lexer_get(ctx->lexer); /* Skip '('. */
- if (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- lexer_error(ctx->lexer, "dns_lookup doesn't take any parameters");
- return;
- }
- /* Validate that the destination is a 1-bit, modifiable field. */
- char *error = expr_type_check(dst, 1, true);
- if (error) {
- lexer_error(ctx->lexer, "%s", error);
- free(error);
- return;
- }
- dl->dst = *dst;
- add_prerequisite(ctx, "udp");
-}
-
-static void
-format_DNS_LOOKUP(const struct ovnact_dns_lookup *dl, struct ds *s)
-{
- expr_field_format(&dl->dst, s);
- ds_put_cstr(s, " = dns_lookup();");
-}
-
-static void
-encode_DNS_LOOKUP(const struct ovnact_dns_lookup *dl,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct mf_subfield dst = expr_resolve_field(&dl->dst);
-
- size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_DNS_LOOKUP,
- true, NX_CTLR_NO_METER,
- ofpacts);
- nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
- ovs_be32 ofs = htonl(dst.ofs);
- ofpbuf_put(ofpacts, &ofs, sizeof ofs);
- encode_finish_controller_op(oc_offset, ofpacts);
-}
-
-
-static void
-ovnact_dns_lookup_free(struct ovnact_dns_lookup *dl OVS_UNUSED)
-{
-}
-
-/* Parses the "put_nd_ra_opts" action.
- * The caller has already consumed "<dst> =", so this just parses the rest. */
-static void
-parse_put_nd_ra_opts(struct action_context *ctx, const struct expr_field *dst,
- struct ovnact_put_opts *po)
-{
- parse_put_opts(ctx, dst, po, ctx->pp->nd_ra_opts, "IPv6 ND RA");
-
- if (ctx->lexer->error) {
- return;
- }
-
- bool addr_mode_stateful = false;
- bool prefix_set = false;
- bool slla_present = false;
- /* Let's validate the options. */
- for (struct ovnact_gen_option *o = po->options;
- o < &po->options[po->n_options]; o++) {
- const union expr_constant *c = o->value.values;
- if (o->value.n_values > 1) {
- lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
- o->option->name);
- return;
- }
-
- bool ok = true;
- switch (o->option->code) {
- case ND_RA_FLAG_ADDR_MODE:
- ok = (c->string && (!strcmp(c->string, "slaac") ||
- !strcmp(c->string, "dhcpv6_stateful") ||
- !strcmp(c->string, "dhcpv6_stateless")));
- if (ok && !strcmp(c->string, "dhcpv6_stateful")) {
- addr_mode_stateful = true;
- }
- break;
-
- case ND_OPT_SOURCE_LINKADDR:
- ok = c->format == LEX_F_ETHERNET;
- slla_present = true;
- break;
-
- case ND_OPT_PREFIX_INFORMATION:
- ok = c->format == LEX_F_IPV6 && c->masked;
- prefix_set = true;
- break;
-
- case ND_OPT_MTU:
- ok = c->format == LEX_F_DECIMAL;
- break;
- }
-
- if (!ok) {
- lexer_error(ctx->lexer, "Invalid value for \"%s\" option",
- o->option->name);
- return;
- }
- }
-
- if (!slla_present) {
- lexer_error(ctx->lexer, "slla option not present");
- return;
- }
-
- if (!addr_mode_stateful && !prefix_set) {
- lexer_error(ctx->lexer, "prefix option needs "
- "to be set when address mode is slaac/dhcpv6_stateless.");
- return;
- }
-
- add_prerequisite(ctx, "ip6");
-}
-
-static void
-format_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po,
- struct ds *s)
-{
- format_put_opts("put_nd_ra_opts", po, s);
-}
-
-static void
-encode_put_nd_ra_option(const struct ovnact_gen_option *o,
- struct ofpbuf *ofpacts, ptrdiff_t ra_offset)
-{
- const union expr_constant *c = o->value.values;
-
- switch (o->option->code) {
- case ND_RA_FLAG_ADDR_MODE:
- {
- struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra);
- if (!strcmp(c->string, "dhcpv6_stateful")) {
- ra->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
- } else if (!strcmp(c->string, "dhcpv6_stateless")) {
- ra->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
- }
- break;
- }
-
- case ND_OPT_SOURCE_LINKADDR:
- {
- struct ovs_nd_lla_opt *lla_opt =
- ofpbuf_put_uninit(ofpacts, sizeof *lla_opt);
- lla_opt->type = ND_OPT_SOURCE_LINKADDR;
- lla_opt->len = 1;
- lla_opt->mac = c->value.mac;
- break;
- }
-
- case ND_OPT_MTU:
- {
- struct ovs_nd_mtu_opt *mtu_opt =
- ofpbuf_put_uninit(ofpacts, sizeof *mtu_opt);
- mtu_opt->type = ND_OPT_MTU;
- mtu_opt->len = 1;
- mtu_opt->reserved = 0;
- put_16aligned_be32(&mtu_opt->mtu, c->value.be32_int);
- break;
- }
-
- case ND_OPT_PREFIX_INFORMATION:
- {
- struct ovs_nd_prefix_opt *prefix_opt =
- ofpbuf_put_uninit(ofpacts, sizeof *prefix_opt);
- uint8_t prefix_len = ipv6_count_cidr_bits(&c->mask.ipv6);
- struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra);
- prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
- prefix_opt->len = 4;
- prefix_opt->prefix_len = prefix_len;
- prefix_opt->la_flags = IPV6_ND_RA_OPT_PREFIX_ON_LINK;
- if (!(ra->mo_flags & IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG)) {
- prefix_opt->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS;
- }
- put_16aligned_be32(&prefix_opt->valid_lifetime,
- htonl(IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME));
- put_16aligned_be32(&prefix_opt->preferred_lifetime,
- htonl(IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME));
- put_16aligned_be32(&prefix_opt->reserved, 0);
- memcpy(prefix_opt->prefix.be32, &c->value.be128[7].be32,
- sizeof(ovs_be32[4]));
- break;
- }
- }
-}
-
-static void
-encode_PUT_ND_RA_OPTS(const struct ovnact_put_opts *po,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct mf_subfield dst = expr_resolve_field(&po->dst);
-
- size_t oc_offset = encode_start_controller_op(
- ACTION_OPCODE_PUT_ND_RA_OPTS, true, NX_CTLR_NO_METER, ofpacts);
- nx_put_header(ofpacts, dst.field->id, OFP13_VERSION, false);
- ovs_be32 ofs = htonl(dst.ofs);
- ofpbuf_put(ofpacts, &ofs, sizeof ofs);
-
- /* Frame the complete ICMPv6 Router Advertisement data encoding
- * the ND RA options in it, in the userdata field, so that when
- * pinctrl module receives the ICMPv6 Router Solicitation packet
- * it can copy the userdata field AS IS and resume the packet.
- */
- size_t ra_offset = ofpacts->size;
- struct ovs_ra_msg *ra = ofpbuf_put_zeros(ofpacts, sizeof *ra);
- ra->icmph.icmp6_type = ND_ROUTER_ADVERT;
- ra->cur_hop_limit = IPV6_ND_RA_CUR_HOP_LIMIT;
- ra->mo_flags = 0;
- ra->router_lifetime = htons(IPV6_ND_RA_LIFETIME);
-
- for (const struct ovnact_gen_option *o = po->options;
- o < &po->options[po->n_options]; o++) {
- encode_put_nd_ra_option(o, ofpacts, ra_offset);
- }
-
- encode_finish_controller_op(oc_offset, ofpacts);
-}
-
-
-static void
-parse_log_arg(struct action_context *ctx, struct ovnact_log *log)
-{
- if (lexer_match_id(ctx->lexer, "verdict")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- if (lexer_match_id(ctx->lexer, "drop")) {
- log->verdict = LOG_VERDICT_DROP;
- } else if (lexer_match_id(ctx->lexer, "reject")) {
- log->verdict = LOG_VERDICT_REJECT;
- } else if (lexer_match_id(ctx->lexer, "allow")) {
- log->verdict = LOG_VERDICT_ALLOW;
- } else {
- lexer_syntax_error(ctx->lexer, "unknown verdict");
- return;
- }
- } else if (lexer_match_id(ctx->lexer, "name")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- /* If multiple names are given, use the most recent. */
- if (ctx->lexer->token.type == LEX_T_STRING) {
- /* Arbitrarily limit the name length to 64 bytes, since
- * these will be encoded in datapath actions. */
- if (strlen(ctx->lexer->token.s) >= 64) {
- lexer_syntax_error(ctx->lexer, "name must be shorter "
- "than 64 characters");
- return;
- }
- free(log->name);
- log->name = xstrdup(ctx->lexer->token.s);
- } else {
- lexer_syntax_error(ctx->lexer, "expecting string");
- return;
- }
- lexer_get(ctx->lexer);
- } else if (lexer_match_id(ctx->lexer, "severity")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- if (ctx->lexer->token.type == LEX_T_ID) {
- uint8_t severity = log_severity_from_string(ctx->lexer->token.s);
- if (severity != UINT8_MAX) {
- log->severity = severity;
- lexer_get(ctx->lexer);
- return;
- } else {
- lexer_syntax_error(ctx->lexer, "unknown severity");
- return;
- }
- }
- lexer_syntax_error(ctx->lexer, "expecting severity");
- } else if (lexer_match_id(ctx->lexer, "meter")) {
- if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
- return;
- }
- /* If multiple meters are given, use the most recent. */
- if (ctx->lexer->token.type == LEX_T_STRING) {
- free(log->meter);
- log->meter = xstrdup(ctx->lexer->token.s);
- } else {
- lexer_syntax_error(ctx->lexer, "expecting string");
- return;
- }
- lexer_get(ctx->lexer);
- } else {
- lexer_syntax_error(ctx->lexer, NULL);
- }
-}
-
-static void
-parse_LOG(struct action_context *ctx)
-{
- struct ovnact_log *log = ovnact_put_LOG(ctx->ovnacts);
-
- /* Provide default values. */
- log->severity = LOG_SEVERITY_INFO;
- log->verdict = LOG_VERDICT_UNKNOWN;
-
- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
- while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
- parse_log_arg(ctx, log);
- if (ctx->lexer->error) {
- return;
- }
- lexer_match(ctx->lexer, LEX_T_COMMA);
- }
- }
- if (log->verdict == LOG_VERDICT_UNKNOWN) {
- lexer_syntax_error(ctx->lexer, "expecting verdict");
- }
-}
-
-static void
-format_LOG(const struct ovnact_log *log, struct ds *s)
-{
- ds_put_cstr(s, "log(");
-
- if (log->name) {
- ds_put_format(s, "name=\"%s\", ", log->name);
- }
-
- ds_put_format(s, "verdict=%s, ", log_verdict_to_string(log->verdict));
- ds_put_format(s, "severity=%s", log_severity_to_string(log->severity));
-
- if (log->meter) {
- ds_put_format(s, ", meter=\"%s\"", log->meter);
- }
-
- ds_put_cstr(s, ");");
-}
-
-static void
-encode_LOG(const struct ovnact_log *log,
- const struct ovnact_encode_params *ep, struct ofpbuf *ofpacts)
-{
- uint32_t meter_id = NX_CTLR_NO_METER;
-
- if (log->meter) {
- meter_id = ovn_extend_table_assign_id(ep->meter_table, log->meter,
- ep->lflow_uuid);
- if (meter_id == EXT_TABLE_ID_INVALID) {
- VLOG_WARN("Unable to assign id for log meter: %s", log->meter);
- return;
- }
- }
-
- size_t oc_offset = encode_start_controller_op(ACTION_OPCODE_LOG, false,
- meter_id, ofpacts);
-
- struct log_pin_header *lph = ofpbuf_put_uninit(ofpacts, sizeof *lph);
- lph->verdict = log->verdict;
- lph->severity = log->severity;
-
- if (log->name) {
- int name_len = strlen(log->name);
- ofpbuf_put(ofpacts, log->name, name_len);
- }
-
- encode_finish_controller_op(oc_offset, ofpacts);
-}
-
-static void
-ovnact_log_free(struct ovnact_log *log)
-{
- free(log->name);
- free(log->meter);
-}
-
-static void
-parse_set_meter_action(struct action_context *ctx)
-{
- uint64_t rate = 0;
- uint64_t burst = 0;
-
- lexer_force_match(ctx->lexer, LEX_T_LPAREN); /* Skip '('. */
- if (ctx->lexer->token.type == LEX_T_INTEGER
- && ctx->lexer->token.format == LEX_F_DECIMAL) {
- rate = ntohll(ctx->lexer->token.value.integer);
- }
- lexer_get(ctx->lexer);
- if (lexer_match(ctx->lexer, LEX_T_COMMA)) { /* Skip ','. */
- if (ctx->lexer->token.type == LEX_T_INTEGER
- && ctx->lexer->token.format == LEX_F_DECIMAL) {
- burst = ntohll(ctx->lexer->token.value.integer);
- }
- lexer_get(ctx->lexer);
- }
- lexer_force_match(ctx->lexer, LEX_T_RPAREN); /* Skip ')'. */
-
- if (!rate) {
- lexer_error(ctx->lexer,
- "Rate %"PRId64" for set_meter is not in valid.",
- rate);
- return;
- }
-
- struct ovnact_set_meter *cl = ovnact_put_SET_METER(ctx->ovnacts);
- cl->rate = rate;
- cl->burst = burst;
-}
-
-static void
-format_SET_METER(const struct ovnact_set_meter *cl, struct ds *s)
-{
- if (cl->burst) {
- ds_put_format(s, "set_meter(%"PRId64", %"PRId64");",
- cl->rate, cl->burst);
- } else {
- ds_put_format(s, "set_meter(%"PRId64");", cl->rate);
- }
-}
-
-static void
-encode_SET_METER(const struct ovnact_set_meter *cl,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- uint32_t table_id;
- struct ofpact_meter *om;
-
- /* Use the special "__string:" prefix to indicate that the name
- * describes the meter itself. */
- char *name;
- if (cl->burst) {
- name = xasprintf("__string: kbps burst stats bands=type=drop "
- "rate=%"PRId64" burst_size=%"PRId64"", cl->rate,
- cl->burst);
- } else {
- name = xasprintf("__string: kbps stats bands=type=drop "
- "rate=%"PRId64"", cl->rate);
- }
-
- table_id = ovn_extend_table_assign_id(ep->meter_table, name,
- ep->lflow_uuid);
- free(name);
- if (table_id == EXT_TABLE_ID_INVALID) {
- return;
- }
-
- /* Create an action to set the meter. */
- om = ofpact_put_METER(ofpacts);
- om->meter_id = table_id;
-}
-
-static void
-ovnact_set_meter_free(struct ovnact_set_meter *ct OVS_UNUSED)
-{
-}
-
-static void
-format_OVNFIELD_LOAD(const struct ovnact_load *load , struct ds *s)
-{
- const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
- switch (f->id) {
- case OVN_ICMP4_FRAG_MTU:
- ds_put_format(s, "%s = %u;", f->name,
- ntohs(load->imm.value.be16_int));
- break;
-
- case OVN_FIELD_N_IDS:
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static void
-encode_OVNFIELD_LOAD(const struct ovnact_load *load,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
- switch (f->id) {
- case OVN_ICMP4_FRAG_MTU: {
- size_t oc_offset = encode_start_controller_op(
- ACTION_OPCODE_PUT_ICMP4_FRAG_MTU, true, NX_CTLR_NO_METER,
- ofpacts);
- ofpbuf_put(ofpacts, &load->imm.value.be16_int, sizeof(ovs_be16));
- encode_finish_controller_op(oc_offset, ofpacts);
- break;
- }
- case OVN_FIELD_N_IDS:
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static void
-parse_check_pkt_larger(struct action_context *ctx,
- const struct expr_field *dst,
- struct ovnact_check_pkt_larger *cipl)
-{
- /* Validate that the destination is a 1-bit, modifiable field. */
- char *error = expr_type_check(dst, 1, true);
- if (error) {
- lexer_error(ctx->lexer, "%s", error);
- free(error);
- return;
- }
-
- int pkt_len;
- lexer_get(ctx->lexer); /* Skip check_pkt_len. */
- if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN)
- || !lexer_get_int(ctx->lexer, &pkt_len)
- || !lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
- return;
- }
-
- cipl->dst = *dst;
- cipl->pkt_len = pkt_len;
-}
-
-static void
-format_CHECK_PKT_LARGER(const struct ovnact_check_pkt_larger *cipl,
- struct ds *s)
-{
- expr_field_format(&cipl->dst, s);
- ds_put_format(s, " = check_pkt_larger(%d);", cipl->pkt_len);
-}
-
-static void
-encode_CHECK_PKT_LARGER(const struct ovnact_check_pkt_larger *cipl,
- const struct ovnact_encode_params *ep OVS_UNUSED,
- struct ofpbuf *ofpacts)
-{
- struct ofpact_check_pkt_larger *check_pkt_larger =
- ofpact_put_CHECK_PKT_LARGER(ofpacts);
- check_pkt_larger->pkt_len = cipl->pkt_len;
- check_pkt_larger->dst = expr_resolve_field(&cipl->dst);
-}
-
-static void
-ovnact_check_pkt_larger_free(struct ovnact_check_pkt_larger *cipl OVS_UNUSED)
-{
-}
-
-/* Parses an assignment or exchange or put_dhcp_opts action. */
-static void
-parse_set_action(struct action_context *ctx)
-{
- struct expr_field lhs;
- if (!expr_field_parse(ctx->lexer, ctx->pp->symtab, &lhs, &ctx->prereqs)) {
- return;
- }
-
- if (lexer_match(ctx->lexer, LEX_T_EXCHANGE)) {
- parse_assignment_action(ctx, true, &lhs);
- } else if (lexer_match(ctx->lexer, LEX_T_EQUALS)) {
- if (ctx->lexer->token.type != LEX_T_ID) {
- parse_LOAD(ctx, &lhs);
- } else if (!strcmp(ctx->lexer->token.s, "put_dhcp_opts")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV4_OPTS(
- ctx->ovnacts));
- } else if (!strcmp(ctx->lexer->token.s, "put_dhcpv6_opts")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_put_dhcp_opts(ctx, &lhs, ovnact_put_PUT_DHCPV6_OPTS(
- ctx->ovnacts));
- } else if (!strcmp(ctx->lexer->token.s, "dns_lookup")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_dns_lookup(ctx, &lhs, ovnact_put_DNS_LOOKUP(ctx->ovnacts));
- } else if (!strcmp(ctx->lexer->token.s, "put_nd_ra_opts")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_put_nd_ra_opts(ctx, &lhs,
- ovnact_put_PUT_ND_RA_OPTS(ctx->ovnacts));
- } else if (!strcmp(ctx->lexer->token.s, "check_pkt_larger")
- && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- parse_check_pkt_larger(ctx, &lhs,
- ovnact_put_CHECK_PKT_LARGER(ctx->ovnacts));
- } else {
- parse_assignment_action(ctx, false, &lhs);
- }
- } else {
- lexer_syntax_error(ctx->lexer, "expecting `=' or `<->'");
- }
-}
-
-static bool
-parse_action(struct action_context *ctx)
-{
- if (ctx->lexer->token.type != LEX_T_ID) {
- lexer_syntax_error(ctx->lexer, NULL);
- return false;
- }
-
- enum lex_type lookahead = lexer_lookahead(ctx->lexer);
- if (lookahead == LEX_T_EQUALS || lookahead == LEX_T_EXCHANGE
- || lookahead == LEX_T_LSQUARE) {
- parse_set_action(ctx);
- } else if (lexer_match_id(ctx->lexer, "next")) {
- parse_NEXT(ctx);
- } else if (lexer_match_id(ctx->lexer, "output")) {
- ovnact_put_OUTPUT(ctx->ovnacts);
- } else if (lexer_match_id(ctx->lexer, "ip.ttl")) {
- parse_DEC_TTL(ctx);
- } else if (lexer_match_id(ctx->lexer, "ct_next")) {
- parse_CT_NEXT(ctx);
- } else if (lexer_match_id(ctx->lexer, "ct_commit")) {
- parse_CT_COMMIT(ctx);
- } else if (lexer_match_id(ctx->lexer, "ct_dnat")) {
- parse_CT_DNAT(ctx);
- } else if (lexer_match_id(ctx->lexer, "ct_snat")) {
- parse_CT_SNAT(ctx);
- } else if (lexer_match_id(ctx->lexer, "ct_lb")) {
- parse_ct_lb_action(ctx);
- } else if (lexer_match_id(ctx->lexer, "ct_clear")) {
- ovnact_put_CT_CLEAR(ctx->ovnacts);
- } else if (lexer_match_id(ctx->lexer, "clone")) {
- parse_CLONE(ctx);
- } else if (lexer_match_id(ctx->lexer, "arp")) {
- parse_ARP(ctx);
- } else if (lexer_match_id(ctx->lexer, "icmp4")) {
- parse_ICMP4(ctx);
- } else if (lexer_match_id(ctx->lexer, "icmp4_error")) {
- parse_ICMP4_ERROR(ctx);
- } else if (lexer_match_id(ctx->lexer, "icmp6")) {
- parse_ICMP6(ctx);
- } else if (lexer_match_id(ctx->lexer, "igmp")) {
- ovnact_put_IGMP(ctx->ovnacts);
- } else if (lexer_match_id(ctx->lexer, "tcp_reset")) {
- parse_TCP_RESET(ctx);
- } else if (lexer_match_id(ctx->lexer, "nd_na")) {
- parse_ND_NA(ctx);
- } else if (lexer_match_id(ctx->lexer, "nd_na_router")) {
- parse_ND_NA_ROUTER(ctx);
- } else if (lexer_match_id(ctx->lexer, "nd_ns")) {
- parse_ND_NS(ctx);
- } else if (lexer_match_id(ctx->lexer, "get_arp")) {
- parse_get_mac_bind(ctx, 32, ovnact_put_GET_ARP(ctx->ovnacts));
- } else if (lexer_match_id(ctx->lexer, "put_arp")) {
- parse_put_mac_bind(ctx, 32, ovnact_put_PUT_ARP(ctx->ovnacts));
- } else if (lexer_match_id(ctx->lexer, "get_nd")) {
- parse_get_mac_bind(ctx, 128, ovnact_put_GET_ND(ctx->ovnacts));
- } else if (lexer_match_id(ctx->lexer, "put_nd")) {
- parse_put_mac_bind(ctx, 128, ovnact_put_PUT_ND(ctx->ovnacts));
- } else if (lexer_match_id(ctx->lexer, "set_queue")) {
- parse_SET_QUEUE(ctx);
- } else if (lexer_match_id(ctx->lexer, "log")) {
- parse_LOG(ctx);
- } else if (lexer_match_id(ctx->lexer, "set_meter")) {
- parse_set_meter_action(ctx);
- } else if (lexer_match_id(ctx->lexer, "trigger_event")) {
- parse_trigger_event(ctx, ovnact_put_TRIGGER_EVENT(ctx->ovnacts));
- } else {
- lexer_syntax_error(ctx->lexer, "expecting action");
- }
- lexer_force_match(ctx->lexer, LEX_T_SEMICOLON);
- return !ctx->lexer->error;
-}
-
-static void
-parse_actions(struct action_context *ctx, enum lex_type sentinel)
-{
- /* "drop;" by itself is a valid (empty) set of actions, but it can't be
- * combined with other actions because that doesn't make sense. */
- if (ctx->lexer->token.type == LEX_T_ID
- && !strcmp(ctx->lexer->token.s, "drop")
- && lexer_lookahead(ctx->lexer) == LEX_T_SEMICOLON) {
- lexer_get(ctx->lexer); /* Skip "drop". */
- lexer_get(ctx->lexer); /* Skip ";". */
- lexer_force_match(ctx->lexer, sentinel);
- return;
- }
-
- while (!lexer_match(ctx->lexer, sentinel)) {
- if (!parse_action(ctx)) {
- return;
- }
- }
-}
-
-/* Parses OVN actions, in the format described for the "actions" column in the
- * Logical_Flow table in ovn-sb(5), and appends the parsed versions of the
- * actions to 'ovnacts' as "struct ovnact"s. The caller must eventually free
- * the parsed ovnacts with ovnacts_free().
- *
- * 'pp' provides most of the parameters for translation.
- *
- * Some actions add extra requirements (prerequisites) to the flow's match. If
- * so, this function sets '*prereqsp' to the actions' prerequisites; otherwise,
- * it sets '*prereqsp' to NULL. The caller owns '*prereqsp' and must
- * eventually free it.
- *
- * Returns true if successful, false if an error occurred. Upon return,
- * returns true if and only if lexer->error is NULL.
- */
-bool
-ovnacts_parse(struct lexer *lexer, const struct ovnact_parse_params *pp,
- struct ofpbuf *ovnacts, struct expr **prereqsp)
-{
- size_t ovnacts_start = ovnacts->size;
-
- struct action_context ctx = {
- .pp = pp,
- .lexer = lexer,
- .ovnacts = ovnacts,
- .prereqs = NULL,
- };
- if (!lexer->error) {
- parse_actions(&ctx, LEX_T_END);
- }
-
- if (!lexer->error) {
- *prereqsp = ctx.prereqs;
- return true;
- } else {
- ofpbuf_pull(ovnacts, ovnacts_start);
- ovnacts_free(ovnacts->data, ovnacts->size);
- ofpbuf_push_uninit(ovnacts, ovnacts_start);
-
- ovnacts->size = ovnacts_start;
- expr_destroy(ctx.prereqs);
- *prereqsp = NULL;
- return false;
- }
-}
-
-/* Like ovnacts_parse(), but the actions are taken from 's'. */
-char * OVS_WARN_UNUSED_RESULT
-ovnacts_parse_string(const char *s, const struct ovnact_parse_params *pp,
- struct ofpbuf *ofpacts, struct expr **prereqsp)
-{
- struct lexer lexer;
-
- lexer_init(&lexer, s);
- lexer_get(&lexer);
- ovnacts_parse(&lexer, pp, ofpacts, prereqsp);
- char *error = lexer_steal_error(&lexer);
- lexer_destroy(&lexer);
-
- return error;
-}
-
-/* Formatting ovnacts. */
-
-static void
-ovnact_format(const struct ovnact *a, struct ds *s)
-{
- switch (a->type) {
-#define OVNACT(ENUM, STRUCT) \
- case OVNACT_##ENUM: \
- format_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), s); \
- break;
- OVNACTS
-#undef OVNACT
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Appends a string representing the 'ovnacts_len' bytes of ovnacts in
- * 'ovnacts' to 'string'. */
-void
-ovnacts_format(const struct ovnact *ovnacts, size_t ovnacts_len,
- struct ds *string)
-{
- if (!ovnacts_len) {
- ds_put_cstr(string, "drop;");
- } else {
- const struct ovnact *a;
-
- OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
- if (a != ovnacts) {
- ds_put_char(string, ' ');
- }
- ovnact_format(a, string);
- }
- }
-}
-
-/* Encoding ovnacts to OpenFlow. */
-
-static void
-ovnact_encode(const struct ovnact *a, const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- switch (a->type) {
-#define OVNACT(ENUM, STRUCT) \
- case OVNACT_##ENUM: \
- encode_##ENUM(ALIGNED_CAST(const struct STRUCT *, a), \
- ep, ofpacts); \
- break;
- OVNACTS
-#undef OVNACT
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Appends ofpacts to 'ofpacts' that represent the actions in the 'ovnacts_len'
- * bytes of actions starting at 'ovnacts'. */
-void
-ovnacts_encode(const struct ovnact *ovnacts, size_t ovnacts_len,
- const struct ovnact_encode_params *ep,
- struct ofpbuf *ofpacts)
-{
- if (ovnacts) {
- const struct ovnact *a;
-
- OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
- ovnact_encode(a, ep, ofpacts);
- }
- }
-}
-
-/* Freeing ovnacts. */
-
-static void
-ovnact_free(struct ovnact *a)
-{
- switch (a->type) {
-#define OVNACT(ENUM, STRUCT) \
- case OVNACT_##ENUM: \
- STRUCT##_free(ALIGNED_CAST(struct STRUCT *, a)); \
- break;
- OVNACTS
-#undef OVNACT
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Frees each of the actions in the 'ovnacts_len' bytes of actions starting at
- * 'ovnacts'.
- *
- * Does not call free(ovnacts); the caller must do so if desirable. */
-void
-ovnacts_free(struct ovnact *ovnacts, size_t ovnacts_len)
-{
- if (ovnacts) {
- struct ovnact *a;
-
- OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
- ovnact_free(a);
- }
- }
-}
diff --git a/ovn/lib/automake.mk b/ovn/lib/automake.mk
index 7cac67fb6..c74430005 100644
--- a/ovn/lib/automake.mk
+++ b/ovn/lib/automake.mk
@@ -6,23 +6,8 @@ ovn_lib_libovn_la_LDFLAGS = \
ovn_lib_libovn_la_SOURCES = \
ovn/lib/acl-log.c \
ovn/lib/acl-log.h \
- ovn/lib/actions.c \
- ovn/lib/chassis-index.c \
- ovn/lib/chassis-index.h \
- ovn/lib/expr.c \
- ovn/lib/extend-table.h \
- ovn/lib/extend-table.c \
- ovn/lib/ip-mcast-index.c \
- ovn/lib/ip-mcast-index.h \
- ovn/lib/mcast-group-index.c \
- ovn/lib/mcast-group-index.h \
- ovn/lib/lex.c \
- ovn/lib/ovn-l7.h \
ovn/lib/ovn-util.c \
ovn/lib/ovn-util.h \
- ovn/lib/logical-fields.c \
- ovn/lib/inc-proc-eng.c \
- ovn/lib/inc-proc-eng.h
nodist_ovn_lib_libovn_la_SOURCES = \
ovn/lib/ovn-nb-idl.c \
ovn/lib/ovn-nb-idl.h \
diff --git a/ovn/lib/chassis-index.c b/ovn/lib/chassis-index.c
deleted file mode 100644
index 10f70fb4a..000000000
--- a/ovn/lib/chassis-index.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* Copyright (c) 2016, 2017 Red Hat, Inc.
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-struct ovsdb_idl_index *
-chassis_index_create(struct ovsdb_idl *idl)
-{
- return ovsdb_idl_index_create1(idl, &sbrec_chassis_col_name);
-}
-
-/* Finds and returns the chassis with the given 'name', or NULL if no such
- * chassis exists. */
-const struct sbrec_chassis *
-chassis_lookup_by_name(struct ovsdb_idl_index *sbrec_chassis_by_name,
- const char *name)
-{
- struct sbrec_chassis *target = sbrec_chassis_index_init_row(
- sbrec_chassis_by_name);
- sbrec_chassis_index_set_name(target, name);
-
- struct sbrec_chassis *retval = sbrec_chassis_index_find(
- sbrec_chassis_by_name, target);
-
- sbrec_chassis_index_destroy_row(target);
-
- return retval;
-}
-
-struct ovsdb_idl_index *
-ha_chassis_group_index_create(struct ovsdb_idl *idl)
-{
- return ovsdb_idl_index_create1(idl, &sbrec_ha_chassis_group_col_name);
-}
-
-/* Finds and returns the HA chassis group with the given 'name', or NULL
- * if no such HA chassis group exists. */
-const struct sbrec_ha_chassis_group *
-ha_chassis_group_lookup_by_name(
- struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name,
- const char *name)
-{
- struct sbrec_ha_chassis_group *target =
- sbrec_ha_chassis_group_index_init_row(sbrec_ha_chassis_grp_by_name);
- sbrec_ha_chassis_group_index_set_name(target, name);
-
- struct sbrec_ha_chassis_group *retval =
- sbrec_ha_chassis_group_index_find(sbrec_ha_chassis_grp_by_name,
- target);
-
- sbrec_ha_chassis_group_index_destroy_row(target);
-
- return retval;
-}
diff --git a/ovn/lib/chassis-index.h b/ovn/lib/chassis-index.h
deleted file mode 100644
index 9bc610ad2..000000000
--- a/ovn/lib/chassis-index.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright (c) 2017, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_CHASSIS_INDEX_H
-#define OVN_CHASSIS_INDEX_H 1
-
-struct ovsdb_idl;
-
-struct ovsdb_idl_index *chassis_index_create(struct ovsdb_idl *);
-
-const struct sbrec_chassis *chassis_lookup_by_name(
- struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name);
-
-struct ovsdb_idl_index *ha_chassis_group_index_create(struct ovsdb_idl *idl);
-const struct sbrec_ha_chassis_group *ha_chassis_group_lookup_by_name(
- struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, const char *name);
-
-#endif /* ovn/lib/chassis-index.h */
diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
deleted file mode 100644
index e4c650f7c..000000000
--- a/ovn/lib/expr.c
+++ /dev/null
@@ -1,3450 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "byte-order.h"
-#include "openvswitch/json.h"
-#include "nx-match.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/match.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/vlog.h"
-#include "openvswitch/shash.h"
-#include "ovn/expr.h"
-#include "ovn/lex.h"
-#include "ovn/logical-fields.h"
-#include "simap.h"
-#include "sset.h"
-#include "util.h"
-
-VLOG_DEFINE_THIS_MODULE(expr);
-
-static struct expr *parse_and_annotate(const char *s,
- const struct shash *symtab,
- struct ovs_list *nesting,
- char **errorp);
-
-/* Returns the name of measurement level 'level'. */
-const char *
-expr_level_to_string(enum expr_level level)
-{
- switch (level) {
- case EXPR_L_NOMINAL: return "nominal";
- case EXPR_L_BOOLEAN: return "Boolean";
- case EXPR_L_ORDINAL: return "ordinal";
- default: OVS_NOT_REACHED();
- }
-}
-
-/* Relational operators. */
-
-/* Returns a string form of relational operator 'relop'. */
-const char *
-expr_relop_to_string(enum expr_relop relop)
-{
- switch (relop) {
- case EXPR_R_EQ: return "==";
- case EXPR_R_NE: return "!=";
- case EXPR_R_LT: return "<";
- case EXPR_R_LE: return "<=";
- case EXPR_R_GT: return ">";
- case EXPR_R_GE: return ">=";
- default: OVS_NOT_REACHED();
- }
-}
-
-bool
-expr_relop_from_token(enum lex_type type, enum expr_relop *relop)
-{
- enum expr_relop r;
-
- switch ((int) type) {
- case LEX_T_EQ: r = EXPR_R_EQ; break;
- case LEX_T_NE: r = EXPR_R_NE; break;
- case LEX_T_LT: r = EXPR_R_LT; break;
- case LEX_T_LE: r = EXPR_R_LE; break;
- case LEX_T_GT: r = EXPR_R_GT; break;
- case LEX_T_GE: r = EXPR_R_GE; break;
- default: return false;
- }
-
- if (relop) {
- *relop = r;
- }
- return true;
-}
-
-/* Returns the relational operator that 'relop' becomes if you turn the
- * relation's operands around, e.g. EXPR_R_EQ does not change because "a == b"
- * and "b == a" are equivalent, but EXPR_R_LE becomes EXPR_R_GE because "a <=
- * b" is equivalent to "b >= a". */
-static enum expr_relop
-expr_relop_turn(enum expr_relop relop)
-{
- switch (relop) {
- case EXPR_R_EQ: return EXPR_R_EQ;
- case EXPR_R_NE: return EXPR_R_NE;
- case EXPR_R_LT: return EXPR_R_GT;
- case EXPR_R_LE: return EXPR_R_GE;
- case EXPR_R_GT: return EXPR_R_LT;
- case EXPR_R_GE: return EXPR_R_LE;
- default: OVS_NOT_REACHED();
- }
-}
-
-/* Returns the relational operator that is the opposite of 'relop'. */
-static enum expr_relop
-expr_relop_invert(enum expr_relop relop)
-{
- switch (relop) {
- case EXPR_R_EQ: return EXPR_R_NE;
- case EXPR_R_NE: return EXPR_R_EQ;
- case EXPR_R_LT: return EXPR_R_GE;
- case EXPR_R_LE: return EXPR_R_GT;
- case EXPR_R_GT: return EXPR_R_LE;
- case EXPR_R_GE: return EXPR_R_LT;
- default: OVS_NOT_REACHED();
- }
-}
-
-/* Checks whether 'relop' is true for strcmp()-like 3-way comparison result
- * 'cmp'. */
-static bool
-expr_relop_test(enum expr_relop relop, int cmp)
-{
- switch (relop) {
- case EXPR_R_EQ: return cmp == 0;
- case EXPR_R_NE: return cmp != 0;
- case EXPR_R_LT: return cmp < 0;
- case EXPR_R_LE: return cmp <= 0;
- case EXPR_R_GT: return cmp > 0;
- case EXPR_R_GE: return cmp >= 0;
- default: OVS_NOT_REACHED();
- }
-}
-
-/* Constructing and manipulating expressions. */
-
-/* Creates and returns a logical AND or OR expression (according to 'type',
- * which must be EXPR_T_AND or EXPR_T_OR) that initially has no
- * sub-expressions. (To satisfy the invariants for expressions, the caller
- * must add at least two sub-expressions whose types are different from
- * 'type'.) */
-struct expr *
-expr_create_andor(enum expr_type type)
-{
- struct expr *e = xmalloc(sizeof *e);
- e->type = type;
- ovs_list_init(&e->andor);
- return e;
-}
-
-/* Returns a logical AND or OR expression (according to 'type', which must be
- * EXPR_T_AND or EXPR_T_OR) whose sub-expressions are 'a' and 'b', with some
- * flexibility:
- *
- * - If 'a' or 'b' is NULL, just returns the other one (which means that if
- * that other one is not of the given 'type', then the returned
- * expression is not either).
- *
- * - If 'a' or 'b', or both, have type 'type', then they are combined into
- * a single node that satisfies the invariants for expressions. */
-struct expr *
-expr_combine(enum expr_type type, struct expr *a, struct expr *b)
-{
- if (!a) {
- return b;
- } else if (!b) {
- return a;
- } else if (a->type == type) {
- if (b->type == type) {
- ovs_list_splice(&a->andor, b->andor.next, &b->andor);
- free(b);
- } else {
- ovs_list_push_back(&a->andor, &b->node);
- }
- return a;
- } else if (b->type == type) {
- ovs_list_push_front(&b->andor, &a->node);
- return b;
- } else {
- struct expr *e = expr_create_andor(type);
- ovs_list_push_back(&e->andor, &a->node);
- ovs_list_push_back(&e->andor, &b->node);
- return e;
- }
-}
-
-static void
-expr_insert_andor(struct expr *andor, struct expr *before, struct expr *new)
-{
- if (new->type == andor->type) {
- if (andor->type == EXPR_T_AND) {
- /* Conjunction junction, what's your function? */
- }
- ovs_list_splice(&before->node, new->andor.next, &new->andor);
- free(new);
- } else {
- ovs_list_insert(&before->node, &new->node);
- }
-}
-
-/* Returns an EXPR_T_BOOLEAN expression with value 'b'. */
-struct expr *
-expr_create_boolean(bool b)
-{
- struct expr *e = xmalloc(sizeof *e);
- e->type = EXPR_T_BOOLEAN;
- e->boolean = b;
- return e;
-}
-
-static void
-expr_not(struct expr *expr)
-{
- struct expr *sub;
-
- switch (expr->type) {
- case EXPR_T_CMP:
- expr->cmp.relop = expr_relop_invert(expr->cmp.relop);
- break;
-
- case EXPR_T_AND:
- case EXPR_T_OR:
- LIST_FOR_EACH (sub, node, &expr->andor) {
- expr_not(sub);
- }
- expr->type = expr->type == EXPR_T_AND ? EXPR_T_OR : EXPR_T_AND;
- break;
-
- case EXPR_T_BOOLEAN:
- expr->boolean = !expr->boolean;
- break;
-
- case EXPR_T_CONDITION:
- expr->cond.not = !expr->cond.not;
- break;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static struct expr *
-expr_fix_andor(struct expr *expr, bool short_circuit)
-{
- struct expr *sub, *next;
-
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- if (sub->type == EXPR_T_BOOLEAN) {
- if (sub->boolean == short_circuit) {
- expr_destroy(expr);
- return expr_create_boolean(short_circuit);
- } else {
- ovs_list_remove(&sub->node);
- expr_destroy(sub);
- }
- }
- }
-
- if (ovs_list_is_short(&expr->andor)) {
- if (ovs_list_is_empty(&expr->andor)) {
- free(expr);
- return expr_create_boolean(!short_circuit);
- } else {
- sub = expr_from_node(ovs_list_front(&expr->andor));
- free(expr);
- return sub;
- }
- } else {
- return expr;
- }
-}
-
-/* Returns 'expr' modified so that top-level oddities are fixed up:
- *
- * - Eliminates any EXPR_T_BOOLEAN operands at the top level.
- *
- * - Replaces one-operand EXPR_T_AND or EXPR_T_OR by its subexpression. */
-static struct expr *
-expr_fix(struct expr *expr)
-{
- switch (expr->type) {
- case EXPR_T_CMP:
- return expr;
-
- case EXPR_T_AND:
- return expr_fix_andor(expr, false);
-
- case EXPR_T_OR:
- return expr_fix_andor(expr, true);
-
- case EXPR_T_BOOLEAN:
- return expr;
-
- case EXPR_T_CONDITION:
- return expr;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Formatting. */
-
-/* Searches bits [0,width) in 'sv' for a contiguous sequence of 1-bits. If one
- * such sequence exists, stores the index of the first 1-bit into '*startp' and
- * the number of 1-bits into '*n_bitsp'. Stores 0 into both variables if no
- * such sequence, or more than one, exists. */
-static void
-find_bitwise_range(const union mf_subvalue *sv, int width,
- int *startp, int *n_bitsp)
-{
- unsigned int start = bitwise_scan(sv, sizeof *sv, true, 0, width);
- if (start < width) {
- unsigned int end = bitwise_scan(sv, sizeof *sv, false, start, width);
- if (end >= width
- || bitwise_scan(sv, sizeof *sv, true, end, width) >= width) {
- *startp = start;
- *n_bitsp = end - start;
- return;
- }
- }
- *startp = *n_bitsp = 0;
-}
-
-static void
-expr_format_cmp(const struct expr *e, struct ds *s)
-{
- /* The common case is numerical comparisons.
- * Handle string comparisons as a special case. */
- if (!e->cmp.symbol->width) {
- ds_put_format(s, "%s %s ", e->cmp.symbol->name,
- expr_relop_to_string(e->cmp.relop));
- json_string_escape(e->cmp.string, s);
- return;
- }
-
- int ofs, n;
- find_bitwise_range(&e->cmp.mask, e->cmp.symbol->width, &ofs, &n);
- if (n == 1 && (e->cmp.relop == EXPR_R_EQ || e->cmp.relop == EXPR_R_NE)) {
- bool positive;
-
- positive = bitwise_get_bit(&e->cmp.value, sizeof e->cmp.value, ofs);
- positive ^= e->cmp.relop == EXPR_R_NE;
- if (!positive) {
- ds_put_char(s, '!');
- }
- ds_put_cstr(s, e->cmp.symbol->name);
- if (e->cmp.symbol->width > 1) {
- ds_put_format(s, "[%d]", ofs);
- }
- return;
- }
-
- ds_put_cstr(s, e->cmp.symbol->name);
- if (n > 0 && n < e->cmp.symbol->width) {
- if (n > 1) {
- ds_put_format(s, "[%d..%d]", ofs, ofs + n - 1);
- } else {
- ds_put_format(s, "[%d]", ofs);
- }
- }
-
- ds_put_format(s, " %s ", expr_relop_to_string(e->cmp.relop));
-
- if (n) {
- union mf_subvalue value;
-
- memset(&value, 0, sizeof value);
- bitwise_copy(&e->cmp.value, sizeof e->cmp.value, ofs,
- &value, sizeof value, 0,
- n);
- mf_format_subvalue(&value, s);
- } else {
- mf_format_subvalue(&e->cmp.value, s);
- ds_put_char(s, '/');
- mf_format_subvalue(&e->cmp.mask, s);
- }
-}
-
-static void
-expr_format_andor(const struct expr *e, const char *op, struct ds *s)
-{
- struct expr *sub;
- int i = 0;
-
- LIST_FOR_EACH (sub, node, &e->andor) {
- if (i++) {
- ds_put_format(s, " %s ", op);
- }
-
- if (sub->type == EXPR_T_AND || sub->type == EXPR_T_OR) {
- ds_put_char(s, '(');
- expr_format(sub, s);
- ds_put_char(s, ')');
- } else {
- expr_format(sub, s);
- }
- }
-}
-
-static void
-expr_format_condition(const struct expr *e, struct ds *s)
-{
- if (e->cond.not) {
- ds_put_char(s, '!');
- }
- switch (e->cond.type) {
- case EXPR_COND_CHASSIS_RESIDENT:
- ds_put_format(s, "is_chassis_resident(");
- json_string_escape(e->cond.string, s);
- ds_put_char(s, ')');
- break;
- }
-}
-
-/* Appends a string form of 'e' to 's'. The string form is acceptable for
- * parsing back into an equivalent expression. */
-void
-expr_format(const struct expr *e, struct ds *s)
-{
- switch (e->type) {
- case EXPR_T_CMP:
- expr_format_cmp(e, s);
- break;
-
- case EXPR_T_AND:
- expr_format_andor(e, "&&", s);
- break;
-
- case EXPR_T_OR:
- expr_format_andor(e, "||", s);
- break;
-
- case EXPR_T_BOOLEAN:
- ds_put_char(s, e->boolean ? '1' : '0');
- break;
-
- case EXPR_T_CONDITION:
- expr_format_condition(e, s);
- break;
- }
-}
-
-/* Prints a string form of 'e' on stdout, followed by a new-line. */
-void
-expr_print(const struct expr *e)
-{
- struct ds output;
-
- ds_init(&output);
- expr_format(e, &output);
- puts(ds_cstr(&output));
- ds_destroy(&output);
-}
-
-/* Parsing. */
-
-#define MAX_PAREN_DEPTH 100
-
-/* Context maintained during expr_parse(). */
-struct expr_context {
- struct lexer *lexer; /* Lexer for pulling more tokens. */
- const struct shash *symtab; /* Symbol table. */
- const struct shash *addr_sets; /* Address set table. */
- const struct shash *port_groups; /* Port group table. */
- struct sset *addr_sets_ref; /* The set of address set referenced. */
- bool not; /* True inside odd number of NOT operators. */
- unsigned int paren_depth; /* Depth of nested parentheses. */
-};
-
-struct expr *expr_parse__(struct expr_context *);
-static void expr_not(struct expr *);
-static bool parse_field(struct expr_context *, struct expr_field *);
-
-static struct expr *
-make_cmp__(const struct expr_field *f, enum expr_relop r,
- const union expr_constant *c)
-{
- struct expr *e = xzalloc(sizeof *e);
- e->type = EXPR_T_CMP;
- e->cmp.symbol = f->symbol;
- e->cmp.relop = r;
- if (f->symbol->width) {
- bitwise_copy(&c->value, sizeof c->value, 0,
- &e->cmp.value, sizeof e->cmp.value, f->ofs,
- f->n_bits);
- if (c->masked) {
- bitwise_copy(&c->mask, sizeof c->mask, 0,
- &e->cmp.mask, sizeof e->cmp.mask, f->ofs,
- f->n_bits);
- } else {
- bitwise_one(&e->cmp.mask, sizeof e->cmp.mask, f->ofs,
- f->n_bits);
- }
- } else {
- e->cmp.string = xstrdup(c->string);
- }
- return e;
-}
-
-/* Returns the minimum reasonable width for integer constant 'c'. */
-static int
-expr_constant_width(const union expr_constant *c)
-{
- if (c->masked) {
- return mf_subvalue_width(&c->mask);
- }
-
- switch (c->format) {
- case LEX_F_DECIMAL:
- case LEX_F_HEXADECIMAL:
- return mf_subvalue_width(&c->value);
-
- case LEX_F_IPV4:
- return 32;
-
- case LEX_F_IPV6:
- return 128;
-
- case LEX_F_ETHERNET:
- return 48;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static bool
-type_check(struct expr_context *ctx, const struct expr_field *f,
- struct expr_constant_set *cs)
-{
- if (cs->type != (f->symbol->width ? EXPR_C_INTEGER : EXPR_C_STRING)) {
- lexer_error(ctx->lexer,
- "%s field %s is not compatible with %s constant.",
- f->symbol->width ? "Integer" : "String",
- f->symbol->name,
- cs->type == EXPR_C_INTEGER ? "integer" : "string");
- return false;
- }
-
- if (f->symbol->width) {
- for (size_t i = 0; i < cs->n_values; i++) {
- int w = expr_constant_width(&cs->values[i]);
- if (w > f->symbol->width) {
- lexer_error(ctx->lexer,
- "%d-bit constant is not compatible with %d-bit "
- "field %s.", w, f->symbol->width, f->symbol->name);
- return false;
- }
- }
- }
-
- return true;
-}
-
-static struct expr *
-make_cmp(struct expr_context *ctx,
- const struct expr_field *f, enum expr_relop r,
- struct expr_constant_set *cs)
-{
- struct expr *e = NULL;
-
- if (!type_check(ctx, f, cs)) {
- goto exit;
- }
-
- if (r != EXPR_R_EQ && r != EXPR_R_NE) {
- if (cs->in_curlies) {
- lexer_error(ctx->lexer, "Only == and != operators may be used "
- "with value sets.");
- goto exit;
- }
- if (f->symbol->level == EXPR_L_NOMINAL ||
- f->symbol->level == EXPR_L_BOOLEAN) {
- lexer_error(ctx->lexer, "Only == and != operators may be used "
- "with %s field %s.",
- expr_level_to_string(f->symbol->level),
- f->symbol->name);
- goto exit;
- }
- if (!cs->n_values) {
- lexer_error(ctx->lexer, "Only == and != operators may be used "
- "to compare a field against an empty value set.");
- goto exit;
- }
- if (cs->values[0].masked) {
- lexer_error(ctx->lexer, "Only == and != operators may be used "
- "with masked constants. Consider using subfields "
- "instead (e.g. eth.src[0..15] > 0x1111 in place of "
- "eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff).");
- goto exit;
- }
- }
-
- if (f->symbol->level == EXPR_L_NOMINAL) {
- if (f->symbol->predicate) {
- ovs_assert(f->symbol->width > 0);
- for (size_t i = 0; i < cs->n_values; i++) {
- const union mf_subvalue *value = &cs->values[i].value;
- bool positive = (value->integer & htonll(1)) != 0;
- positive ^= r == EXPR_R_NE;
- positive ^= ctx->not;
- if (!positive) {
- const char *name = f->symbol->name;
- lexer_error(ctx->lexer,
- "Nominal predicate %s may only be tested "
- "positively, e.g. `%s' or `%s == 1' but not "
- "`!%s' or `%s == 0'.",
- name, name, name, name, name);
- goto exit;
- }
- }
- } else if (r != (ctx->not ? EXPR_R_NE : EXPR_R_EQ)) {
- lexer_error(ctx->lexer, "Nominal field %s may only be tested for "
- "equality (taking enclosing `!' operators into "
- "account).", f->symbol->name);
- goto exit;
- }
- }
-
- if (!cs->n_values) {
- e = expr_create_boolean(r == EXPR_R_NE);
- goto exit;
- }
- e = make_cmp__(f, r, &cs->values[0]);
- for (size_t i = 1; i < cs->n_values; i++) {
- e = expr_combine(r == EXPR_R_EQ ? EXPR_T_OR : EXPR_T_AND,
- e, make_cmp__(f, r, &cs->values[i]));
- }
-exit:
- expr_constant_set_destroy(cs);
- return e;
-}
-
-static bool
-parse_field(struct expr_context *ctx, struct expr_field *f)
-{
- const struct expr_symbol *symbol;
-
- if (ctx->lexer->token.type != LEX_T_ID) {
- lexer_syntax_error(ctx->lexer, "expecting field name");
- return false;
- }
-
- symbol = shash_find_data(ctx->symtab, ctx->lexer->token.s);
- if (!symbol) {
- lexer_syntax_error(ctx->lexer, "expecting field name");
- return false;
- }
- lexer_get(ctx->lexer);
-
- f->symbol = symbol;
- if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) {
- int low, high;
-
- if (!symbol->width) {
- lexer_error(ctx->lexer,
- "Cannot select subfield of string field %s.",
- symbol->name);
- return false;
- }
-
- if (!lexer_force_int(ctx->lexer, &low)) {
- return false;
- }
- if (lexer_match(ctx->lexer, LEX_T_ELLIPSIS)) {
- if (!lexer_force_int(ctx->lexer, &high)) {
- return false;
- }
- } else {
- high = low;
- }
-
- if (!lexer_force_match(ctx->lexer, LEX_T_RSQUARE)) {
- return false;
- }
-
- if (low > high) {
- lexer_error(ctx->lexer, "Invalid bit range %d to %d.", low, high);
- return false;
- } else if (high >= symbol->width) {
- lexer_error(ctx->lexer,
- "Cannot select bits %d to %d of %d-bit field %s.",
- low, high, symbol->width, symbol->name);
- return false;
- } else if (symbol->level == EXPR_L_NOMINAL
- && (low != 0 || high != symbol->width - 1)) {
- lexer_error(ctx->lexer,
- "Cannot select subfield of nominal field %s.",
- symbol->name);
- return false;
- }
-
- f->ofs = low;
- f->n_bits = high - low + 1;
- } else {
- f->ofs = 0;
- f->n_bits = symbol->width;
- }
-
- return true;
-}
-
-static bool
-parse_relop(struct expr_context *ctx, enum expr_relop *relop)
-{
- if (expr_relop_from_token(ctx->lexer->token.type, relop)) {
- lexer_get(ctx->lexer);
- return true;
- } else {
- lexer_syntax_error(ctx->lexer, "expecting relational operator");
- return false;
- }
-}
-
-static bool
-assign_constant_set_type(struct expr_context *ctx,
- struct expr_constant_set *cs,
- enum expr_constant_type type)
-{
- if (!cs->n_values || cs->type == type) {
- cs->type = type;
- return true;
- } else {
- lexer_syntax_error(ctx->lexer, "expecting %s",
- cs->type == EXPR_C_INTEGER ? "integer" : "string");
- return false;
- }
-}
-
-static bool
-parse_addr_sets(struct expr_context *ctx, struct expr_constant_set *cs,
- size_t *allocated_values)
-{
- if (ctx->addr_sets_ref) {
- sset_add(ctx->addr_sets_ref, ctx->lexer->token.s);
- }
-
- struct expr_constant_set *addr_sets
- = (ctx->addr_sets
- ? shash_find_data(ctx->addr_sets, ctx->lexer->token.s)
- : NULL);
- if (!addr_sets) {
- lexer_syntax_error(ctx->lexer, "expecting address set name");
- return false;
- }
-
- if (!assign_constant_set_type(ctx, cs, EXPR_C_INTEGER)) {
- return false;
- }
-
- size_t n_values = cs->n_values + addr_sets->n_values;
- if (n_values >= *allocated_values) {
- cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
- *allocated_values = n_values;
- }
- for (size_t i = 0; i < addr_sets->n_values; i++) {
- cs->values[cs->n_values++] = addr_sets->values[i];
- }
-
- return true;
-}
-
-static bool
-parse_port_group(struct expr_context *ctx, struct expr_constant_set *cs,
- size_t *allocated_values)
-{
- struct expr_constant_set *port_group
- = (ctx->port_groups
- ? shash_find_data(ctx->port_groups, ctx->lexer->token.s)
- : NULL);
- if (!port_group) {
- lexer_syntax_error(ctx->lexer, "expecting port group name");
- return false;
- }
-
- if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
- return false;
- }
-
- size_t n_values = cs->n_values + port_group->n_values;
- if (n_values >= *allocated_values) {
- cs->values = xrealloc(cs->values, n_values * sizeof *cs->values);
- *allocated_values = n_values;
- }
- for (size_t i = 0; i < port_group->n_values; i++) {
- cs->values[cs->n_values++].string =
- xstrdup(port_group->values[i].string);
- }
-
- return true;
-}
-
-static bool
-parse_constant(struct expr_context *ctx, struct expr_constant_set *cs,
- size_t *allocated_values)
-{
- if (cs->n_values >= *allocated_values) {
- cs->values = x2nrealloc(cs->values, allocated_values,
- sizeof *cs->values);
- }
-
- if (ctx->lexer->token.type == LEX_T_STRING) {
- if (!assign_constant_set_type(ctx, cs, EXPR_C_STRING)) {
- return false;
- }
- cs->values[cs->n_values++].string = xstrdup(ctx->lexer->token.s);
- lexer_get(ctx->lexer);
- return true;
- } else if (ctx->lexer->token.type == LEX_T_INTEGER ||
- ctx->lexer->token.type == LEX_T_MASKED_INTEGER) {
- if (!assign_constant_set_type(ctx, cs, EXPR_C_INTEGER)) {
- return false;
- }
-
- union expr_constant *c = &cs->values[cs->n_values++];
- c->value = ctx->lexer->token.value;
- c->format = ctx->lexer->token.format;
- c->masked = ctx->lexer->token.type == LEX_T_MASKED_INTEGER;
- if (c->masked) {
- c->mask = ctx->lexer->token.mask;
- }
- lexer_get(ctx->lexer);
- return true;
- } else if (ctx->lexer->token.type == LEX_T_MACRO) {
- if (!parse_addr_sets(ctx, cs, allocated_values)) {
- return false;
- }
- lexer_get(ctx->lexer);
- return true;
- } else if (ctx->lexer->token.type == LEX_T_PORT_GROUP) {
- if (!parse_port_group(ctx, cs, allocated_values)) {
- return false;
- }
- lexer_get(ctx->lexer);
- return true;
- } else {
- lexer_syntax_error(ctx->lexer, "expecting constant");
- return false;
- }
-}
-
-/* Parses a single or {}-enclosed set of integer or string constants into 'cs',
- * which the caller need not have initialized. Returns true on success, in
- * which case the caller owns 'cs', false on failure, in which case 'cs' is
- * indeterminate. */
-static bool
-parse_constant_set(struct expr_context *ctx, struct expr_constant_set *cs)
-{
- size_t allocated_values = 0;
- bool ok;
-
- memset(cs, 0, sizeof *cs);
- if (lexer_match(ctx->lexer, LEX_T_LCURLY)) {
- ok = true;
- cs->in_curlies = true;
- do {
- if (!parse_constant(ctx, cs, &allocated_values)) {
- ok = false;
- break;
- }
- lexer_match(ctx->lexer, LEX_T_COMMA);
- } while (!lexer_match(ctx->lexer, LEX_T_RCURLY));
- } else {
- ok = parse_constant(ctx, cs, &allocated_values);
- }
- if (!ok) {
- expr_constant_set_destroy(cs);
- }
- return ok;
-}
-
-/* Parses from 'lexer' a single integer or string constant compatible with the
- * type of 'f' into 'c'.
- *
- * Returns true if successful, false if an error occurred. Upon return,
- * returns true if and only if lexer->error is NULL. On failure, 'c' is
- * indeterminate. */
-bool
-expr_constant_parse(struct lexer *lexer, const struct expr_field *f,
- union expr_constant *c)
-{
- if (lexer->error) {
- return false;
- }
-
- struct expr_context ctx = { .lexer = lexer };
-
- struct expr_constant_set cs;
- memset(&cs, 0, sizeof cs);
- size_t allocated_values = 0;
- if (parse_constant(&ctx, &cs, &allocated_values)
- && type_check(&ctx, f, &cs)) {
- *c = cs.values[0];
- cs.n_values = 0;
- }
- expr_constant_set_destroy(&cs);
-
- return !lexer->error;
-}
-
-/* Appends to 's' a re-parseable representation of constant 'c' with the given
- * 'type'. */
-void
-expr_constant_format(const union expr_constant *c,
- enum expr_constant_type type, struct ds *s)
-{
- if (type == EXPR_C_STRING) {
- json_string_escape(c->string, s);
- } else {
- struct lex_token token;
- token.type = c->masked ? LEX_T_MASKED_INTEGER : LEX_T_INTEGER;
- token.s = NULL;
- token.format = c->format;
- token.value = c->value;
- if (c->masked) {
- token.mask = c->mask;
- }
-
- lex_token_format(&token, s);
- }
-}
-
-/* Frees the contents of 'c', which has the specified 'type'.
- *
- * Does not free(c). */
-void
-expr_constant_destroy(const union expr_constant *c,
- enum expr_constant_type type)
-{
- if (c && type == EXPR_C_STRING) {
- free(c->string);
- }
-}
-
-/* Parses from 'lexer' a single or {}-enclosed set of at least one integer or
- * string constants into 'cs', which the caller need not have initialized.
- *
- * Returns true if successful, false if an error occurred. Upon return,
- * returns true if and only if lexer->error is NULL. On failure, 'cs' is
- * indeterminate. */
-bool
-expr_constant_set_parse(struct lexer *lexer, struct expr_constant_set *cs)
-{
- if (!lexer->error) {
- struct expr_context ctx = { .lexer = lexer };
- parse_constant_set(&ctx, cs);
- }
- return !lexer->error;
-}
-
-/* Appends to 's' a re-parseable representation of 'cs'. */
-void
-expr_constant_set_format(const struct expr_constant_set *cs, struct ds *s)
-{
- bool curlies = cs->in_curlies || cs->n_values != 1;
- if (curlies) {
- ds_put_char(s, '{');
- }
-
- for (const union expr_constant *c = cs->values;
- c < &cs->values[cs->n_values]; c++) {
- if (c != cs->values) {
- ds_put_cstr(s, ", ");
- }
-
- expr_constant_format(c, cs->type, s);
- }
-
- if (curlies) {
- ds_put_char(s, '}');
- }
-}
-
-void
-expr_constant_set_destroy(struct expr_constant_set *cs)
-{
- if (cs) {
- if (cs->type == EXPR_C_STRING) {
- for (size_t i = 0; i < cs->n_values; i++) {
- free(cs->values[i].string);
- }
- }
- free(cs->values);
- }
-}
-
-/* Adds an constant set named 'name' to 'const_sets', replacing any existing
- * constant set entry with the given name. */
-void
-expr_const_sets_add(struct shash *const_sets, const char *name,
- const char *const *values, size_t n_values,
- bool convert_to_integer)
-{
- /* Replace any existing entry for this name. */
- expr_const_sets_remove(const_sets, name);
-
- struct expr_constant_set *cs = xzalloc(sizeof *cs);
- cs->in_curlies = true;
- cs->n_values = 0;
- cs->values = xmalloc(n_values * sizeof *cs->values);
- if (convert_to_integer) {
- cs->type = EXPR_C_INTEGER;
- for (size_t i = 0; i < n_values; i++) {
- /* Use the lexer to convert each constant set into the proper
- * integer format. */
- struct lexer lex;
- lexer_init(&lex, values[i]);
- lexer_get(&lex);
- if (lex.token.type != LEX_T_INTEGER
- && lex.token.type != LEX_T_MASKED_INTEGER) {
- VLOG_WARN("Invalid constant set entry: '%s', token type: %d",
- values[i], lex.token.type);
- } else {
- union expr_constant *c = &cs->values[cs->n_values++];
- c->value = lex.token.value;
- c->format = lex.token.format;
- c->masked = lex.token.type == LEX_T_MASKED_INTEGER;
- if (c->masked) {
- c->mask = lex.token.mask;
- }
- }
- lexer_destroy(&lex);
- }
- } else {
- cs->type = EXPR_C_STRING;
- for (size_t i = 0; i < n_values; i++) {
- union expr_constant *c = &cs->values[cs->n_values++];
- c->string = xstrdup(values[i]);
- }
- }
-
- shash_add(const_sets, name, cs);
-}
-
-void
-expr_const_sets_remove(struct shash *const_sets, const char *name)
-{
- struct expr_constant_set *cs = shash_find_and_delete(const_sets, name);
- if (cs) {
- expr_constant_set_destroy(cs);
- free(cs);
- }
-}
-
-/* Destroy all contents of 'const_sets'. */
-void
-expr_const_sets_destroy(struct shash *const_sets)
-{
- struct shash_node *node, *next;
-
- SHASH_FOR_EACH_SAFE (node, next, const_sets) {
- struct expr_constant_set *cs = node->data;
-
- shash_delete(const_sets, node);
- expr_constant_set_destroy(cs);
- free(cs);
- }
-}
-
-static struct expr *
-parse_chassis_resident(struct expr_context *ctx)
-{
- if (ctx->lexer->token.type != LEX_T_STRING) {
- lexer_syntax_error(ctx->lexer, "expecting string");
- return NULL;
- }
-
- struct expr *e = xzalloc(sizeof *e);
- e->type = EXPR_T_CONDITION;
- e->cond.type = EXPR_COND_CHASSIS_RESIDENT;
- e->cond.not = false;
- e->cond.string = xstrdup(ctx->lexer->token.s);
-
- lexer_get(ctx->lexer);
- if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
- expr_destroy(e);
- return NULL;
- }
-
- return e;
-}
-
-static struct expr *
-expr_parse_primary(struct expr_context *ctx, bool *atomic)
-{
- *atomic = false;
- if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
- if (ctx->paren_depth >= MAX_PAREN_DEPTH) {
- lexer_error(ctx->lexer, "Parentheses nested too deeply.");
- return NULL;
- }
-
- ctx->paren_depth++;
- struct expr *e = expr_parse__(ctx);
- ctx->paren_depth--;
-
- if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
- expr_destroy(e);
- return NULL;
- }
- *atomic = true;
- return e;
- }
-
- if (ctx->lexer->token.type == LEX_T_ID) {
- struct expr_field f;
- enum expr_relop r;
- struct expr_constant_set c;
-
- if (lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
- if (lexer_match_id(ctx->lexer, "is_chassis_resident")) {
- lexer_get(ctx->lexer); /* Skip "(". */
- *atomic = true;
- return parse_chassis_resident(ctx);
- }
- lexer_error(ctx->lexer, "parsing function name");
- return NULL;
- }
-
- if (!parse_field(ctx, &f)) {
- return NULL;
- }
-
- if (!expr_relop_from_token(ctx->lexer->token.type, &r)) {
- if (!f.n_bits || ctx->lexer->token.type == LEX_T_EQUALS) {
- lexer_syntax_error(ctx->lexer,
- "expecting relational operator");
- return NULL;
- } else if (f.n_bits > 1 && !ctx->not) {
- lexer_error(ctx->lexer,
- "Explicit `!= 0' is required for inequality "
- "test of multibit field against 0.");
- return NULL;
- }
-
- *atomic = true;
-
- union expr_constant *cst = xzalloc(sizeof *cst);
- cst->format = LEX_F_HEXADECIMAL;
- cst->masked = false;
-
- c.type = EXPR_C_INTEGER;
- c.values = cst;
- c.n_values = 1;
- c.in_curlies = false;
- return make_cmp(ctx, &f, EXPR_R_NE, &c);
- } else if (parse_relop(ctx, &r) && parse_constant_set(ctx, &c)) {
- return make_cmp(ctx, &f, r, &c);
- } else {
- return NULL;
- }
- } else {
- struct expr_constant_set c1;
- if (!parse_constant_set(ctx, &c1)) {
- return NULL;
- }
-
- if (!expr_relop_from_token(ctx->lexer->token.type, NULL)
- && c1.n_values == 1
- && c1.type == EXPR_C_INTEGER
- && c1.values[0].format == LEX_F_DECIMAL
- && !c1.values[0].masked
- && !c1.in_curlies) {
- uint64_t x = ntohll(c1.values[0].value.integer);
- if (x <= 1) {
- *atomic = true;
- expr_constant_set_destroy(&c1);
- return expr_create_boolean(x);
- }
- }
-
- enum expr_relop r1;
- struct expr_field f;
- if (!parse_relop(ctx, &r1) || !parse_field(ctx, &f)) {
- expr_constant_set_destroy(&c1);
- return NULL;
- }
-
- if (!expr_relop_from_token(ctx->lexer->token.type, NULL)) {
- return make_cmp(ctx, &f, expr_relop_turn(r1), &c1);
- }
-
- enum expr_relop r2;
- struct expr_constant_set c2;
- if (!parse_relop(ctx, &r2) || !parse_constant_set(ctx, &c2)) {
- expr_constant_set_destroy(&c1);
- return NULL;
- } else {
- /* Reject "1 == field == 2", "1 < field > 2", and so on. */
- if (!(((r1 == EXPR_R_LT || r1 == EXPR_R_LE) &&
- (r2 == EXPR_R_LT || r2 == EXPR_R_LE)) ||
- ((r1 == EXPR_R_GT || r1 == EXPR_R_GE) &&
- (r2 == EXPR_R_GT || r2 == EXPR_R_GE)))) {
- lexer_error(ctx->lexer, "Range expressions must have the "
- "form `x < field < y' or `x > field > y', with "
- "each `<' optionally replaced by `<=' or `>' by "
- "`>=').");
- expr_constant_set_destroy(&c1);
- expr_constant_set_destroy(&c2);
- return NULL;
- }
-
- struct expr *e1 = make_cmp(ctx, &f, expr_relop_turn(r1), &c1);
- struct expr *e2 = make_cmp(ctx, &f, r2, &c2);
- if (ctx->lexer->error) {
- expr_destroy(e1);
- expr_destroy(e2);
- return NULL;
- }
- return expr_combine(EXPR_T_AND, e1, e2);
- }
- }
-}
-
-static struct expr *
-expr_parse_not(struct expr_context *ctx)
-{
- bool atomic;
-
- if (lexer_match(ctx->lexer, LEX_T_LOG_NOT)) {
- ctx->not = !ctx->not;
- struct expr *expr = expr_parse_primary(ctx, &atomic);
- ctx->not = !ctx->not;
-
- if (expr) {
- if (!atomic) {
- lexer_error(ctx->lexer,
- "Missing parentheses around operand of !.");
- expr_destroy(expr);
- return NULL;
- }
- expr_not(expr);
- }
- return expr;
- } else {
- return expr_parse_primary(ctx, &atomic);
- }
-}
-
-struct expr *
-expr_parse__(struct expr_context *ctx)
-{
- struct expr *e = expr_parse_not(ctx);
- if (!e) {
- return NULL;
- }
-
- enum lex_type lex_type = ctx->lexer->token.type;
- if (lex_type == LEX_T_LOG_AND || lex_type == LEX_T_LOG_OR) {
- enum expr_type expr_type
- = lex_type == LEX_T_LOG_AND ? EXPR_T_AND : EXPR_T_OR;
-
- lexer_get(ctx->lexer);
- do {
- struct expr *e2 = expr_parse_not(ctx);
- if (!e2) {
- expr_destroy(e);
- return NULL;
- }
- e = expr_combine(expr_type, e, e2);
- } while (lexer_match(ctx->lexer, lex_type));
- if (ctx->lexer->token.type == LEX_T_LOG_AND
- || ctx->lexer->token.type == LEX_T_LOG_OR) {
- expr_destroy(e);
- lexer_error(ctx->lexer,
- "&& and || must be parenthesized when used together.");
- return NULL;
- }
- }
- return e;
-}
-
-/* Parses an expression from 'lexer' using the symbols in 'symtab' and
- * address set table in 'addr_sets'. If successful, returns the new
- * expression; on failure, returns NULL. Returns nonnull if and only if
- * lexer->error is NULL. */
-struct expr *
-expr_parse(struct lexer *lexer, const struct shash *symtab,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- struct sset *addr_sets_ref)
-{
- struct expr_context ctx = { .lexer = lexer,
- .symtab = symtab,
- .addr_sets = addr_sets,
- .port_groups = port_groups,
- .addr_sets_ref = addr_sets_ref };
- return lexer->error ? NULL : expr_parse__(&ctx);
-}
-
-/* Parses the expression in 's' using the symbols in 'symtab' and
- * address set table in 'addr_sets'. If successful, returns the new
- * expression and sets '*errorp' to NULL. On failure, returns NULL and
- * sets '*errorp' to an explanatory error message. The caller must
- * eventually free the returned expression (with expr_destroy()) or
- * error (with free()). */
-struct expr *
-expr_parse_string(const char *s, const struct shash *symtab,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- struct sset *addr_sets_ref,
- char **errorp)
-{
- struct lexer lexer;
-
- lexer_init(&lexer, s);
- lexer_get(&lexer);
- struct expr *expr = expr_parse(&lexer, symtab, addr_sets, port_groups,
- addr_sets_ref);
- lexer_force_end(&lexer);
- *errorp = lexer_steal_error(&lexer);
- if (*errorp) {
- expr_destroy(expr);
- expr = NULL;
- }
- lexer_destroy(&lexer);
-
- return expr;
-}
-
-/* Parses a field or subfield from 'lexer' into 'field', obtaining field names
- * from 'symtab'. Returns true if successful, false if an error occurred.
- * Upon return, returns true if and only if lexer->error is NULL. */
-bool
-expr_field_parse(struct lexer *lexer, const struct shash *symtab,
- struct expr_field *field, struct expr **prereqsp)
-{
- struct expr_context ctx = { .lexer = lexer, .symtab = symtab };
- if (parse_field(&ctx, field) && field->symbol->predicate) {
- lexer_error(lexer, "Predicate symbol %s used where lvalue required.",
- field->symbol->name);
- }
- if (!lexer->error) {
- const struct expr_symbol *symbol = field->symbol;
- while (symbol) {
- if (symbol->prereqs) {
- char *error;
- struct ovs_list nesting = OVS_LIST_INITIALIZER(&nesting);
- struct expr *e = parse_and_annotate(symbol->prereqs, symtab,
- &nesting, &error);
- if (error) {
- lexer_error(lexer, "%s", error);
- free(error);
- break;
- }
- *prereqsp = expr_combine(EXPR_T_AND, *prereqsp, e);
- }
-
- if (!symbol->parent) {
- break;
- }
- symbol = symbol->parent;
- }
- }
- if (!lexer->error) {
- return true;
- }
- memset(field, 0, sizeof *field);
- return false;
-}
-
-/* Appends to 's' a re-parseable representation of 'field'. */
-void
-expr_field_format(const struct expr_field *field, struct ds *s)
-{
- ds_put_cstr(s, field->symbol->name);
- if (field->ofs || field->n_bits != field->symbol->width) {
- if (field->n_bits != 1) {
- ds_put_format(s, "[%d..%d]",
- field->ofs, field->ofs + field->n_bits - 1);
- } else {
- ds_put_format(s, "[%d]", field->ofs);
- }
- }
-}
-
-void
-expr_symbol_format(const struct expr_symbol *symbol, struct ds *s)
-{
- ds_put_format(s, "%s = ", symbol->name);
- if (symbol->parent) {
- struct expr_field f = { symbol->parent,
- symbol->parent_ofs,
- symbol->width };
- expr_field_format(&f, s);
- } else if (symbol->predicate) {
- ds_put_cstr(s, symbol->predicate);
- } else if (symbol->ovn_field) {
- ds_put_cstr(s, symbol->name);
- } else {
- nx_format_field_name(symbol->field->id, OFP13_VERSION, s);
- }
-}
-
-static struct expr_symbol *
-add_symbol(struct shash *symtab, const char *name, int width,
- const char *prereqs, enum expr_level level,
- bool must_crossproduct, bool rw)
-{
- struct expr_symbol *symbol = xzalloc(sizeof *symbol);
- symbol->name = xstrdup(name);
- symbol->prereqs = prereqs && prereqs[0] ? xstrdup(prereqs) : NULL;
- symbol->width = width;
- symbol->level = level;
- symbol->must_crossproduct = must_crossproduct;
- symbol->rw = rw;
- shash_add_assert(symtab, symbol->name, symbol);
- return symbol;
-}
-
-/* Adds field 'id' to symbol table 'symtab' under the given 'name'. Whenever
- * 'name' is referenced, expression annotation (see expr_annotate()) will
- * ensure that 'prereqs' are also true. If 'must_crossproduct' is true, then
- * conversion to flows will never attempt to use the field as a conjunctive
- * match dimension (see "Crossproducting" in the large comment on struct
- * expr_symbol in expr.h for an example).
- *
- * A given field 'id' must only be used for a single symbol in a symbol table.
- * Use subfields to duplicate or subset a field (you can even make a subfield
- * include all the bits of the "parent" field if you like). */
-struct expr_symbol *
-expr_symtab_add_field(struct shash *symtab, const char *name,
- enum mf_field_id id, const char *prereqs,
- bool must_crossproduct)
-{
- const struct mf_field *field = mf_from_id(id);
- struct expr_symbol *symbol;
-
- symbol = add_symbol(symtab, name, field->n_bits, prereqs,
- (field->maskable == MFM_FULLY
- ? EXPR_L_ORDINAL
- : EXPR_L_NOMINAL),
- must_crossproduct, field->writable);
- symbol->field = field;
- return symbol;
-}
-
-static bool
-parse_field_from_string(const char *s, const struct shash *symtab,
- struct expr_field *field, char **errorp)
-{
- struct lexer lexer;
- lexer_init(&lexer, s);
- lexer_get(&lexer);
-
- struct expr_context ctx = { .lexer = &lexer, .symtab = symtab };
- parse_field(&ctx, field);
- lexer_force_end(&lexer);
- *errorp = lexer_steal_error(&lexer);
- lexer_destroy(&lexer);
-
- return !*errorp;
-}
-
-/* Adds 'name' as a subfield of a larger field in 'symtab'. Whenever
- * 'name' is referenced, expression annotation (see expr_annotate()) will
- * ensure that 'prereqs' are also true.
- *
- * 'subfield' must describe the subfield as a string, e.g. "vlan.tci[0..11]"
- * for the low 12 bits of a larger field named "vlan.tci". */
-struct expr_symbol *
-expr_symtab_add_subfield(struct shash *symtab, const char *name,
- const char *prereqs, const char *subfield)
-{
- struct expr_symbol *symbol;
- struct expr_field f;
- char *error;
-
- if (!parse_field_from_string(subfield, symtab, &f, &error)) {
- VLOG_WARN("%s: error parsing %s subfield (%s)", subfield, name, error);
- free(error);
- return NULL;
- }
-
- enum expr_level level = f.symbol->level;
- if (level != EXPR_L_ORDINAL) {
- VLOG_WARN("can't define %s as subfield of %s field %s",
- name, expr_level_to_string(level), f.symbol->name);
- }
-
- symbol = add_symbol(symtab, name, f.n_bits, prereqs, level, false,
- f.symbol->rw);
- symbol->parent = f.symbol;
- symbol->parent_ofs = f.ofs;
- return symbol;
-}
-
-/* Adds a string-valued symbol named 'name' to 'symtab' with the specified
- * 'prereqs'. */
-struct expr_symbol *
-expr_symtab_add_string(struct shash *symtab, const char *name,
- enum mf_field_id id, const char *prereqs)
-{
- const struct mf_field *field = mf_from_id(id);
- struct expr_symbol *symbol;
-
- symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false,
- field->writable);
- symbol->field = field;
- return symbol;
-}
-
-static enum expr_level
-expr_get_level(const struct expr *expr)
-{
- const struct expr *sub;
- enum expr_level level = EXPR_L_ORDINAL;
-
- switch (expr->type) {
- case EXPR_T_CMP:
- return (expr->cmp.symbol->level == EXPR_L_NOMINAL
- ? EXPR_L_NOMINAL
- : EXPR_L_BOOLEAN);
-
- case EXPR_T_AND:
- case EXPR_T_OR:
- LIST_FOR_EACH (sub, node, &expr->andor) {
- enum expr_level sub_level = expr_get_level(sub);
- level = MIN(level, sub_level);
- }
- return level;
-
- case EXPR_T_BOOLEAN:
- case EXPR_T_CONDITION:
- return EXPR_L_BOOLEAN;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static enum expr_level
-expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
-{
- struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, errorp);
- enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
- expr_destroy(expr);
- return level;
-}
-
-/* Adds a predicate symbol, whose value is the given Boolean 'expression',
- * named 'name' to 'symtab'. For example, "ip4 && ip4.proto == 6" might be an
- * appropriate predicate named "tcp4". */
-struct expr_symbol *
-expr_symtab_add_predicate(struct shash *symtab, const char *name,
- const char *expansion)
-{
- struct expr_symbol *symbol;
- enum expr_level level;
- char *error;
-
- level = expr_parse_level(expansion, symtab, &error);
- if (error) {
- VLOG_WARN("%s: error parsing %s expansion (%s)",
- expansion, name, error);
- free(error);
- return NULL;
- }
-
- symbol = add_symbol(symtab, name, 1, NULL, level, false, false);
- symbol->predicate = xstrdup(expansion);
- return symbol;
-}
-
-struct expr_symbol *
-expr_symtab_add_ovn_field(struct shash *symtab, const char *name,
- enum ovn_field_id id)
-{
- const struct ovn_field *ovn_field = ovn_field_from_id(id);
- struct expr_symbol *symbol;
-
- symbol = add_symbol(symtab, name, ovn_field->n_bits, NULL,
- EXPR_L_NOMINAL, false, true);
- symbol->ovn_field = ovn_field;
- return symbol;
-}
-
-/* Destroys 'symtab' and all of its symbols. */
-void
-expr_symtab_destroy(struct shash *symtab)
-{
- struct shash_node *node, *next;
-
- SHASH_FOR_EACH_SAFE (node, next, symtab) {
- struct expr_symbol *symbol = node->data;
-
- shash_delete(symtab, node);
- free(symbol->name);
- free(symbol->prereqs);
- free(symbol->predicate);
- free(symbol);
- }
-}
-
-/* Cloning. */
-
-static struct expr *
-expr_clone_cmp(struct expr *expr)
-{
- struct expr *new = xmemdup(expr, sizeof *expr);
- if (!new->cmp.symbol->width) {
- new->cmp.string = xstrdup(new->cmp.string);
- }
- return new;
-}
-
-static struct expr *
-expr_clone_andor(struct expr *expr)
-{
- struct expr *new = expr_create_andor(expr->type);
- struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &expr->andor) {
- struct expr *new_sub = expr_clone(sub);
- ovs_list_push_back(&new->andor, &new_sub->node);
- }
- return new;
-}
-
-static struct expr *
-expr_clone_condition(struct expr *expr)
-{
- struct expr *new = xmemdup(expr, sizeof *expr);
- new->cond.string = xstrdup(new->cond.string);
- return new;
-}
-
-/* Returns a clone of 'expr'. This is a "deep copy": neither the returned
- * expression nor any of its substructure will be shared with 'expr'. */
-struct expr *
-expr_clone(struct expr *expr)
-{
- switch (expr->type) {
- case EXPR_T_CMP:
- return expr_clone_cmp(expr);
-
- case EXPR_T_AND:
- case EXPR_T_OR:
- return expr_clone_andor(expr);
-
- case EXPR_T_BOOLEAN:
- return expr_create_boolean(expr->boolean);
-
- case EXPR_T_CONDITION:
- return expr_clone_condition(expr);
- }
- OVS_NOT_REACHED();
-}
-
-/* Destroys 'expr' and all of the sub-expressions it references. */
-void
-expr_destroy(struct expr *expr)
-{
- if (!expr) {
- return;
- }
-
- struct expr *sub, *next;
-
- switch (expr->type) {
- case EXPR_T_CMP:
- if (!expr->cmp.symbol->width) {
- free(expr->cmp.string);
- }
- break;
-
- case EXPR_T_AND:
- case EXPR_T_OR:
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- ovs_list_remove(&sub->node);
- expr_destroy(sub);
- }
- break;
-
- case EXPR_T_BOOLEAN:
- break;
-
- case EXPR_T_CONDITION:
- free(expr->cond.string);
- break;
- }
- free(expr);
-}
-
-/* Annotation. */
-
-/* An element in a linked list of symbols.
- *
- * Used to detect when a symbol is being expanded recursively, to allow
- * flagging an error. */
-struct annotation_nesting {
- struct ovs_list node;
- const struct expr_symbol *symbol;
-};
-
-static struct expr *expr_annotate_(struct expr *, const struct shash *symtab,
- struct ovs_list *nesting, char **errorp);
-
-static struct expr *
-parse_and_annotate(const char *s, const struct shash *symtab,
- struct ovs_list *nesting, char **errorp)
-{
- char *error;
- struct expr *expr;
-
- expr = expr_parse_string(s, symtab, NULL, NULL, NULL, &error);
- if (expr) {
- expr = expr_annotate_(expr, symtab, nesting, &error);
- }
- if (expr) {
- *errorp = NULL;
- } else {
- *errorp = xasprintf("Error parsing expression `%s' encountered as "
- "prerequisite or predicate of initial expression: "
- "%s", s, error);
- free(error);
- }
- return expr;
-}
-
-static struct expr *
-expr_annotate_cmp(struct expr *expr, const struct shash *symtab,
- bool append_prereqs, struct ovs_list *nesting, char **errorp)
-{
- const struct expr_symbol *symbol = expr->cmp.symbol;
- const struct annotation_nesting *iter;
- LIST_FOR_EACH (iter, node, nesting) {
- if (iter->symbol == symbol) {
- *errorp = xasprintf("Recursive expansion of symbol `%s'.",
- symbol->name);
- expr_destroy(expr);
- return NULL;
- }
- }
-
- struct annotation_nesting an;
- an.symbol = symbol;
- ovs_list_push_back(nesting, &an.node);
-
- struct expr *prereqs = NULL;
- if (append_prereqs && symbol->prereqs) {
- prereqs = parse_and_annotate(symbol->prereqs, symtab, nesting, errorp);
- if (!prereqs) {
- goto error;
- }
- }
-
- if (symbol->parent) {
- expr->cmp.symbol = symbol->parent;
- mf_subvalue_shift(&expr->cmp.value, symbol->parent_ofs);
- mf_subvalue_shift(&expr->cmp.mask, symbol->parent_ofs);
- } else if (symbol->predicate) {
- struct expr *predicate;
-
- predicate = parse_and_annotate(symbol->predicate, symtab,
- nesting, errorp);
- if (!predicate) {
- goto error;
- }
-
- bool positive = (expr->cmp.value.integer & htonll(1)) != 0;
- positive ^= expr->cmp.relop == EXPR_R_NE;
- if (!positive) {
- expr_not(predicate);
- }
-
- expr_destroy(expr);
- expr = predicate;
- }
-
- *errorp = NULL;
- ovs_list_remove(&an.node);
- return prereqs ? expr_combine(EXPR_T_AND, expr, prereqs) : expr;
-
-error:
- expr_destroy(expr);
- expr_destroy(prereqs);
- ovs_list_remove(&an.node);
- return NULL;
-}
-
-/* Append (logical AND) prerequisites for given symbol to the expression. */
-static struct expr *
-expr_append_prereqs(struct expr *expr, const struct expr_symbol *symbol,
- const struct shash *symtab, struct ovs_list *nesting,
- char **errorp)
-{
- struct expr *prereqs = NULL;
-
- if (symbol->prereqs) {
- prereqs = parse_and_annotate(symbol->prereqs, symtab, nesting, errorp);
- if (!prereqs) {
- expr_destroy(expr);
- return NULL;
- }
- }
-
- return prereqs ? expr_combine(EXPR_T_AND, expr, prereqs) : expr;
-}
-
-static const struct expr_symbol *expr_get_unique_symbol(
- const struct expr *expr);
-
-/* Ordinarily, annotation adds prerequisites to the expression, and that's what
- * this function does if 'append_prereqs' is true. If 'append_prereqs' is
- * false, this function ignores prerequisites (in which case the caller must
- * have arranged to deal with them). */
-static struct expr *
-expr_annotate__(struct expr *expr, const struct shash *symtab,
- bool append_prereqs, struct ovs_list *nesting, char **errorp)
-{
- switch (expr->type) {
- case EXPR_T_CMP:
- return expr_annotate_cmp(expr, symtab, append_prereqs, nesting,
- errorp);
-
- case EXPR_T_AND:
- case EXPR_T_OR: {
- struct expr *sub, *next;
-
- /* Detect whether every term in 'expr' mentions the same symbol. If
- * so, then suppress prerequisites for that symbol for those terms and
- * instead apply them once at our higher level.
- *
- * If 'append_prereqs' is false, though, we're not supposed to handle
- * prereqs at all (because our caller is already doing it). */
- if (append_prereqs) {
- const struct expr_symbol *sym = expr_get_unique_symbol(expr);
- if (sym) {
- append_prereqs = false;
- expr = expr_append_prereqs(expr, sym, symtab, nesting, errorp);
- if (!expr) {
- return NULL;
- }
- }
- }
-
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- ovs_list_remove(&sub->node);
- struct expr *new_sub = expr_annotate__(sub, symtab, append_prereqs,
- nesting, errorp);
- if (!new_sub) {
- expr_destroy(expr);
- return NULL;
- }
- expr_insert_andor(expr, next, new_sub);
- }
- *errorp = NULL;
- return expr;
- }
-
- case EXPR_T_BOOLEAN:
- case EXPR_T_CONDITION:
- *errorp = NULL;
- return expr;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Same interface and purpose as expr_annotate(), with an additional parameter
- * for internal bookkeeping.
- *
- * Uses 'nesting' to ensure that a given symbol is not recursively expanded. */
-static struct expr *
-expr_annotate_(struct expr *expr, const struct shash *symtab,
- struct ovs_list *nesting, char **errorp)
-{
- return expr_annotate__(expr, symtab, true, nesting, errorp);
-}
-
-/* "Annotates" 'expr', which does the following:
- *
- * - Applies prerequisites, by locating each comparison operator whose
- * field has a prerequisite and adding a logical AND against those
- * prerequisites.
- *
- * - Expands references to subfield symbols, by replacing them by
- * references to their underlying field symbols (suitably shifted).
- *
- * - Expands references to predicate symbols, by replacing them by the
- * expressions that they expand to.
- *
- * In each case, annotation occurs recursively as necessary.
- *
- * If successful, returns the annotated expression and sets '*errorp' to NULL.
- * On failure, returns NULL and sets '*errorp' to an explanatory error message,
- * which the caller must free. In either case, the caller transfers ownership
- * of 'expr' and receives ownership of the returned expression, if any. */
-struct expr *
-expr_annotate(struct expr *expr, const struct shash *symtab, char **errorp)
-{
- struct ovs_list nesting = OVS_LIST_INITIALIZER(&nesting);
- return expr_annotate_(expr, symtab, &nesting, errorp);
-}
-
-static struct expr *
-expr_simplify_eq(struct expr *expr)
-{
- const union mf_subvalue *mask = &expr->cmp.mask;
- if (is_all_zeros(mask, sizeof *mask)) {
- /* Simplify "ip4.dst == 0/0" to just "1" (plus a prerequisite). */
- expr_destroy(expr);
- return expr_create_boolean(true);
- }
- return expr;
-}
-
-static struct expr *
-expr_simplify_ne(struct expr *expr)
-{
- struct expr *new = NULL;
- const union mf_subvalue *value = &expr->cmp.value;
- const union mf_subvalue *mask = &expr->cmp.mask;
- int w = expr->cmp.symbol->width;
- int i;
-
- for (i = 0; (i = bitwise_scan(mask, sizeof *mask, true, i, w)) < w; i++) {
- struct expr *e;
-
- e = xzalloc(sizeof *e);
- e->type = EXPR_T_CMP;
- e->cmp.symbol = expr->cmp.symbol;
- e->cmp.relop = EXPR_R_EQ;
- bitwise_put_bit(&e->cmp.value, sizeof e->cmp.value, i,
- !bitwise_get_bit(value, sizeof *value, i));
- bitwise_put1(&e->cmp.mask, sizeof e->cmp.mask, i);
-
- new = expr_combine(EXPR_T_OR, new, e);
- }
- if (!new) {
- /* Handle a comparison like "ip4.dst != 0/0", where the mask has no
- * 1-bits.
- *
- * The correct result for this expression may not be obvious. It's
- * easier to understand that "ip4.dst == 0/0" should be true, since 0/0
- * matches every IPv4 address; then, "ip4.dst != 0/0" should have the
- * opposite result. */
- new = expr_create_boolean(false);
- }
-
- expr_destroy(expr);
-
- return new;
-}
-
-static struct expr *
-expr_simplify_relational(struct expr *expr)
-{
- const union mf_subvalue *value = &expr->cmp.value;
- int start, n_bits, end;
-
- find_bitwise_range(&expr->cmp.mask, expr->cmp.symbol->width,
- &start, &n_bits);
- ovs_assert(n_bits > 0);
- end = start + n_bits;
-
- /* Handle some special cases.
- *
- * These optimize to just "true":
- *
- * tcp.dst >= 0
- * tcp.dst <= 65535
- *
- * These are easier to understand, and equivalent, when treated as if
- * > or < were !=:
- *
- * tcp.dst > 0
- * tcp.dst < 65535
- */
- bool lt = expr->cmp.relop == EXPR_R_LT || expr->cmp.relop == EXPR_R_LE;
- bool eq = expr->cmp.relop == EXPR_R_LE || expr->cmp.relop == EXPR_R_GE;
- if (bitwise_scan(value, sizeof *value, !lt, start, end) == end) {
- if (eq) {
- expr_destroy(expr);
- return expr_create_boolean(true);
- } else {
- return expr_simplify_ne(expr);
- }
- }
-
- /* Reduce "tcp.dst >= 1234" to "tcp.dst == 1234 || tcp.dst > 1234",
- * and similarly for "tcp.dst <= 1234". */
- struct expr *new = NULL;
- if (eq) {
- new = xmemdup(expr, sizeof *expr);
- new->cmp.relop = EXPR_R_EQ;
- }
-
- for (int z = bitwise_scan(value, sizeof *value, lt, start, end);
- z < end;
- z = bitwise_scan(value, sizeof *value, lt, z + 1, end)) {
- struct expr *e;
-
- e = xmemdup(expr, sizeof *expr);
- e->cmp.relop = EXPR_R_EQ;
- bitwise_toggle_bit(&e->cmp.value, sizeof e->cmp.value, z);
- bitwise_zero(&e->cmp.value, sizeof e->cmp.value, start, z - start);
- bitwise_zero(&e->cmp.mask, sizeof e->cmp.mask, start, z - start);
- new = expr_combine(EXPR_T_OR, new, e);
- }
- expr_destroy(expr);
- return new ? new : expr_create_boolean(false);
-}
-
-/* Resolves condition and replaces the expression with a boolean. */
-static struct expr *
-expr_simplify_condition(struct expr *expr,
- bool (*is_chassis_resident)(const void *c_aux,
- const char *port_name),
- const void *c_aux)
-{
- bool result;
-
- switch (expr->cond.type) {
- case EXPR_COND_CHASSIS_RESIDENT:
- result = is_chassis_resident(c_aux, expr->cond.string);
- break;
-
- default:
- OVS_NOT_REACHED();
- }
-
- result ^= expr->cond.not;
- expr_destroy(expr);
- return expr_create_boolean(result);
-}
-
-/* Takes ownership of 'expr' and returns an equivalent expression whose
- * EXPR_T_CMP nodes use only tests for equality (EXPR_R_EQ). */
-struct expr *
-expr_simplify(struct expr *expr,
- bool (*is_chassis_resident)(const void *c_aux,
- const char *port_name),
- const void *c_aux)
-{
- struct expr *sub, *next;
-
- switch (expr->type) {
- case EXPR_T_CMP:
- return (!expr->cmp.symbol->width ? expr
- : expr->cmp.relop == EXPR_R_EQ ? expr_simplify_eq(expr)
- : expr->cmp.relop == EXPR_R_NE ? expr_simplify_ne(expr)
- : expr_simplify_relational(expr));
-
- case EXPR_T_AND:
- case EXPR_T_OR:
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- ovs_list_remove(&sub->node);
- expr_insert_andor(expr, next,
- expr_simplify(sub, is_chassis_resident, c_aux));
- }
- return expr_fix(expr);
-
- case EXPR_T_BOOLEAN:
- return expr;
-
- case EXPR_T_CONDITION:
- return expr_simplify_condition(expr, is_chassis_resident, c_aux);
- }
- OVS_NOT_REACHED();
-}
-
-/* Tests whether 'expr' is an expression over exactly one symbol: that is,
- * whether it is either a EXPR_T_CMP node or a tree of ANDs and ORs all over
- * the same symbol. If it is, returns the symbol in question. If it is not
- * (that is, if there is more than one symbol or no symbols at all), returns
- * NULL. */
-static const struct expr_symbol *
-expr_get_unique_symbol(const struct expr *expr)
-{
- switch (expr->type) {
- case EXPR_T_CMP:
- return expr->cmp.symbol;
-
- case EXPR_T_AND:
- case EXPR_T_OR: {
- const struct expr_symbol *prev = NULL;
- struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &expr->andor) {
- const struct expr_symbol *symbol = expr_get_unique_symbol(sub);
- if (!symbol || (prev && symbol != prev)) {
- return NULL;
- }
- prev = symbol;
- }
- return prev;
- }
-
- case EXPR_T_BOOLEAN:
- case EXPR_T_CONDITION:
- return NULL;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-struct expr_sort {
- struct expr *expr;
- const struct expr_symbol *symbol;
- enum expr_type type;
-};
-
-static int
-compare_expr_sort(const void *a_, const void *b_)
-{
- const struct expr_sort *a = a_;
- const struct expr_sort *b = b_;
-
- if (a->type != b->type) {
- return a->type < b->type ? -1 : 1;
- } else if (a->symbol) {
- int cmp = strcmp(a->symbol->name, b->symbol->name);
- if (cmp) {
- return cmp;
- }
-
- enum expr_type a_type = a->expr->type;
- enum expr_type b_type = a->expr->type;
- return a_type < b_type ? -1 : a_type > b_type;
- } else if (a->type == EXPR_T_AND || a->type == EXPR_T_OR) {
- size_t a_len = ovs_list_size(&a->expr->andor);
- size_t b_len = ovs_list_size(&b->expr->andor);
- return a_len < b_len ? -1 : a_len > b_len;
- } else {
- return 0;
- }
-}
-
-static struct expr *crush_cmps(struct expr *, const struct expr_symbol *);
-
-static bool
-disjunction_matches_string(const struct expr *or, const char *s)
-{
- const struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &or->andor) {
- if (!strcmp(sub->cmp.string, s)) {
- return true;
- }
- }
-
- return false;
-}
-
-/* Implementation of crush_cmps() for expr->type == EXPR_T_AND and a
- * string-typed 'symbol'. */
-static struct expr *
-crush_and_string(struct expr *expr, const struct expr_symbol *symbol)
-{
- ovs_assert(!ovs_list_is_short(&expr->andor));
-
- struct expr *singleton = NULL;
-
- /* First crush each subexpression into either a single EXPR_T_CMP or an
- * EXPR_T_OR with EXPR_T_CMP subexpressions. */
- struct expr *sub, *next = NULL;
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- ovs_list_remove(&sub->node);
- struct expr *new = crush_cmps(sub, symbol);
- switch (new->type) {
- case EXPR_T_CMP:
- if (!singleton) {
- ovs_list_insert(&next->node, &new->node);
- singleton = new;
- } else {
- bool match = !strcmp(new->cmp.string, singleton->cmp.string);
- expr_destroy(new);
- if (!match) {
- expr_destroy(expr);
- return expr_create_boolean(false);
- }
- }
- break;
- case EXPR_T_AND:
- OVS_NOT_REACHED();
- case EXPR_T_OR:
- ovs_list_insert(&next->node, &new->node);
- break;
- case EXPR_T_BOOLEAN:
- if (!new->boolean) {
- expr_destroy(expr);
- return new;
- }
- free(new);
- break;
- case EXPR_T_CONDITION:
- OVS_NOT_REACHED();
- }
- }
-
- /* If we have a singleton, then the result is either the singleton itself
- * (if the ORs allow the singleton) or false. */
- if (singleton) {
- LIST_FOR_EACH (sub, node, &expr->andor) {
- if (sub->type == EXPR_T_OR
- && !disjunction_matches_string(sub, singleton->cmp.string)) {
- expr_destroy(expr);
- return expr_create_boolean(false);
- }
- }
- ovs_list_remove(&singleton->node);
- expr_destroy(expr);
- return singleton;
- }
-
- /* Otherwise the result is the intersection of all of the ORs. */
- struct sset result = SSET_INITIALIZER(&result);
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- struct sset strings = SSET_INITIALIZER(&strings);
- const struct expr *s;
- LIST_FOR_EACH (s, node, &sub->andor) {
- sset_add(&strings, s->cmp.string);
- }
- if (sset_is_empty(&result)) {
- sset_swap(&result, &strings);
- } else {
- sset_intersect(&result, &strings);
- }
- sset_destroy(&strings);
-
- if (sset_is_empty(&result)) {
- expr_destroy(expr);
- sset_destroy(&result);
- return expr_create_boolean(false);
- }
- }
-
- expr_destroy(expr);
- expr = expr_create_andor(EXPR_T_OR);
-
- const char *string;
- SSET_FOR_EACH (string, &result) {
- sub = xmalloc(sizeof *sub);
- sub->type = EXPR_T_CMP;
- sub->cmp.relop = EXPR_R_EQ;
- sub->cmp.symbol = symbol;
- sub->cmp.string = xstrdup(string);
- ovs_list_push_back(&expr->andor, &sub->node);
- }
- sset_destroy(&result);
- return expr_fix(expr);
-}
-
-/* Implementation of crush_cmps() for expr->type == EXPR_T_AND and a
- * numeric-typed 'symbol'. */
-static struct expr *
-crush_and_numeric(struct expr *expr, const struct expr_symbol *symbol)
-{
- ovs_assert(!ovs_list_is_short(&expr->andor));
-
- union mf_subvalue value, mask;
- memset(&value, 0, sizeof value);
- memset(&mask, 0, sizeof mask);
-
- struct expr *sub, *next = NULL;
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- ovs_list_remove(&sub->node);
- struct expr *new = crush_cmps(sub, symbol);
- switch (new->type) {
- case EXPR_T_CMP:
- if (!mf_subvalue_intersect(&value, &mask,
- &new->cmp.value, &new->cmp.mask,
- &value, &mask)) {
- expr_destroy(new);
- expr_destroy(expr);
- return expr_create_boolean(false);
- }
- expr_destroy(new);
- break;
- case EXPR_T_AND:
- OVS_NOT_REACHED();
- case EXPR_T_OR:
- ovs_list_insert(&next->node, &new->node);
- break;
- case EXPR_T_BOOLEAN:
- if (!new->boolean) {
- expr_destroy(expr);
- return new;
- }
- expr_destroy(new);
- break;
- case EXPR_T_CONDITION:
- OVS_NOT_REACHED();
- }
- }
- if (ovs_list_is_empty(&expr->andor)) {
- if (is_all_zeros(&mask, sizeof mask)) {
- expr_destroy(expr);
- return expr_create_boolean(true);
- } else {
- struct expr *cmp;
- cmp = xmalloc(sizeof *cmp);
- cmp->type = EXPR_T_CMP;
- cmp->cmp.symbol = symbol;
- cmp->cmp.relop = EXPR_R_EQ;
- cmp->cmp.value = value;
- cmp->cmp.mask = mask;
- expr_destroy(expr);
- return cmp;
- }
- } else if (ovs_list_is_short(&expr->andor)) {
- /* Transform "a && (b || c || d)" into "ab || ac || ad" where "ab" is
- * computed as "a && b", etc. */
- struct expr *disjuncts = expr_from_node(ovs_list_pop_front(&expr->andor));
- struct expr *or;
-
- or = xmalloc(sizeof *or);
- or->type = EXPR_T_OR;
- ovs_list_init(&or->andor);
-
- ovs_assert(disjuncts->type == EXPR_T_OR);
- LIST_FOR_EACH_SAFE (sub, next, node, &disjuncts->andor) {
- ovs_assert(sub->type == EXPR_T_CMP);
- ovs_list_remove(&sub->node);
- if (mf_subvalue_intersect(&value, &mask,
- &sub->cmp.value, &sub->cmp.mask,
- &sub->cmp.value, &sub->cmp.mask)) {
- ovs_list_push_back(&or->andor, &sub->node);
- } else {
- expr_destroy(sub);
- }
- }
- free(disjuncts);
- free(expr);
- if (ovs_list_is_empty(&or->andor)) {
- free(or);
- return expr_create_boolean(false);
- } else if (ovs_list_is_short(&or->andor)) {
- struct expr *cmp = expr_from_node(ovs_list_pop_front(&or->andor));
- free(or);
- return cmp;
- } else {
- return or;
- }
- } else {
- /* Transform "x && (a0 || a1) && (b0 || b1) && ..." into
- * "(xa0b0 || xa0b1 || xa1b0 || xa1b1) && ...". */
- struct expr *as = expr_from_node(ovs_list_pop_front(&expr->andor));
- struct expr *bs = expr_from_node(ovs_list_pop_front(&expr->andor));
- struct expr *new = NULL;
- struct expr *or;
-
- or = xmalloc(sizeof *or);
- or->type = EXPR_T_OR;
- ovs_list_init(&or->andor);
-
- struct expr *a;
- LIST_FOR_EACH (a, node, &as->andor) {
- union mf_subvalue a_value, a_mask;
-
- ovs_assert(a->type == EXPR_T_CMP);
- if (!mf_subvalue_intersect(&value, &mask,
- &a->cmp.value, &a->cmp.mask,
- &a_value, &a_mask)) {
- continue;
- }
-
- struct expr *b;
- LIST_FOR_EACH (b, node, &bs->andor) {
- ovs_assert(b->type == EXPR_T_CMP);
- if (!new) {
- new = xmalloc(sizeof *new);
- new->type = EXPR_T_CMP;
- new->cmp.symbol = symbol;
- new->cmp.relop = EXPR_R_EQ;
- }
- if (mf_subvalue_intersect(&a_value, &a_mask,
- &b->cmp.value, &b->cmp.mask,
- &new->cmp.value, &new->cmp.mask)) {
- ovs_list_push_back(&or->andor, &new->node);
- new = NULL;
- }
- }
- }
- expr_destroy(as);
- expr_destroy(bs);
- free(new);
-
- if (ovs_list_is_empty(&or->andor)) {
- expr_destroy(expr);
- free(or);
- return expr_create_boolean(false);
- } else if (ovs_list_is_short(&or->andor)) {
- struct expr *cmp = expr_from_node(ovs_list_pop_front(&or->andor));
- free(or);
- if (ovs_list_is_empty(&expr->andor)) {
- expr_destroy(expr);
- return crush_cmps(cmp, symbol);
- } else {
- return crush_cmps(expr_combine(EXPR_T_AND, cmp, expr), symbol);
- }
- } else if (!ovs_list_is_empty(&expr->andor)) {
- struct expr *e = expr_combine(EXPR_T_AND, or, expr);
- ovs_assert(!ovs_list_is_short(&e->andor));
- return crush_cmps(e, symbol);
- } else {
- expr_destroy(expr);
- return crush_cmps(or, symbol);
- }
- }
-}
-
-static int
-compare_cmps_3way(const struct expr *a, const struct expr *b)
-{
- ovs_assert(a->cmp.symbol == b->cmp.symbol);
- if (!a->cmp.symbol->width) {
- return strcmp(a->cmp.string, b->cmp.string);
- } else {
- int d = memcmp(&a->cmp.value, &b->cmp.value, sizeof a->cmp.value);
- if (!d) {
- d = memcmp(&a->cmp.mask, &b->cmp.mask, sizeof a->cmp.mask);
- }
- return d;
- }
-}
-
-static int
-compare_cmps_cb(const void *a_, const void *b_)
-{
- const struct expr *const *ap = a_;
- const struct expr *const *bp = b_;
- const struct expr *a = *ap;
- const struct expr *b = *bp;
- return compare_cmps_3way(a, b);
-}
-
-/* Implementation of crush_cmps() for expr->type == EXPR_T_OR. */
-static struct expr *
-crush_or(struct expr *expr, const struct expr_symbol *symbol)
-{
- struct expr *sub, *next = NULL;
-
- /* First, crush all the subexpressions. That might eliminate the
- * OR-expression entirely; if so, return the result. Otherwise, 'expr'
- * is now a disjunction of cmps over the same symbol. */
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- ovs_list_remove(&sub->node);
- expr_insert_andor(expr, next, crush_cmps(sub, symbol));
- }
- expr = expr_fix(expr);
- if (expr->type != EXPR_T_OR) {
- return expr;
- }
-
- /* Sort subexpressions by value and mask, to bring together duplicates. */
- size_t n = ovs_list_size(&expr->andor);
- struct expr **subs = xmalloc(n * sizeof *subs);
-
- size_t i = 0;
- LIST_FOR_EACH (sub, node, &expr->andor) {
- subs[i++] = sub;
- }
- ovs_assert(i == n);
-
- qsort(subs, n, sizeof *subs, compare_cmps_cb);
-
- /* Eliminate duplicates. */
- ovs_list_init(&expr->andor);
- ovs_list_push_back(&expr->andor, &subs[0]->node);
- for (i = 1; i < n; i++) {
- struct expr *a = expr_from_node(ovs_list_back(&expr->andor));
- struct expr *b = subs[i];
- if (compare_cmps_3way(a, b)) {
- ovs_list_push_back(&expr->andor, &b->node);
- } else {
- expr_destroy(b);
- }
- }
- free(subs);
- return expr_fix(expr);
-}
-
-/* Takes ownership of 'expr', which must have a unique symbol in the sense of
- * 'expr_get_unique_symbol(expr)', where 'symbol' is the symbol returned by
- * that function. Returns an equivalent expression owned by the caller that is
- * a single EXPR_T_CMP or a disjunction of them or a EXPR_T_BOOLEAN. */
-static struct expr *
-crush_cmps(struct expr *expr, const struct expr_symbol *symbol)
-{
- switch (expr->type) {
- case EXPR_T_OR:
- return crush_or(expr, symbol);
-
- case EXPR_T_AND:
- return (symbol->width
- ? crush_and_numeric(expr, symbol)
- : crush_and_string(expr, symbol));
-
- case EXPR_T_CMP:
- return expr;
-
- case EXPR_T_BOOLEAN:
- return expr;
-
- /* Should not hit expression type condition, since crush_cmps is only
- * called during expr_normalize, after expr_simplify which resolves
- * all conditions. */
- case EXPR_T_CONDITION:
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Applied to an EXPR_T_AND 'expr' whose subexpressions are in terms of only
- * EXPR_T_CMP, EXPR_T_AND, and EXPR_T_OR, this takes ownership of 'expr' and
- * returns a new expression in terms of EXPR_T_CMP, EXPR_T_AND, EXPR_T_OR, or
- * EXPR_T_BOOLEAN.
- *
- * The function attempts to bring together and combine clauses of the original
- * 'expr' that were in terms of a single variable. For example, it combines
- * (x[0] == 1 && x[1] == 1) into the single x[0..1] == 3. */
-static struct expr *
-expr_sort(struct expr *expr)
-{
- ovs_assert(expr->type == EXPR_T_AND);
-
- size_t n = ovs_list_size(&expr->andor);
- struct expr_sort *subs = xmalloc(n * sizeof *subs);
- struct expr *sub;
- size_t i;
-
- i = 0;
- LIST_FOR_EACH (sub, node, &expr->andor) {
- subs[i].expr = sub;
- subs[i].symbol = expr_get_unique_symbol(sub);
- subs[i].type = subs[i].symbol ? EXPR_T_CMP : sub->type;
- i++;
- }
- ovs_assert(i == n);
-
- qsort(subs, n, sizeof *subs, compare_expr_sort);
-
- ovs_list_init(&expr->andor);
- free(expr);
- expr = NULL;
-
- for (i = 0; i < n; ) {
- if (subs[i].symbol) {
- size_t j;
- for (j = i + 1; j < n; j++) {
- if (subs[i].symbol != subs[j].symbol) {
- break;
- }
- }
-
- struct expr *crushed;
- if (j == i + 1) {
- crushed = crush_cmps(subs[i].expr, subs[i].symbol);
- } else {
- struct expr *combined = subs[i].expr;
- for (size_t k = i + 1; k < j; k++) {
- combined = expr_combine(EXPR_T_AND, combined,
- subs[k].expr);
- }
- ovs_assert(!ovs_list_is_short(&combined->andor));
- crushed = crush_cmps(combined, subs[i].symbol);
- }
- if (crushed->type == EXPR_T_BOOLEAN) {
- if (!crushed->boolean) {
- for (size_t k = j; k < n; k++) {
- expr_destroy(subs[k].expr);
- }
- expr_destroy(expr);
- expr = crushed;
- break;
- } else {
- free(crushed);
- }
- } else {
- expr = expr_combine(EXPR_T_AND, expr, crushed);
- }
- i = j;
- } else {
- expr = expr_combine(EXPR_T_AND, expr, subs[i++].expr);
- }
- }
- free(subs);
-
- return expr;
-}
-
-static struct expr *expr_normalize_or(struct expr *expr);
-
-/* Returns 'expr', which is an AND, reduced to OR(AND(clause)) where
- * a clause is a cmp or a disjunction of cmps on a single field. */
-static struct expr *
-expr_normalize_and(struct expr *expr)
-{
- expr = expr_sort(expr);
- if (expr->type != EXPR_T_AND) {
- return expr;
- }
-
- struct expr *a, *b;
- LIST_FOR_EACH_SAFE (a, b, node, &expr->andor) {
- if (&b->node == &expr->andor
- || a->type != EXPR_T_CMP || b->type != EXPR_T_CMP
- || a->cmp.symbol != b->cmp.symbol) {
- continue;
- } else if (a->cmp.symbol->width
- ? mf_subvalue_intersect(&a->cmp.value, &a->cmp.mask,
- &b->cmp.value, &b->cmp.mask,
- &b->cmp.value, &b->cmp.mask)
- : !strcmp(a->cmp.string, b->cmp.string)) {
- ovs_list_remove(&a->node);
- expr_destroy(a);
- } else {
- expr_destroy(expr);
- return expr_create_boolean(false);
- }
- }
- if (ovs_list_is_short(&expr->andor)) {
- struct expr *sub = expr_from_node(ovs_list_front(&expr->andor));
- free(expr);
- return sub;
- }
-
- struct expr *sub;
- LIST_FOR_EACH (sub, node, &expr->andor) {
- if (sub->type == EXPR_T_CMP) {
- continue;
- }
-
- ovs_assert(sub->type == EXPR_T_OR);
- const struct expr_symbol *symbol = expr_get_unique_symbol(sub);
- if (!symbol || symbol->must_crossproduct) {
- struct expr *or = expr_create_andor(EXPR_T_OR);
- struct expr *k;
-
- LIST_FOR_EACH (k, node, &sub->andor) {
- struct expr *and = expr_create_andor(EXPR_T_AND);
- struct expr *m;
-
- LIST_FOR_EACH (m, node, &expr->andor) {
- struct expr *term = m == sub ? k : m;
- if (term->type == EXPR_T_AND) {
- struct expr *p;
-
- LIST_FOR_EACH (p, node, &term->andor) {
- struct expr *new = expr_clone(p);
- ovs_list_push_back(&and->andor, &new->node);
- }
- } else {
- struct expr *new = expr_clone(term);
- ovs_list_push_back(&and->andor, &new->node);
- }
- }
- ovs_list_push_back(&or->andor, &and->node);
- }
- expr_destroy(expr);
- return expr_normalize_or(or);
- }
- }
- return expr;
-}
-
-static struct expr *
-expr_normalize_or(struct expr *expr)
-{
- struct expr *sub, *next;
-
- LIST_FOR_EACH_SAFE (sub, next, node, &expr->andor) {
- if (sub->type == EXPR_T_AND) {
- ovs_list_remove(&sub->node);
-
- struct expr *new = expr_normalize_and(sub);
- if (new->type == EXPR_T_BOOLEAN) {
- if (new->boolean) {
- expr_destroy(expr);
- return new;
- }
- free(new);
- } else {
- expr_insert_andor(expr, next, new);
- }
- } else {
- ovs_assert(sub->type == EXPR_T_CMP);
- }
- }
- if (ovs_list_is_empty(&expr->andor)) {
- free(expr);
- return expr_create_boolean(false);
- }
- if (ovs_list_is_short(&expr->andor)) {
- struct expr *e = expr_from_node(ovs_list_pop_front(&expr->andor));
- free(expr);
- return e;
- }
-
- return expr;
-}
-
-/* Takes ownership of 'expr', which is either a constant "true" or "false" or
- * an expression in terms of only relationals, AND, and OR. Returns either a
- * constant "true" or "false" or 'expr' reduced to OR(AND(clause)) where a
- * clause is a cmp or a disjunction of cmps on a single field. This form is
- * significant because it is a form that can be directly converted to OpenFlow
- * flows with the Open vSwitch "conjunctive match" extension.
- *
- * 'expr' must already have been simplified, with expr_simplify(). */
-struct expr *
-expr_normalize(struct expr *expr)
-{
- switch (expr->type) {
- case EXPR_T_CMP:
- return expr;
-
- case EXPR_T_AND:
- return expr_normalize_and(expr);
-
- case EXPR_T_OR:
- return expr_normalize_or(expr);
-
- case EXPR_T_BOOLEAN:
- return expr;
-
- /* Should not hit expression type condition, since expr_normalize is
- * only called after expr_simplify, which resolves all conditions. */
- case EXPR_T_CONDITION:
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Creates, initializes, and returns a new 'struct expr_match'. If 'm' is
- * nonnull then it is copied into the new expr_match, otherwise the new
- * expr_match's 'match' member is initialized to a catch-all match for the
- * caller to refine in-place.
- *
- * If 'conj_id' is nonzero, adds one conjunction based on 'conj_id', 'clause',
- * and 'n_clauses' to the returned 'struct expr_match', otherwise the
- * expr_match will not have any conjunctions.
- *
- * The caller should use expr_match_add() to add the expr_match to a hash table
- * after it is finalized. */
-static struct expr_match *
-expr_match_new(const struct match *m, uint8_t clause, uint8_t n_clauses,
- uint32_t conj_id)
-{
- struct expr_match *match = xmalloc(sizeof *match);
- if (m) {
- match->match = *m;
- } else {
- match_init_catchall(&match->match);
- }
- if (conj_id) {
- match->conjunctions = xmalloc(sizeof *match->conjunctions);
- match->conjunctions[0].id = conj_id;
- match->conjunctions[0].clause = clause;
- match->conjunctions[0].n_clauses = n_clauses;
- match->n = 1;
- match->allocated = 1;
- } else {
- match->conjunctions = NULL;
- match->n = 0;
- match->allocated = 0;
- }
- return match;
-}
-
-/* Adds 'match' to hash table 'matches', which becomes the new owner of
- * 'match'.
- *
- * This might actually destroy 'match' because it gets merged together with
- * some existing conjunction.*/
-static void
-expr_match_add(struct hmap *matches, struct expr_match *match)
-{
- uint32_t hash = match_hash(&match->match, 0);
- struct expr_match *m;
-
- HMAP_FOR_EACH_WITH_HASH (m, hmap_node, hash, matches) {
- if (match_equal(&m->match, &match->match)) {
- if (!m->n || !match->n) {
- free(m->conjunctions);
- m->conjunctions = NULL;
- m->n = 0;
- m->allocated = 0;
- } else {
- ovs_assert(match->n == 1);
- if (m->n >= m->allocated) {
- m->conjunctions = x2nrealloc(m->conjunctions,
- &m->allocated,
- sizeof *m->conjunctions);
- }
- m->conjunctions[m->n++] = match->conjunctions[0];
- }
- free(match->conjunctions);
- free(match);
- return;
- }
- }
-
- hmap_insert(matches, &match->hmap_node, hash);
-}
-
-/* Applies EXPR_T_CMP-typed 'expr' to 'm'. This will only work properly if 'm'
- * doesn't already match on 'expr->cmp.symbol', because it replaces any
- * existing match on that symbol instead of intersecting with it.
- *
- * If 'expr' is a comparison on a string field, uses 'lookup_port' and 'aux' to
- * convert the string to a port number. In such a case, if the port can't be
- * found, returns false. In all other cases, returns true. */
-static bool
-constrain_match(const struct expr *expr,
- bool (*lookup_port)(const void *aux,
- const char *port_name,
- unsigned int *portp),
- const void *aux, struct match *m)
-{
- ovs_assert(expr->type == EXPR_T_CMP);
- if (expr->cmp.symbol->width) {
- mf_mask_subfield(expr->cmp.symbol->field, &expr->cmp.value,
- &expr->cmp.mask, m);
- } else {
- unsigned int port;
- if (!lookup_port(aux, expr->cmp.string, &port)) {
- return false;
- }
-
- struct mf_subfield sf;
- sf.field = expr->cmp.symbol->field;
- sf.ofs = 0;
- sf.n_bits = expr->cmp.symbol->field->n_bits;
-
- union mf_subvalue x;
- memset(&x, 0, sizeof x);
- x.integer = htonll(port);
-
- mf_write_subfield(&sf, &x, m);
- }
- return true;
-}
-
-static bool
-add_disjunction(const struct expr *or,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux,
- struct match *m, uint8_t clause, uint8_t n_clauses,
- uint32_t conj_id, struct hmap *matches)
-{
- struct expr *sub;
- int n = 0;
-
- ovs_assert(or->type == EXPR_T_OR);
- LIST_FOR_EACH (sub, node, &or->andor) {
- struct expr_match *match = expr_match_new(m, clause, n_clauses,
- conj_id);
- if (constrain_match(sub, lookup_port, aux, &match->match)) {
- expr_match_add(matches, match);
- n++;
- } else {
- free(match->conjunctions);
- free(match);
- }
- }
-
- /* If n == 1, then this didn't really need to be a disjunction. Oh well,
- * that shouldn't happen much. */
- return n > 0;
-}
-
-static void
-add_conjunction(const struct expr *and,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux, uint32_t *n_conjsp, struct hmap *matches)
-{
- struct match match;
- int n_clauses = 0;
- struct expr *sub;
-
- match_init_catchall(&match);
-
- ovs_assert(and->type == EXPR_T_AND);
- LIST_FOR_EACH (sub, node, &and->andor) {
- switch (sub->type) {
- case EXPR_T_CMP:
- if (!constrain_match(sub, lookup_port, aux, &match)) {
- return;
- }
- break;
- case EXPR_T_OR:
- n_clauses++;
- break;
- case EXPR_T_AND:
- case EXPR_T_BOOLEAN:
- case EXPR_T_CONDITION:
- default:
- OVS_NOT_REACHED();
- }
- }
-
- if (!n_clauses) {
- expr_match_add(matches, expr_match_new(&match, 0, 0, 0));
- } else if (n_clauses == 1) {
- LIST_FOR_EACH (sub, node, &and->andor) {
- if (sub->type == EXPR_T_OR) {
- add_disjunction(sub, lookup_port, aux, &match, 0, 0, 0,
- matches);
- }
- }
- } else {
- int clause = 0;
- (*n_conjsp)++;
- LIST_FOR_EACH (sub, node, &and->andor) {
- if (sub->type == EXPR_T_OR) {
- if (!add_disjunction(sub, lookup_port, aux, &match, clause++,
- n_clauses, *n_conjsp, matches)) {
- /* This clause can't ever match, so we might as well skip
- * adding the other clauses--the overall disjunctive flow
- * can't ever match. Ideally we would also back out all of
- * the clauses we already added, but that seems like a lot
- * of trouble for a case that might never occur in
- * practice. */
- return;
- }
- }
- }
-
- /* Add the flow that matches on conj_id. */
- match_set_conj_id(&match, *n_conjsp);
- expr_match_add(matches, expr_match_new(&match, 0, 0, 0));
- }
-}
-
-static void
-add_cmp_flow(const struct expr *cmp,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux, struct hmap *matches)
-{
- struct expr_match *m = expr_match_new(NULL, 0, 0, 0);
- if (constrain_match(cmp, lookup_port, aux, &m->match)) {
- expr_match_add(matches, m);
- } else {
- free(m);
- }
-}
-
-/* Converts 'expr', which must be in the form returned by expr_normalize(), to
- * a collection of Open vSwitch flows in 'matches', which this function
- * initializes to an hmap of "struct expr_match" structures. Returns the
- * number of conjunctive match IDs consumed by 'matches', which uses
- * conjunctive match IDs beginning with 0; the caller must offset or remap them
- * into the desired range as necessary.
- *
- * The matches inserted into 'matches' will be of three distinct kinds:
- *
- * - Ordinary flows. The caller should add these OpenFlow flows with
- * its desired actions.
- *
- * - Conjunctive flows, distinguished by 'n > 0' in the expr_match
- * structure. The caller should add these OpenFlow flows with the
- * conjunction(id, k/n) actions as specified in the 'conjunctions' array,
- * remapping the ids.
- *
- * - conj_id flows, distinguished by matching on the "conj_id" field. The
- * caller should remap the conj_id and add the OpenFlow flow with its
- * desired actions.
- *
- * 'lookup_port' must be a function to map from a port name to a port number.
- * When successful, 'lookup_port' stores the port number into '*portp' and
- * returns true; when there is no port by the given name, it returns false.
- * 'aux' is passed to 'lookup_port' as auxiliary data. Any comparisons against
- * string fields in 'expr' are translated into integers through this function.
- * A comparison against a string that is not in 'ports' acts like a Boolean
- * "false"; that is, it will always fail to match. For a simple expression,
- * this means that the overall expression always fails to match, but an
- * expression with a disjunction on the string field might still match on other
- * port names.
- *
- * (This treatment of string fields might be too simplistic in general, but it
- * seems reasonable for now when string fields are used only for ports.) */
-uint32_t
-expr_to_matches(const struct expr *expr,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux, struct hmap *matches)
-{
- uint32_t n_conjs = 0;
-
- hmap_init(matches);
- switch (expr->type) {
- case EXPR_T_CMP:
- add_cmp_flow(expr, lookup_port, aux, matches);
- break;
-
- case EXPR_T_AND:
- add_conjunction(expr, lookup_port, aux, &n_conjs, matches);
- break;
-
- case EXPR_T_OR:
- if (expr_get_unique_symbol(expr)) {
- struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &expr->andor) {
- add_cmp_flow(sub, lookup_port, aux, matches);
- }
- } else {
- struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &expr->andor) {
- if (sub->type == EXPR_T_AND) {
- add_conjunction(sub, lookup_port, aux, &n_conjs, matches);
- } else {
- add_cmp_flow(sub, lookup_port, aux, matches);
- }
- }
- }
- break;
-
- case EXPR_T_BOOLEAN:
- if (expr->boolean) {
- struct expr_match *m = expr_match_new(NULL, 0, 0, 0);
- expr_match_add(matches, m);
- } else {
- /* No match. */
- }
- break;
-
- /* Should not hit expression type condition, since expr_to_matches is
- * only called after expr_simplify, which resolves all conditions. */
- case EXPR_T_CONDITION:
- default:
- OVS_NOT_REACHED();
- }
- return n_conjs;
-}
-
-/* Destroys all of the 'struct expr_match'es in 'matches', as well as the
- * 'matches' hmap itself. */
-void
-expr_matches_destroy(struct hmap *matches)
-{
- struct expr_match *m;
-
- HMAP_FOR_EACH_POP (m, hmap_node, matches) {
- free(m->conjunctions);
- free(m);
- }
- hmap_destroy(matches);
-}
-
-/* Prints a representation of the 'struct expr_match'es in 'matches' to
- * 'stream'. */
-void
-expr_matches_print(const struct hmap *matches, FILE *stream)
-{
- if (hmap_is_empty(matches)) {
- fputs("(no flows)\n", stream);
- return;
- }
-
- const struct expr_match *m;
- HMAP_FOR_EACH (m, hmap_node, matches) {
- char *s = match_to_string(&m->match, NULL, OFP_DEFAULT_PRIORITY);
- fputs(s, stream);
- free(s);
-
- if (m->n) {
- for (int i = 0; i < m->n; i++) {
- const struct cls_conjunction *c = &m->conjunctions[i];
- fprintf(stream, "%c conjunction(%"PRIu32", %d/%d)",
- i == 0 ? ':' : ',', c->id, c->clause, c->n_clauses);
- }
- }
- putc('\n', stream);
- }
-}
-
-/* Returns true if 'expr' honors the invariants for expressions (see the large
- * comment above "struct expr" in expr.h), false otherwise. */
-bool
-expr_honors_invariants(const struct expr *expr)
-{
- const struct expr *sub;
-
- switch (expr->type) {
- case EXPR_T_CMP:
- if (expr->cmp.symbol->width) {
- for (int i = 0; i < ARRAY_SIZE(expr->cmp.value.be64); i++) {
- if (expr->cmp.value.be64[i] & ~expr->cmp.mask.be64[i]) {
- return false;
- }
- }
- }
- return true;
-
- case EXPR_T_AND:
- case EXPR_T_OR:
- if (ovs_list_is_short(&expr->andor)) {
- return false;
- }
- LIST_FOR_EACH (sub, node, &expr->andor) {
- if (sub->type == expr->type || !expr_honors_invariants(sub)) {
- return false;
- }
- }
- return true;
-
- case EXPR_T_BOOLEAN:
- case EXPR_T_CONDITION:
- return true;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static bool
-expr_is_normalized_and(const struct expr *expr)
-{
- /* XXX should also check that no symbol is repeated. */
- const struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &expr->andor) {
- if (!expr_get_unique_symbol(sub)) {
- return false;
- }
- }
- return true;
-}
-
-/* Returns true if 'expr' is in the form returned by expr_normalize(), false
- * otherwise. */
-bool
-expr_is_normalized(const struct expr *expr)
-{
- switch (expr->type) {
- case EXPR_T_CMP:
- return true;
-
- case EXPR_T_AND:
- return expr_is_normalized_and(expr);
-
- case EXPR_T_OR:
- if (!expr_get_unique_symbol(expr)) {
- const struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &expr->andor) {
- if (!expr_get_unique_symbol(sub)
- && !expr_is_normalized_and(sub)) {
- return false;
- }
- }
- }
- return true;
-
- case EXPR_T_BOOLEAN:
- return true;
-
- case EXPR_T_CONDITION:
- return false;
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static bool
-expr_evaluate_andor(const struct expr *e, const struct flow *f,
- bool short_circuit,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux)
-{
- const struct expr *sub;
-
- LIST_FOR_EACH (sub, node, &e->andor) {
- if (expr_evaluate(sub, f, lookup_port, aux) == short_circuit) {
- return short_circuit;
- }
- }
- return !short_circuit;
-}
-
-static bool
-expr_evaluate_cmp(const struct expr *e, const struct flow *f,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux)
-{
- const struct expr_symbol *s = e->cmp.symbol;
- const struct mf_field *field = s->field;
-
- int cmp;
- if (e->cmp.symbol->width) {
- int n_bytes = field->n_bytes;
- const uint8_t *cst = &e->cmp.value.u8[sizeof e->cmp.value - n_bytes];
- const uint8_t *mask = &e->cmp.mask.u8[sizeof e->cmp.mask - n_bytes];
-
- /* Get field value and mask off undesired bits. */
- union mf_value value;
- mf_get_value(field, f, &value);
- for (int i = 0; i < field->n_bytes; i++) {
- value.b[i] &= mask[i];
- }
-
- /* Compare against constant. */
- cmp = memcmp(&value, cst, n_bytes);
- } else {
- /* Get field value. */
- struct mf_subfield sf = { .field = field, .ofs = 0,
- .n_bits = field->n_bits };
- uint64_t value = mf_get_subfield(&sf, f);
-
- /* Get constant. */
- unsigned int cst;
- if (!lookup_port(aux, e->cmp.string, &cst)) {
- return false;
- }
-
- /* Compare. */
- cmp = value < cst ? -1 : value > cst;
- }
-
- return expr_relop_test(e->cmp.relop, cmp);
-}
-
-/* Evaluates 'e' against microflow 'uflow' and returns the result.
- *
- * 'lookup_port' must be a function to map from a port name to a port number
- * and 'aux' auxiliary data to pass to it; see expr_to_matches() for more
- * details.
- *
- * This isn't particularly fast. For performance-sensitive tasks, use
- * expr_to_matches() and the classifier. */
-bool
-expr_evaluate(const struct expr *e, const struct flow *uflow,
- bool (*lookup_port)(const void *aux, const char *port_name,
- unsigned int *portp),
- const void *aux)
-{
- switch (e->type) {
- case EXPR_T_CMP:
- return expr_evaluate_cmp(e, uflow, lookup_port, aux);
-
- case EXPR_T_AND:
- return expr_evaluate_andor(e, uflow, false, lookup_port, aux);
-
- case EXPR_T_OR:
- return expr_evaluate_andor(e, uflow, true, lookup_port, aux);
-
- case EXPR_T_BOOLEAN:
- return e->boolean;
-
- case EXPR_T_CONDITION:
- /* Assume tests calling expr_evaluate are not chassis specific, so
- * is_chassis_resident evaluates as true. */
- return (e->cond.not ? false : true);
-
- default:
- OVS_NOT_REACHED();
- }
-}
-
-/* Action parsing helper. */
-
-/* Checks that 'f' is 'n_bits' wide (where 'n_bits == 0' means that 'f' must be
- * a string field) and, if 'rw' is true, that 'f' is modifiable. Returns NULL
- * if 'f' is acceptable, otherwise a malloc()'d error message that the caller
- * must free(). */
-char * OVS_WARN_UNUSED_RESULT
-expr_type_check(const struct expr_field *f, int n_bits, bool rw)
-{
- if (n_bits != f->n_bits) {
- if (n_bits && f->n_bits) {
- return xasprintf("Cannot use %d-bit field %s[%d..%d] "
- "where %d-bit field is required.",
- f->n_bits, f->symbol->name,
- f->ofs, f->ofs + f->n_bits - 1,
- n_bits);
- } else if (n_bits) {
- return xasprintf("Cannot use string field %s where numeric "
- "field is required.", f->symbol->name);
- } else {
- return xasprintf("Cannot use numeric field %s where string "
- "field is required.", f->symbol->name);
- }
- }
-
- if (rw && !f->symbol->rw) {
- return xasprintf("Field %s is not modifiable.", f->symbol->name);
- }
-
- return NULL;
-}
-
-/* Returns the mf_subfield that corresponds to 'f'. */
-struct mf_subfield
-expr_resolve_field(const struct expr_field *f)
-{
- const struct expr_symbol *symbol = f->symbol;
- int ofs = f->ofs;
-
- while (symbol->parent) {
- ofs += symbol->parent_ofs;
- symbol = symbol->parent;
- }
-
- int n_bits = symbol->width ? f->n_bits : symbol->field->n_bits;
- return (struct mf_subfield) { symbol->field, ofs, n_bits };
-}
-
-static bool
-microflow_is_chassis_resident_cb(const void *c_aux OVS_UNUSED,
- const char *port_name OVS_UNUSED)
-{
- /* Assume tests calling expr_parse_microflow are not chassis specific, so
- * is_chassis_resident need not be supplied and should return true. */
- return true;
-}
-
-static struct expr *
-expr_parse_microflow__(struct lexer *lexer,
- const struct shash *symtab,
- bool (*lookup_port)(const void *aux,
- const char *port_name,
- unsigned int *portp),
- const void *aux,
- struct expr *e, struct flow *uflow)
-{
- char *error;
- e = expr_annotate(e, symtab, &error);
- if (error) {
- lexer_error(lexer, "%s", error);
- free(error);
- return NULL;
- }
-
- struct ds annotated = DS_EMPTY_INITIALIZER;
- expr_format(e, &annotated);
-
- e = expr_simplify(e, microflow_is_chassis_resident_cb, NULL);
- e = expr_normalize(e);
-
- struct match m = MATCH_CATCHALL_INITIALIZER;
-
- switch (e->type) {
- case EXPR_T_BOOLEAN:
- if (!e->boolean) {
- lexer_error(lexer, "Constraints are contradictory.");
- }
- break;
-
- case EXPR_T_OR:
- lexer_error(lexer, "Constraints are ambiguous: %s.",
- ds_cstr(&annotated));
- break;
-
- case EXPR_T_CMP:
- constrain_match(e, lookup_port, aux, &m);
- break;
-
- case EXPR_T_AND: {
- struct expr *sub;
- LIST_FOR_EACH (sub, node, &e->andor) {
- if (sub->type == EXPR_T_CMP) {
- constrain_match(sub, lookup_port, aux, &m);
- } else {
- ovs_assert(sub->type == EXPR_T_OR);
- lexer_error(lexer, "Constraints are ambiguous: %s.",
- ds_cstr(&annotated));
- break;
- }
- }
- }
- break;
-
- /* Should not hit expression type condition, since
- * expr_simplify was called above. */
- case EXPR_T_CONDITION:
- default:
- OVS_NOT_REACHED();
- }
- ds_destroy(&annotated);
-
- *uflow = m.flow;
- return e;
-}
-
-/* Parses 's' as a microflow, using symbols from 'symtab', address set
- * table from 'addr_sets', and looking up port numbers using 'lookup_port'
- * and 'aux'. On success, stores the result in 'uflow' and returns
- * NULL, otherwise zeros 'uflow' and returns an error message that the
- * caller must free().
- *
- * A "microflow" is a description of a single stream of packets, such as half a
- * TCP connection. 's' uses the syntax of an OVN logical expression to express
- * constraints that describe the microflow. For example, "ip4 && tcp.src ==
- * 80" would set uflow->dl_type to ETH_TYPE_IP, uflow->nw_proto to IPPROTO_TCP,
- * and uflow->tp_src to 80.
- *
- * Microflow expressions can be erroneous in two ways. First, they can be
- * ambiguous. For example, "tcp.src == 80" is ambiguous because it does not
- * state IPv4 or IPv6 as the Ethernet type. "ip4 && tcp.src > 1024" is also
- * ambiguous because it does not constrain bits of tcp.src to particular
- * values. Second, they can be contradictory, e.g. "ip4 && ip6". This
- * function will report both types of errors.
- *
- * This function isn't that smart, so it can yield errors for some "clever"
- * formulations of particular microflows that area accepted other ways. For
- * example, all of the following expressions are equivalent:
- * ip4 && tcp.src[1..15] == 0x28
- * ip4 && tcp.src > 79 && tcp.src < 82
- * ip4 && 80 <= tcp.src <= 81
- * ip4 && tcp.src == {80, 81}
- * but as of this writing this function only accepts the first two, rejecting
- * the last two as ambiguous. Just don't be too clever. */
-char * OVS_WARN_UNUSED_RESULT
-expr_parse_microflow(const char *s, const struct shash *symtab,
- const struct shash *addr_sets,
- const struct shash *port_groups,
- bool (*lookup_port)(const void *aux,
- const char *port_name,
- unsigned int *portp),
- const void *aux, struct flow *uflow)
-{
- struct lexer lexer;
- lexer_init(&lexer, s);
- lexer_get(&lexer);
-
- struct expr *e = expr_parse(&lexer, symtab, addr_sets, port_groups, NULL);
- lexer_force_end(&lexer);
-
- if (e) {
- e = expr_parse_microflow__(&lexer, symtab, lookup_port, aux, e, uflow);
- }
-
- char *error = lexer_steal_error(&lexer);
- lexer_destroy(&lexer);
- expr_destroy(e);
-
- if (error) {
- memset(uflow, 0, sizeof *uflow);
- }
- return error;
-}
diff --git a/ovn/lib/extend-table.c b/ovn/lib/extend-table.c
deleted file mode 100644
index ccf70ca72..000000000
--- a/ovn/lib/extend-table.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (c) 2017 DtDream Technology Co.,Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include <string.h>
-
-#include "bitmap.h"
-#include "hash.h"
-#include "lib/uuid.h"
-#include "openvswitch/vlog.h"
-#include "ovn/lib/extend-table.h"
-
-VLOG_DEFINE_THIS_MODULE(extend_table);
-
-void
-ovn_extend_table_init(struct ovn_extend_table *table)
-{
- table->table_ids = bitmap_allocate(MAX_EXT_TABLE_ID);
- bitmap_set1(table->table_ids, 0); /* table id 0 is invalid. */
- hmap_init(&table->desired);
- hmap_init(&table->existing);
-}
-
-static void
-ovn_extend_table_info_destroy(struct hmap *target)
-{
- struct ovn_extend_table_info *e, *next;
- HMAP_FOR_EACH_SAFE (e, next, hmap_node, target) {
- hmap_remove(target, &e->hmap_node);
- free(e->name);
- free(e);
- }
- hmap_destroy(target);
-}
-
-void
-ovn_extend_table_destroy(struct ovn_extend_table *table)
-{
- bitmap_free(table->table_ids);
-
- ovn_extend_table_info_destroy(&table->desired);
- ovn_extend_table_info_destroy(&table->existing);
-}
-
-/* Finds and returns a group_info in 'existing' whose key is identical
- * to 'target''s key, or NULL if there is none. */
-struct ovn_extend_table_info *
-ovn_extend_table_lookup(struct hmap *exisiting,
- const struct ovn_extend_table_info *target)
-{
- struct ovn_extend_table_info *e;
-
- HMAP_FOR_EACH_WITH_HASH (e, hmap_node, target->hmap_node.hash,
- exisiting) {
- if (e->table_id == target->table_id) {
- return e;
- }
- }
- return NULL;
-}
-
-/* Clear either desired or existing in ovn_extend_table. */
-void
-ovn_extend_table_clear(struct ovn_extend_table *table, bool existing)
-{
- struct ovn_extend_table_info *g, *next;
- struct hmap *target = existing ? &table->existing : &table->desired;
-
- HMAP_FOR_EACH_SAFE (g, next, hmap_node, target) {
- hmap_remove(target, &g->hmap_node);
- /* Don't unset bitmap for desired group_info if the group_id
- * was not freshly reserved. */
- if (existing || g->new_table_id) {
- bitmap_set0(table->table_ids, g->table_id);
- }
- free(g->name);
- free(g);
- }
-}
-
-/* Remove an entry from existing table */
-void
-ovn_extend_table_remove_existing(struct ovn_extend_table *table,
- struct ovn_extend_table_info *existing)
-{
- /* Remove 'existing' from 'groups->existing' */
- hmap_remove(&table->existing, &existing->hmap_node);
- free(existing->name);
-
- /* Dealloc group_id. */
- bitmap_set0(table->table_ids, existing->table_id);
- free(existing);
-}
-
-/* Remove entries in desired table that are created by the lflow_uuid */
-void
-ovn_extend_table_remove_desired(struct ovn_extend_table *table,
- const struct uuid *lflow_uuid)
-{
- struct ovn_extend_table_info *e, *next_e;
- HMAP_FOR_EACH_SAFE (e, next_e, hmap_node, &table->desired) {
- if (uuid_equals(&e->lflow_uuid, lflow_uuid)) {
- hmap_remove(&table->desired, &e->hmap_node);
- free(e->name);
- if (e->new_table_id) {
- bitmap_set0(table->table_ids, e->table_id);
- }
- free(e);
- }
- }
-
-}
-
-static struct ovn_extend_table_info*
-ovn_extend_info_clone(struct ovn_extend_table_info *source)
-{
- struct ovn_extend_table_info *clone = xmalloc(sizeof *clone);
- clone->name = xstrdup(source->name);
- clone->table_id = source->table_id;
- clone->new_table_id = source->new_table_id;
- clone->hmap_node.hash = source->hmap_node.hash;
- clone->lflow_uuid = source->lflow_uuid;
- return clone;
-}
-
-void
-ovn_extend_table_sync(struct ovn_extend_table *table)
-{
- struct ovn_extend_table_info *desired, *next;
-
- /* Copy the contents of desired to existing. */
- HMAP_FOR_EACH_SAFE (desired, next, hmap_node, &table->desired) {
- if (!ovn_extend_table_lookup(&table->existing, desired)) {
- desired->new_table_id = false;
- struct ovn_extend_table_info *clone =
- ovn_extend_info_clone(desired);
- hmap_insert(&table->existing, &clone->hmap_node,
- clone->hmap_node.hash);
- }
- }
-}
-
-/* Assign a new table ID for the table information from the bitmap.
- * If it already exists, return the old ID. */
-uint32_t
-ovn_extend_table_assign_id(struct ovn_extend_table *table, const char *name,
- struct uuid lflow_uuid)
-{
- uint32_t table_id = 0, hash;
- struct ovn_extend_table_info *table_info;
-
- hash = hash_string(name, 0);
-
- /* Check whether we have non installed but allocated group_id. */
- HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->desired) {
- if (!strcmp(table_info->name, name) &&
- table_info->new_table_id) {
- return table_info->table_id;
- }
- }
-
- /* Check whether we already have an installed entry for this
- * combination. */
- HMAP_FOR_EACH_WITH_HASH (table_info, hmap_node, hash, &table->existing) {
- if (!strcmp(table_info->name, name)) {
- table_id = table_info->table_id;
- }
- }
-
- bool new_table_id = false;
- if (!table_id) {
- /* Reserve a new group_id. */
- table_id = bitmap_scan(table->table_ids, 0, 1, MAX_EXT_TABLE_ID + 1);
- new_table_id = true;
- }
-
- if (table_id == MAX_EXT_TABLE_ID + 1) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_ERR_RL(&rl, "%"PRIu32" out of table ids.", table_id);
- return EXT_TABLE_ID_INVALID;
- }
- bitmap_set1(table->table_ids, table_id);
-
- table_info = xmalloc(sizeof *table_info);
- table_info->name = xstrdup(name);
- table_info->table_id = table_id;
- table_info->hmap_node.hash = hash;
- table_info->new_table_id = new_table_id;
- table_info->lflow_uuid = lflow_uuid;
-
- hmap_insert(&table->desired,
- &table_info->hmap_node, table_info->hmap_node.hash);
-
- return table_id;
-}
diff --git a/ovn/lib/extend-table.h b/ovn/lib/extend-table.h
deleted file mode 100644
index 5be13fee1..000000000
--- a/ovn/lib/extend-table.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2017 DtDream Technology Co.,Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef EXTEND_TABLE_H
-#define EXTEND_TABLE_H 1
-
-#define MAX_EXT_TABLE_ID 65535
-#define EXT_TABLE_ID_INVALID 0
-
-#include "openvswitch/hmap.h"
-#include "openvswitch/list.h"
-#include "openvswitch/uuid.h"
-
-/* Used to manage expansion tables associated with Flow table,
- * such as the Group Table or Meter Table. */
-struct ovn_extend_table {
- unsigned long *table_ids; /* Used as a bitmap with value set
- * for allocated group ids in either
- * desired or existing. */
- struct hmap desired;
- struct hmap existing;
-};
-
-struct ovn_extend_table_info {
- struct hmap_node hmap_node;
- char *name; /* Name for the table entity. */
- struct uuid lflow_uuid;
- uint32_t table_id;
- bool new_table_id; /* 'True' if 'table_id' was reserved from
- * ovn_extend_table's 'table_ids' bitmap. */
-};
-
-void ovn_extend_table_init(struct ovn_extend_table *);
-
-void ovn_extend_table_destroy(struct ovn_extend_table *);
-
-struct ovn_extend_table_info *ovn_extend_table_lookup(
- struct hmap *, const struct ovn_extend_table_info *);
-
-void ovn_extend_table_clear(struct ovn_extend_table *, bool);
-
-void ovn_extend_table_remove_existing(struct ovn_extend_table *,
- struct ovn_extend_table_info *);
-
-void ovn_extend_table_remove_desired(struct ovn_extend_table *,
- const struct uuid *lflow_uuid);
-
-/* Copy the contents of desired to existing. */
-void ovn_extend_table_sync(struct ovn_extend_table *);
-
-uint32_t ovn_extend_table_assign_id(struct ovn_extend_table *,
- const char *name,
- struct uuid lflow_uuid);
-
-/* Iterates 'DESIRED' through all of the 'ovn_extend_table_info's in
- * 'TABLE'->desired that are not in 'TABLE'->existing. (The loop body
- * presumably adds them.) */
-#define EXTEND_TABLE_FOR_EACH_UNINSTALLED(DESIRED, TABLE) \
- HMAP_FOR_EACH (DESIRED, hmap_node, &(TABLE)->desired) \
- if (!ovn_extend_table_lookup(&(TABLE)->existing, DESIRED))
-
-/* Iterates 'EXISTING' through all of the 'ovn_extend_table_info's in
- * 'TABLE'->existing that are not in 'TABLE'->desired. (The loop body
- * presumably removes them.) */
-#define EXTEND_TABLE_FOR_EACH_INSTALLED(EXISTING, NEXT, TABLE) \
- HMAP_FOR_EACH_SAFE (EXISTING, NEXT, hmap_node, &(TABLE)->existing) \
- if (!ovn_extend_table_lookup(&(TABLE)->desired, EXISTING))
-
-#endif /* ovn/lib/extend-table.h */
diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c
deleted file mode 100644
index 1ddea1a85..000000000
--- a/ovn/lib/inc-proc-eng.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (c) 2018 eBay Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include <errno.h>
-#include <getopt.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "lib/util.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/vlog.h"
-#include "inc-proc-eng.h"
-
-VLOG_DEFINE_THIS_MODULE(inc_proc_eng);
-
-static bool engine_force_recompute = false;
-static const struct engine_context *engine_context;
-
-void
-engine_set_force_recompute(bool val)
-{
- engine_force_recompute = val;
-}
-
-const struct engine_context *
-engine_get_context(void)
-{
- return engine_context;
-}
-
-void
-engine_set_context(const struct engine_context *ctx)
-{
- engine_context = ctx;
-}
-
-void
-engine_init(struct engine_node *node)
-{
- for (size_t i = 0; i < node->n_inputs; i++) {
- engine_init(node->inputs[i].node);
- }
- if (node->init) {
- node->init(node);
- }
-}
-
-void
-engine_cleanup(struct engine_node *node)
-{
- for (size_t i = 0; i < node->n_inputs; i++) {
- engine_cleanup(node->inputs[i].node);
- }
- if (node->cleanup) {
- node->cleanup(node);
- }
-}
-
-struct engine_node *
-engine_get_input(const char *input_name, struct engine_node *node)
-{
- size_t i;
- for (i = 0; i < node->n_inputs; i++) {
- if (!strcmp(node->inputs[i].node->name, input_name)) {
- return node->inputs[i].node;
- }
- }
- OVS_NOT_REACHED();
- return NULL;
-}
-
-void
-engine_add_input(struct engine_node *node, struct engine_node *input,
- bool (*change_handler)(struct engine_node *))
-{
- ovs_assert(node->n_inputs < ENGINE_MAX_INPUT);
- node->inputs[node->n_inputs].node = input;
- node->inputs[node->n_inputs].change_handler = change_handler;
- node->n_inputs ++;
-}
-
-struct ovsdb_idl_index *
-engine_ovsdb_node_get_index(struct engine_node *node, const char *name)
-{
- struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data;
- for (size_t i = 0; i < ed->n_indexes; i++) {
- if (!strcmp(ed->indexes[i].name, name)) {
- return ed->indexes[i].index;
- }
- }
- OVS_NOT_REACHED();
- return NULL;
-}
-
-void
-engine_ovsdb_node_add_index(struct engine_node *node, const char *name,
- struct ovsdb_idl_index *index)
-{
- struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data;
- ovs_assert(ed->n_indexes < ENGINE_MAX_OVSDB_INDEX);
-
- ed->indexes[ed->n_indexes].name = name;
- ed->indexes[ed->n_indexes].index = index;
- ed->n_indexes ++;
-}
-
-void
-engine_run(struct engine_node *node, uint64_t run_id)
-{
- if (node->run_id == run_id) {
- return;
- }
- node->run_id = run_id;
-
- node->changed = false;
- if (!node->n_inputs) {
- node->run(node);
- VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
- return;
- }
-
- for (size_t i = 0; i < node->n_inputs; i++) {
- engine_run(node->inputs[i].node, run_id);
- }
-
- bool need_compute = false;
- bool need_recompute = false;
-
- if (engine_force_recompute) {
- need_recompute = true;
- } else {
- for (size_t i = 0; i < node->n_inputs; i++) {
- if (node->inputs[i].node->changed) {
- need_compute = true;
- if (!node->inputs[i].change_handler) {
- need_recompute = true;
- break;
- }
- }
- }
- }
-
- if (need_recompute) {
- VLOG_DBG("node: %s, recompute (%s)", node->name,
- engine_force_recompute ? "forced" : "triggered");
- node->run(node);
- } else if (need_compute) {
- for (size_t i = 0; i < node->n_inputs; i++) {
- if (node->inputs[i].node->changed) {
- VLOG_DBG("node: %s, handle change for input %s",
- node->name, node->inputs[i].node->name);
- if (!node->inputs[i].change_handler(node)) {
- VLOG_DBG("node: %s, can't handle change for input %s, "
- "fall back to recompute",
- node->name, node->inputs[i].node->name);
- node->run(node);
- break;
- }
- }
- }
- }
-
- VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
-}
-
-bool
-engine_need_run(struct engine_node *node)
-{
- size_t i;
-
- if (!node->n_inputs) {
- node->run(node);
- VLOG_DBG("input node: %s, changed: %d", node->name, node->changed);
- return node->changed;
- }
-
- for (i = 0; i < node->n_inputs; i++) {
- if (engine_need_run(node->inputs[i].node)) {
- return true;
- }
- }
-
- return false;
-}
diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h
deleted file mode 100644
index aab899e13..000000000
--- a/ovn/lib/inc-proc-eng.h
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (c) 2018 eBay Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef INC_PROC_ENG_H
-#define INC_PROC_ENG_H 1
-
-/* The Incremental Processing Engine is a framework for incrementally
- * processing changes from different inputs. The main user is ovn-controller.
- * To compute desired states (e.g. openflow rules) based on many inputs (e.g.
- * south-bound DB tables, local OVSDB interfaces, etc.), it is straightforward
- * to recompute everything when there is any change in any inputs, but it
- * is inefficient when the size of the input data becomes large. Instead,
- * tracking the changes and update the desired states based on what's changed
- * is more efficient and scalable. However, it is not straightforward to
- * implement the change-based processing when there are a big number of
- * inputs. In addition, what makes it more complicated is that intermediate
- * results needs to be computed, which needs to be reused in different part
- * of the processing and finally generates the final desired states. It is
- * proved to be difficult and error-prone to implement this kind of complex
- * processing by ad-hoc implementation.
- *
- * This framework is to provide a generic way to solve the above problem.
- * It does not understand the processing logic, but provides a unified way
- * to describe the inputs and dependencies clearly, with interfaces for
- * users to implement the processing logic for how to handle each input
- * changes.
- *
- * The engine is composed of engine_nodes. Each engine_node is either
- * an input, an output or both (intermediate result). Each engine node
- * maintains its own data, which is persistent across interactions. Each node
- * has zero to ENGINE_MAX_INPUT inputs, which creates a DAG (directed
- * acyclic graph). For each input of each engine_node, there is a
- * change_handler to process changes of that input, and update the data
- * of the engine_node. Then the user can simply call the run() method
- * of the engine so that the processing will happen in the order according
- * to the dependencies defined and handle the changes incrementally.
- *
- * While the more fine-grained dependencies and change-handlers are
- * implemented, the more efficient the processing will be, it is not
- * realistic to implement all change-processing for all inputs (and
- * intermediate results). The engine doesn't require change-handler to be
- * implemented for every input of every node. Users can choose to implement
- * the most important change-handlers (for the changes happens most
- * frequently) for overall performance. When there is no change_handler
- * defined for a certain input on a certain engine_node, the run() method
- * of the engine_node will be called to fall-back to a full recompute
- * against all its inputs.
- */
-
-#define ENGINE_MAX_INPUT 256
-#define ENGINE_MAX_OVSDB_INDEX 256
-
-struct engine_context {
- struct ovsdb_idl_txn *ovs_idl_txn;
- struct ovsdb_idl_txn *ovnsb_idl_txn;
-};
-
-struct engine_node;
-
-struct engine_node_input {
- /* The input node. */
- struct engine_node *node;
-
- /* Change handler for changes of the input node. The changes may need to be
- * evaluated against all the other inputs. Returns:
- * - true: if change can be handled
- * - false: if change cannot be handled (indicating full recompute needed)
- */
- bool (*change_handler)(struct engine_node *node);
-};
-
-struct engine_node {
- /* A unique id to distinguish each iteration of the engine_run(). */
- uint64_t run_id;
-
- /* A unique name for each node. */
- char *name;
-
- /* Number of inputs of this node. */
- size_t n_inputs;
-
- /* Inputs of this node. */
- struct engine_node_input inputs[ENGINE_MAX_INPUT];
-
- /* Data of this node. It is vague and interpreted by the related functions.
- * The content of the data should be changed only by the change_handlers
- * and run() function of the current node. Users should ensure that the
- * data is read-only in change-handlers of the nodes that depends on this
- * node. */
- void *data;
-
- /* Whether the data changed in the last engine run. */
- bool changed;
-
- /* Method to initialize data. It may be NULL. */
- void (*init)(struct engine_node *);
-
- /* Method to clean up data. It may be NULL. */
- void (*cleanup)(struct engine_node *);
-
- /* Fully processes all inputs of this node and regenerates the data
- * of this node */
- void (*run)(struct engine_node *);
-};
-
-/* Initialize the data for the engine nodes recursively. It calls each node's
- * init() method if not NULL. It should be called before the main loop. */
-void engine_init(struct engine_node *);
-
-/* Execute the processing recursively, which should be called in the main
- * loop. */
-void engine_run(struct engine_node *, uint64_t run_id);
-
-/* Clean up the data for the engine nodes recursively. It calls each node's
- * cleanup() method if not NULL. It should be called before the program
- * terminates. */
-void engine_cleanup(struct engine_node *);
-
-/* Check if engine needs to run, i.e. any change to be processed. */
-bool
-engine_need_run(struct engine_node *);
-
-/* Get the input node with <name> for <node> */
-struct engine_node * engine_get_input(const char *input_name,
- struct engine_node *);
-
-/* Add an input (dependency) for <node>, with corresponding change_handler,
- * which can be NULL. If the change_handler is NULL, the engine will not
- * be able to process the change incrementally, and will fall back to call
- * the run method to recompute. */
-void engine_add_input(struct engine_node *node, struct engine_node *input,
- bool (*change_handler)(struct engine_node *));
-
-/* Force the engine to recompute everything if set to true. It is used
- * in circumstances when we are not sure there is change or not, or
- * when there is change but the engine couldn't be executed in that
- * iteration, and the change can't be tracked across iterations */
-void engine_set_force_recompute(bool val);
-
-const struct engine_context * engine_get_context(void);
-
-void engine_set_context(const struct engine_context *);
-
-struct ed_ovsdb_index {
- const char *name;
- struct ovsdb_idl_index *index;
-};
-
-struct ed_type_ovsdb_table {
- const void *table;
- size_t n_indexes;
- struct ed_ovsdb_index indexes[ENGINE_MAX_OVSDB_INDEX];
-};
-
-#define EN_OVSDB_GET(NODE) \
- (((struct ed_type_ovsdb_table *)NODE->data)->table)
-
-struct ovsdb_idl_index * engine_ovsdb_node_get_index(struct engine_node *,
- const char *name);
-
-void engine_ovsdb_node_add_index(struct engine_node *, const char *name,
- struct ovsdb_idl_index *);
-
-/* Macro to define an engine node. */
-#define ENGINE_NODE(NAME, NAME_STR) \
- struct engine_node en_##NAME = { \
- .name = NAME_STR, \
- .data = &ed_##NAME, \
- .init = en_##NAME##_init, \
- .run = en_##NAME##_run, \
- .cleanup = en_##NAME##_cleanup, \
- };
-
-/* Macro to define member functions of an engine node which represents
- * a table of OVSDB */
-#define ENGINE_FUNC_OVSDB(DB_NAME, TBL_NAME) \
-static void \
-en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \
-{ \
- const struct DB_NAME##rec_##TBL_NAME##_table *table = \
- EN_OVSDB_GET(node); \
- if (DB_NAME##rec_##TBL_NAME##_table_track_get_first(table)) { \
- node->changed = true; \
- return; \
- } \
- node->changed = false; \
-} \
-static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \
- = NULL; \
-static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \
- = NULL;
-
-/* Macro to define member functions of an engine node which represents
- * a table of OVN SB DB */
-#define ENGINE_FUNC_SB(TBL_NAME) \
- ENGINE_FUNC_OVSDB(sb, TBL_NAME)
-
-/* Macro to define member functions of an engine node which represents
- * a table of open_vswitch DB */
-#define ENGINE_FUNC_OVS(TBL_NAME) \
- ENGINE_FUNC_OVSDB(ovs, TBL_NAME)
-
-/* Macro to define an engine node which represents a table of OVSDB */
-#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR, IDL) \
- struct ed_type_ovsdb_table ed_##DB_NAME##_##TBL_NAME; \
- memset(&ed_##DB_NAME##_##TBL_NAME, 0, sizeof ed_##DB_NAME##_##TBL_NAME); \
- ovs_assert(IDL); \
- ed_##DB_NAME##_##TBL_NAME.table = \
- DB_NAME##rec_##TBL_NAME##_table_get(IDL); \
- ENGINE_NODE(DB_NAME##_##TBL_NAME, DB_NAME_STR"_"TBL_NAME_STR)
-
-/* Macro to define an engine node which represents a table of OVN SB DB */
-#define ENGINE_NODE_SB(TBL_NAME, TBL_NAME_STR) \
- ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR, ovnsb_idl_loop.idl);
-
-/* Macro to define an engine node which represents a table of open_vswitch
- * DB */
-#define ENGINE_NODE_OVS(TBL_NAME, TBL_NAME_STR) \
- ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR, ovs_idl_loop.idl);
-
-#endif /* ovn/lib/inc-proc-eng.h */
diff --git a/ovn/lib/ip-mcast-index.c b/ovn/lib/ip-mcast-index.c
deleted file mode 100644
index 1f6ebc4ae..000000000
--- a/ovn/lib/ip-mcast-index.c
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright (c) 2019, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "ovn/lib/ip-mcast-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-struct ovsdb_idl_index *
-ip_mcast_index_create(struct ovsdb_idl *idl)
-{
- return ovsdb_idl_index_create1(idl, &sbrec_ip_multicast_col_datapath);
-}
-
-const struct sbrec_ip_multicast *
-ip_mcast_lookup(struct ovsdb_idl_index *ip_mcast_index,
- const struct sbrec_datapath_binding *datapath)
-{
- struct sbrec_ip_multicast *target =
- sbrec_ip_multicast_index_init_row(ip_mcast_index);
- sbrec_ip_multicast_index_set_datapath(target, datapath);
-
- struct sbrec_ip_multicast *ip_mcast =
- sbrec_ip_multicast_index_find(ip_mcast_index, target);
- sbrec_ip_multicast_index_destroy_row(target);
-
- return ip_mcast;
-}
diff --git a/ovn/lib/ip-mcast-index.h b/ovn/lib/ip-mcast-index.h
deleted file mode 100644
index a23b4a7e6..000000000
--- a/ovn/lib/ip-mcast-index.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (c) 2019, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_IP_MCAST_INDEX_H
-#define OVN_IP_MCAST_INDEX_H 1
-
-struct ovsdb_idl;
-
-struct sbrec_datapath_binding;
-
-#define OVN_MCAST_MIN_IDLE_TIMEOUT_S 15
-#define OVN_MCAST_MAX_IDLE_TIMEOUT_S 3600
-#define OVN_MCAST_DEFAULT_IDLE_TIMEOUT_S 300
-#define OVN_MCAST_MIN_QUERY_INTERVAL_S 1
-#define OVN_MCAST_MAX_QUERY_INTERVAL_S OVN_MCAST_MAX_IDLE_TIMEOUT_S
-#define OVN_MCAST_DEFAULT_QUERY_MAX_RESPONSE_S 1
-#define OVN_MCAST_DEFAULT_MAX_ENTRIES 2048
-
-struct ovsdb_idl_index *ip_mcast_index_create(struct ovsdb_idl *);
-const struct sbrec_ip_multicast *ip_mcast_lookup(
- struct ovsdb_idl_index *ip_mcast_index,
- const struct sbrec_datapath_binding *datapath);
-
-#endif /* ovn/lib/ip-mcast-index.h */
diff --git a/ovn/lib/lex.c b/ovn/lib/lex.c
deleted file mode 100644
index 7a2ab4111..000000000
--- a/ovn/lib/lex.c
+++ /dev/null
@@ -1,1023 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include <ctype.h>
-#include <errno.h>
-#include <stdarg.h>
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/json.h"
-#include "ovn/lex.h"
-#include "packets.h"
-#include "util.h"
-
-/* Returns a string that represents 'format'. */
-const char *
-lex_format_to_string(enum lex_format format)
-{
- switch (format) {
- case LEX_F_DECIMAL:
- return "decimal";
- case LEX_F_HEXADECIMAL:
- return "hexadecimal";
- case LEX_F_IPV4:
- return "IPv4";
- case LEX_F_IPV6:
- return "IPv6";
- case LEX_F_ETHERNET:
- return "Ethernet";
- default:
- abort();
- }
-}
-
-/* Initializes 'token'. */
-void
-lex_token_init(struct lex_token *token)
-{
- token->type = LEX_T_END;
- token->s = NULL;
-}
-
-/* Frees memory owned by 'token'. */
-void
-lex_token_destroy(struct lex_token *token)
-{
- if (token->s != token->buffer) {
- free(token->s);
- }
- token->s = NULL;
-}
-
-/* Exchanges 'a' and 'b'. */
-void
-lex_token_swap(struct lex_token *a, struct lex_token *b)
-{
- struct lex_token tmp = *a;
- *a = *b;
- *b = tmp;
-
- /* Before swap, if 's' was pointed to 'buffer', its value shall be changed
- * to point to the 'buffer' with the copied value. */
- if (a->s == b->buffer) {
- a->s = a->buffer;
- }
- if (b->s == a->buffer) {
- b->s = b->buffer;
- }
-}
-
-/* The string 's' need not be null-terminated at 'length'. */
-void
-lex_token_strcpy(struct lex_token *token, const char *s, size_t length)
-{
- lex_token_destroy(token);
- token->s = (length + 1 <= sizeof token->buffer
- ? token->buffer
- : xmalloc(length + 1));
- memcpy(token->s, s, length);
- token->s[length] = '\0';
-}
-
-void
-lex_token_strset(struct lex_token *token, char *s)
-{
- lex_token_destroy(token);
- token->s = s;
-}
-
-void
-lex_token_vsprintf(struct lex_token *token, const char *format, va_list args)
-{
- lex_token_destroy(token);
-
- va_list args2;
- va_copy(args2, args);
- token->s = (vsnprintf(token->buffer, sizeof token->buffer, format, args)
- < sizeof token->buffer
- ? token->buffer
- : xvasprintf(format, args2));
- va_end(args2);
-}
-
-/* lex_token_format(). */
-
-static size_t
-lex_token_n_zeros(enum lex_format format)
-{
- switch (format) {
- case LEX_F_DECIMAL: return offsetof(union mf_subvalue, integer);
- case LEX_F_HEXADECIMAL: return 0;
- case LEX_F_IPV4: return offsetof(union mf_subvalue, ipv4);
- case LEX_F_IPV6: return offsetof(union mf_subvalue, ipv6);
- case LEX_F_ETHERNET: return offsetof(union mf_subvalue, mac);
- default: OVS_NOT_REACHED();
- }
-}
-
-/* Returns the effective format for 'token', that is, the format in which it
- * should actually be printed. This is ordinarily the same as 'token->format',
- * but it's always possible that someone sets up a token with a format that
- * won't work for a value, e.g. 'token->value' is wider than 32 bits but the
- * format is LEX_F_IPV4. (The lexer itself won't do that; this is an attempt
- * to avoid confusion in the future.) */
-static enum lex_format
-lex_token_get_format(const struct lex_token *token)
-{
- size_t n_zeros = lex_token_n_zeros(token->format);
- return (is_all_zeros(&token->value, n_zeros)
- && (token->type != LEX_T_MASKED_INTEGER
- || is_all_zeros(&token->mask, n_zeros))
- ? token->format
- : LEX_F_HEXADECIMAL);
-}
-
-static void
-lex_token_format_value(const union mf_subvalue *value,
- enum lex_format format, struct ds *s)
-{
- switch (format) {
- case LEX_F_DECIMAL:
- ds_put_format(s, "%"PRIu64, ntohll(value->integer));
- break;
-
- case LEX_F_HEXADECIMAL:
- mf_format_subvalue(value, s);
- break;
-
- case LEX_F_IPV4:
- ds_put_format(s, IP_FMT, IP_ARGS(value->ipv4));
- break;
-
- case LEX_F_IPV6:
- ipv6_format_addr(&value->ipv6, s);
- break;
-
- case LEX_F_ETHERNET:
- ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(value->mac));
- break;
-
- default:
- OVS_NOT_REACHED();
- }
-
-}
-
-static void
-lex_token_format_masked_integer(const struct lex_token *token, struct ds *s)
-{
- enum lex_format format = lex_token_get_format(token);
-
- lex_token_format_value(&token->value, format, s);
- ds_put_char(s, '/');
-
- const union mf_subvalue *mask = &token->mask;
- if (format == LEX_F_IPV4 && ip_is_cidr(mask->ipv4)) {
- ds_put_format(s, "%d", ip_count_cidr_bits(mask->ipv4));
- } else if (token->format == LEX_F_IPV6 && ipv6_is_cidr(&mask->ipv6)) {
- ds_put_format(s, "%d", ipv6_count_cidr_bits(&mask->ipv6));
- } else {
- lex_token_format_value(&token->mask, format, s);
- }
-}
-
-/* Appends a string representation of 'token' to 's', in a format that can be
- * losslessly parsed back by the lexer. (LEX_T_END and LEX_T_ERROR can't be
- * parsed back.) */
-void
-lex_token_format(const struct lex_token *token, struct ds *s)
-{
- switch (token->type) {
- case LEX_T_END:
- ds_put_cstr(s, "$");
- break;
-
- case LEX_T_ID:
- ds_put_cstr(s, token->s);
- break;
-
- case LEX_T_ERROR:
- ds_put_cstr(s, "error(");
- json_string_escape(token->s, s);
- ds_put_char(s, ')');
- break;
-
- case LEX_T_STRING:
- json_string_escape(token->s, s);
- break;
-
- case LEX_T_INTEGER:
- lex_token_format_value(&token->value, lex_token_get_format(token), s);
- break;
-
- case LEX_T_MASKED_INTEGER:
- lex_token_format_masked_integer(token, s);
- break;
-
- case LEX_T_MACRO:
- ds_put_format(s, "$%s", token->s);
- break;
-
- case LEX_T_PORT_GROUP:
- ds_put_format(s, "@%s", token->s);
- break;
-
- case LEX_T_LPAREN:
- ds_put_cstr(s, "(");
- break;
- case LEX_T_RPAREN:
- ds_put_cstr(s, ")");
- break;
- case LEX_T_LCURLY:
- ds_put_cstr(s, "{");
- break;
- case LEX_T_RCURLY:
- ds_put_cstr(s, "}");
- break;
- case LEX_T_LSQUARE:
- ds_put_cstr(s, "[");
- break;
- case LEX_T_RSQUARE:
- ds_put_cstr(s, "]");
- break;
- case LEX_T_EQ:
- ds_put_cstr(s, "==");
- break;
- case LEX_T_NE:
- ds_put_cstr(s, "!=");
- break;
- case LEX_T_LT:
- ds_put_cstr(s, "<");
- break;
- case LEX_T_LE:
- ds_put_cstr(s, "<=");
- break;
- case LEX_T_GT:
- ds_put_cstr(s, ">");
- break;
- case LEX_T_GE:
- ds_put_cstr(s, ">=");
- break;
- case LEX_T_LOG_NOT:
- ds_put_cstr(s, "!");
- break;
- case LEX_T_LOG_AND:
- ds_put_cstr(s, "&&");
- break;
- case LEX_T_LOG_OR:
- ds_put_cstr(s, "||");
- break;
- case LEX_T_ELLIPSIS:
- ds_put_cstr(s, "..");
- break;
- case LEX_T_COMMA:
- ds_put_cstr(s, ",");
- break;
- case LEX_T_SEMICOLON:
- ds_put_cstr(s, ";");
- break;
- case LEX_T_EQUALS:
- ds_put_cstr(s, "=");
- break;
- case LEX_T_EXCHANGE:
- ds_put_cstr(s, "<->");
- break;
- case LEX_T_DECREMENT:
- ds_put_cstr(s, "--");
- break;
- case LEX_T_COLON:
- ds_put_char(s, ':');
- break;
- default:
- OVS_NOT_REACHED();
- }
-
-}
-
-/* lex_token_parse(). */
-
-static void OVS_PRINTF_FORMAT(2, 3)
-lex_error(struct lex_token *token, const char *message, ...)
-{
- ovs_assert(!token->s);
- token->type = LEX_T_ERROR;
-
- va_list args;
- va_start(args, message);
- lex_token_vsprintf(token, message, args);
- va_end(args);
-}
-
-static void
-lex_parse_hex_integer(const char *start, size_t len, struct lex_token *token)
-{
- const char *in = start + (len - 1);
- uint8_t *out = token->value.u8 + (sizeof token->value.u8 - 1);
-
- for (int i = 0; i < len; i++) {
- int hexit = hexit_value(in[-i]);
- if (hexit < 0) {
- lex_error(token, "Invalid syntax in hexadecimal constant.");
- return;
- } else if (hexit) {
- /* Check within loop to ignore any number of leading zeros. */
- if (i / 2 >= sizeof token->value.u8) {
- lex_error(token, "Hexadecimal constant requires more than "
- "%"PRIuSIZE" bits.", 8 * sizeof token->value.u8);
- return;
- }
- out[-(i / 2)] |= i % 2 ? hexit << 4 : hexit;
- }
- }
- token->format = LEX_F_HEXADECIMAL;
-}
-
-static const char *
-lex_parse_integer__(const char *p, struct lex_token *token)
-{
- lex_token_init(token);
- token->type = LEX_T_INTEGER;
- memset(&token->value, 0, sizeof token->value);
-
- /* Find the extent of an "integer" token, which can be in decimal or
- * hexadecimal, or an Ethernet address or IPv4 or IPv6 address, as 'start'
- * through 'end'.
- *
- * Special cases we handle here are:
- *
- * - The ellipsis token "..", used as e.g. 123..456. A doubled dot
- * is never valid syntax as part of an "integer", so we stop if
- * we encounter two dots in a row.
- *
- * - Syntax like 1.2.3.4:1234 to indicate an IPv4 address followed by a
- * port number should be considered three tokens: 1.2.3.4 : 1234.
- * The obvious approach is to allow just dots or just colons within a
- * given integer, but that would disallow IPv4-mapped IPv6 addresses,
- * e.g. ::ffff:192.0.2.128. However, even in those addresses, a
- * colon never follows a dot, so we stop if we encounter a colon
- * after a dot.
- *
- * (There is no corresponding way to parse an IPv6 address followed
- * by a port number: ::1:2:3:4:1234 is unavoidably ambiguous.)
- */
- const char *start = p;
- const char *end = start;
- bool saw_dot = false;
- while (isalnum((unsigned char) *end)
- || (*end == ':' && !saw_dot)
- || (*end == '.' && end[1] != '.')) {
- if (*end == '.') {
- saw_dot = true;
- }
- end++;
- }
- size_t len = end - start;
-
- int n;
- struct eth_addr mac;
-
- if (!len) {
- lex_error(token, "Integer constant expected.");
- } else if (len == 17
- && ovs_scan(start, ETH_ADDR_SCAN_FMT"%n",
- ETH_ADDR_SCAN_ARGS(mac), &n)
- && n == len) {
- token->value.mac = mac;
- token->format = LEX_F_ETHERNET;
- } else if (start + strspn(start, "0123456789") == end) {
- if (p[0] == '0' && len > 1) {
- lex_error(token, "Decimal constants must not have leading zeros.");
- } else {
- unsigned long long int integer;
- char *tail;
-
- errno = 0;
- integer = strtoull(p, &tail, 10);
- if (tail != end || errno == ERANGE) {
- lex_error(token, "Decimal constants must be less than 2**64.");
- } else {
- token->value.integer = htonll(integer);
- token->format = LEX_F_DECIMAL;
- }
- }
- } else if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
- if (len > 2) {
- lex_parse_hex_integer(start + 2, len - 2, token);
- } else {
- lex_error(token, "Hex digits expected following 0%c.", p[1]);
- }
- } else if (len < INET6_ADDRSTRLEN) {
- char copy[INET6_ADDRSTRLEN];
- memcpy(copy, p, len);
- copy[len] = '\0';
-
- if (ip_parse(copy, &token->value.ipv4)) {
- token->format = LEX_F_IPV4;
- } else if (ipv6_parse(copy, &token->value.ipv6)) {
- token->format = LEX_F_IPV6;
- } else {
- lex_error(token, "Invalid numeric constant.");
- }
- } else {
- lex_error(token, "Invalid numeric constant.");
- }
-
- ovs_assert(token->type == LEX_T_INTEGER || token->type == LEX_T_ERROR);
- return end;
-}
-
-static const char *
-lex_parse_mask(const char *p, struct lex_token *token)
-{
- struct lex_token mask;
-
- /* Parse just past the '/' as a second integer. Handle errors. */
- p = lex_parse_integer__(p + 1, &mask);
- if (mask.type == LEX_T_ERROR) {
- lex_token_swap(&mask, token);
- lex_token_destroy(&mask);
- return p;
- }
- ovs_assert(mask.type == LEX_T_INTEGER);
-
- /* Now convert the value and mask into a masked integer token.
- * We have a few special cases. */
- token->type = LEX_T_MASKED_INTEGER;
- memset(&token->mask, 0, sizeof token->mask);
- uint32_t prefix_bits = ntohll(mask.value.integer);
- if (token->format == mask.format) {
- /* Same format value and mask is always OK. */
- token->mask = mask.value;
- } else if (token->format == LEX_F_IPV4
- && mask.format == LEX_F_DECIMAL
- && prefix_bits <= 32) {
- /* IPv4 address with decimal mask is a CIDR prefix. */
- token->mask.integer = htonll(ntohl(be32_prefix_mask(prefix_bits)));
- } else if (token->format == LEX_F_IPV6
- && mask.format == LEX_F_DECIMAL
- && prefix_bits <= 128) {
- /* IPv6 address with decimal mask is a CIDR prefix. */
- token->mask.ipv6 = ipv6_create_mask(prefix_bits);
- } else if (token->format == LEX_F_DECIMAL
- && mask.format == LEX_F_HEXADECIMAL
- && token->value.integer == 0) {
- /* Special case for e.g. 0/0x1234. */
- token->format = LEX_F_HEXADECIMAL;
- token->mask = mask.value;
- } else {
- lex_error(token, "Value and mask have incompatible formats.");
- return p;
- }
-
- /* Check invariant that a 1-bit in the value corresponds to a 1-bit in the
- * mask. */
- for (int i = 0; i < ARRAY_SIZE(token->mask.be32); i++) {
- ovs_be32 v = token->value.be32[i];
- ovs_be32 m = token->mask.be32[i];
-
- if (v & ~m) {
- lex_error(token, "Value contains unmasked 1-bits.");
- break;
- }
- }
-
- /* Done! */
- lex_token_destroy(&mask);
- return p;
-}
-
-static const char *
-lex_parse_integer(const char *p, struct lex_token *token)
-{
- p = lex_parse_integer__(p, token);
- if (token->type == LEX_T_INTEGER && *p == '/') {
- p = lex_parse_mask(p, token);
- }
- return p;
-}
-
-static const char *
-lex_parse_string(const char *p, struct lex_token *token)
-{
- const char *start = ++p;
- char * s = NULL;
- for (;;) {
- switch (*p) {
- case '\0':
- lex_error(token, "Input ends inside quoted string.");
- return p;
-
- case '"':
- token->type = (json_string_unescape(start, p - start, &s)
- ? LEX_T_STRING : LEX_T_ERROR);
- lex_token_strset(token, s);
- return p + 1;
-
- case '\\':
- p++;
- if (*p) {
- p++;
- }
- break;
-
- default:
- p++;
- break;
- }
- }
-}
-
-static bool
-lex_is_id1(unsigned char c)
-{
- return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
- || c == '_' || c == '.');
-}
-
-static bool
-lex_is_idn(unsigned char c)
-{
- return lex_is_id1(c) || (c >= '0' && c <= '9');
-}
-
-static const char *
-lex_parse_id(const char *p, enum lex_type type, struct lex_token *token)
-{
- const char *start = p;
-
- do {
- p++;
- } while (lex_is_idn(*p));
-
- token->type = type;
- lex_token_strcpy(token, start, p - start);
- return p;
-}
-
-static const char *
-lex_parse_addr_set(const char *p, struct lex_token *token)
-{
- p++;
- if (!lex_is_id1(*p)) {
- lex_error(token, "`$' must be followed by a valid identifier.");
- return p;
- }
-
- return lex_parse_id(p, LEX_T_MACRO, token);
-}
-
-static const char *
-lex_parse_port_group(const char *p, struct lex_token *token)
-{
- p++;
- if (!lex_is_id1(*p)) {
- lex_error(token, "`@' must be followed by a valid identifier.");
- return p;
- }
-
- return lex_parse_id(p, LEX_T_PORT_GROUP, token);
-}
-
-/* Initializes 'token' and parses the first token from the beginning of
- * null-terminated string 'p' into 'token'. Stores a pointer to the start of
- * the token (after skipping white space and comments, if any) into '*startp'.
- * Returns the character position at which to begin parsing the next token. */
-const char *
-lex_token_parse(struct lex_token *token, const char *p, const char **startp)
-{
- lex_token_init(token);
-
-next:
- *startp = p;
- switch (*p) {
- case '\0':
- token->type = LEX_T_END;
- return p;
-
- case ' ': case '\t': case '\n': case '\r': case '\v': case '\f':
- p++;
- goto next;
-
- case '/':
- p++;
- if (*p == '/') {
- do {
- p++;
- } while (*p != '\0' && *p != '\n');
- goto next;
- } else if (*p == '*') {
- p++;
- for (;;) {
- if (*p == '*' && p[1] == '/') {
- p += 2;
- goto next;
- } else if (*p == '\0' || *p == '\n') {
- lex_error(token, "`/*' without matching `*/'.");
- return p;
- } else {
- p++;
- }
- }
- goto next;
- } else {
- lex_error(token,
- "`/' is only valid as part of `//' or `/*'.");
- }
- break;
-
- case '(':
- token->type = LEX_T_LPAREN;
- p++;
- break;
-
- case ')':
- token->type = LEX_T_RPAREN;
- p++;
- break;
-
- case '{':
- token->type = LEX_T_LCURLY;
- p++;
- break;
-
- case '}':
- token->type = LEX_T_RCURLY;
- p++;
- break;
-
- case '[':
- token->type = LEX_T_LSQUARE;
- p++;
- break;
-
- case ']':
- token->type = LEX_T_RSQUARE;
- p++;
- break;
-
- case '=':
- p++;
- if (*p == '=') {
- token->type = LEX_T_EQ;
- p++;
- } else {
- token->type = LEX_T_EQUALS;
- }
- break;
-
- case '!':
- p++;
- if (*p == '=') {
- token->type = LEX_T_NE;
- p++;
- } else {
- token->type = LEX_T_LOG_NOT;
- }
- break;
-
- case '&':
- p++;
- if (*p == '&') {
- token->type = LEX_T_LOG_AND;
- p++;
- } else {
- lex_error(token, "`&' is only valid as part of `&&'.");
- }
- break;
-
- case '|':
- p++;
- if (*p == '|') {
- token->type = LEX_T_LOG_OR;
- p++;
- } else {
- lex_error(token, "`|' is only valid as part of `||'.");
- }
- break;
-
- case '<':
- p++;
- if (*p == '=') {
- token->type = LEX_T_LE;
- p++;
- } else if (*p == '-' && p[1] == '>') {
- token->type = LEX_T_EXCHANGE;
- p += 2;
- } else {
- token->type = LEX_T_LT;
- }
- break;
-
- case '>':
- p++;
- if (*p == '=') {
- token->type = LEX_T_GE;
- p++;
- } else {
- token->type = LEX_T_GT;
- }
- break;
-
- case '.':
- p++;
- if (*p == '.') {
- token->type = LEX_T_ELLIPSIS;
- p++;
- } else {
- lex_error(token, "`.' is only valid as part of `..' or a number.");
- }
- break;
-
- case ',':
- p++;
- token->type = LEX_T_COMMA;
- break;
-
- case ';':
- p++;
- token->type = LEX_T_SEMICOLON;
- break;
-
- case '-':
- p++;
- if (*p == '-') {
- token->type = LEX_T_DECREMENT;
- p++;
- } else {
- lex_error(token, "`-' is only valid as part of `--'.");
- }
- break;
-
- case '$':
- p = lex_parse_addr_set(p, token);
- break;
-
- case '@':
- p = lex_parse_port_group(p, token);
- break;
-
- case ':':
- if (p[1] != ':') {
- token->type = LEX_T_COLON;
- p++;
- break;
- }
- /* IPv6 address beginning with "::". */
- /* fall through */
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- p = lex_parse_integer(p, token);
- break;
-
- case '"':
- p = lex_parse_string(p, token);
- break;
-
- case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
- /* We need to distinguish an Ethernet address or IPv6 address from an
- * identifier. Fortunately, Ethernet addresses and IPv6 addresses that
- * are ambiguous based on the first character, always start with hex
- * digits followed by a colon, but identifiers never do. */
- p = (p[strspn(p, "0123456789abcdefABCDEF")] == ':'
- ? lex_parse_integer(p, token)
- : lex_parse_id(p, LEX_T_ID, token));
- break;
-
- default:
- if (lex_is_id1(*p)) {
- p = lex_parse_id(p, LEX_T_ID, token);
- } else {
- if (isprint((unsigned char) *p)) {
- lex_error(token, "Invalid character `%c' in input.", *p);
- } else {
- lex_error(token, "Invalid byte 0x%d in input.", *p);
- }
- p++;
- }
- break;
- }
-
- return p;
-}
-
-/* Initializes 'lexer' for parsing 'input'.
- *
- * While the lexer is in use, 'input' must remain available, but the caller
- * otherwise retains ownership of 'input'.
- *
- * The caller must call lexer_get() to obtain the first token. */
-void
-lexer_init(struct lexer *lexer, const char *input)
-{
- lexer->input = input;
- lexer->start = NULL;
- lex_token_init(&lexer->token);
- lexer->error = NULL;
-}
-
-/* Frees storage associated with 'lexer'. */
-void
-lexer_destroy(struct lexer *lexer)
-{
- lex_token_destroy(&lexer->token);
- free(lexer->error);
-}
-
-/* Obtains the next token from 'lexer' into 'lexer->token', and returns the
- * token's type. The caller may examine 'lexer->token' directly to obtain full
- * information about the token. */
-enum lex_type
-lexer_get(struct lexer *lexer)
-{
- lex_token_destroy(&lexer->token);
- lexer->input = lex_token_parse(&lexer->token, lexer->input, &lexer->start);
- return lexer->token.type;
-}
-
-/* Returns the type of the next token that will be fetched by lexer_get(),
- * without advancing 'lexer->token' to that token. */
-enum lex_type
-lexer_lookahead(const struct lexer *lexer)
-{
- struct lex_token next;
- enum lex_type type;
- const char *start;
-
- lex_token_parse(&next, lexer->input, &start);
- type = next.type;
- lex_token_destroy(&next);
- return type;
-}
-
-/* If 'lexer''s current token has the given 'type', advances 'lexer' to the
- * next token and returns true. Otherwise returns false. */
-bool
-lexer_match(struct lexer *lexer, enum lex_type type)
-{
- if (lexer->token.type == type) {
- lexer_get(lexer);
- return true;
- } else {
- return false;
- }
-}
-
-bool
-lexer_force_match(struct lexer *lexer, enum lex_type t)
-{
- if (t == LEX_T_END) {
- return lexer_force_end(lexer);
- } else if (lexer_match(lexer, t)) {
- return true;
- } else {
- struct lex_token token = { .type = t };
- struct ds s = DS_EMPTY_INITIALIZER;
- lex_token_format(&token, &s);
-
- lexer_syntax_error(lexer, "expecting `%s'", ds_cstr(&s));
-
- ds_destroy(&s);
-
- return false;
- }
-}
-
-/* If 'lexer''s current token is the identifier given in 'id', advances 'lexer'
- * to the next token and returns true. Otherwise returns false. */
-bool
-lexer_match_id(struct lexer *lexer, const char *id)
-{
- if (lexer->token.type == LEX_T_ID && !strcmp(lexer->token.s, id)) {
- lexer_get(lexer);
- return true;
- } else {
- return false;
- }
-}
-
-bool
-lexer_is_int(const struct lexer *lexer)
-{
- return (lexer->token.type == LEX_T_INTEGER
- && lexer->token.format == LEX_F_DECIMAL
- && ntohll(lexer->token.value.integer) <= INT_MAX);
-}
-
-bool
-lexer_get_int(struct lexer *lexer, int *value)
-{
- if (lexer_is_int(lexer)) {
- *value = ntohll(lexer->token.value.integer);
- lexer_get(lexer);
- return true;
- } else {
- *value = 0;
- return false;
- }
-}
-
-bool
-lexer_force_int(struct lexer *lexer, int *value)
-{
- bool ok = lexer_get_int(lexer, value);
- if (!ok) {
- lexer_syntax_error(lexer, "expecting small integer");
- }
- return ok;
-}
-
-bool
-lexer_force_end(struct lexer *lexer)
-{
- if (lexer->token.type == LEX_T_END) {
- return true;
- } else {
- lexer_syntax_error(lexer, "expecting end of input");
- return false;
- }
-}
-
-static bool
-lexer_error_handle_common(struct lexer *lexer)
-{
- if (lexer->error) {
- /* Already have an error, suppress this one since the cascade seems
- * unlikely to be useful. */
- return true;
- } else if (lexer->token.type == LEX_T_ERROR) {
- /* The lexer signaled an error. Nothing at a higher level accepts an
- * error token, so we'll inevitably end up here with some meaningless
- * parse error. Report the lexical error instead. */
- lexer->error = xstrdup(lexer->token.s);
- return true;
- } else {
- return false;
- }
-}
-
-void OVS_PRINTF_FORMAT(2, 3)
-lexer_error(struct lexer *lexer, const char *message, ...)
-{
- if (lexer_error_handle_common(lexer)) {
- return;
- }
-
- va_list args;
- va_start(args, message);
- lexer->error = xvasprintf(message, args);
- va_end(args);
-}
-
-void OVS_PRINTF_FORMAT(2, 3)
-lexer_syntax_error(struct lexer *lexer, const char *message, ...)
-{
- if (lexer_error_handle_common(lexer)) {
- return;
- }
-
- struct ds s;
-
- ds_init(&s);
- ds_put_cstr(&s, "Syntax error");
- if (lexer->token.type == LEX_T_END) {
- ds_put_cstr(&s, " at end of input");
- } else if (lexer->start) {
- ds_put_format(&s, " at `%.*s'",
- (int) (lexer->input - lexer->start),
- lexer->start);
- }
-
- if (message) {
- ds_put_char(&s, ' ');
-
- va_list args;
- va_start(args, message);
- ds_put_format_valist(&s, message, args);
- va_end(args);
- }
- ds_put_char(&s, '.');
-
- lexer->error = ds_steal_cstr(&s);
-}
-
-char *
-lexer_steal_error(struct lexer *lexer)
-{
- char *error = lexer->error;
- lexer->error = NULL;
- return error;
-}
diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c
deleted file mode 100644
index 4ad5bf481..000000000
--- a/ovn/lib/logical-fields.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/* Copyright (c) 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "openvswitch/shash.h"
-#include "ovn/expr.h"
-#include "ovn/logical-fields.h"
-#include "ovs-thread.h"
-#include "packets.h"
-
-/* Silence a warning. */
-extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS];
-
-const struct ovn_field ovn_fields[OVN_FIELD_N_IDS] = {
- {
- OVN_ICMP4_FRAG_MTU,
- "icmp4.frag_mtu",
- 2, 16,
- },
-};
-
-static struct shash ovnfield_by_name;
-
-static void
-add_subregister(const char *name,
- const char *parent_name, int parent_idx,
- int width, int idx,
- struct shash *symtab)
-{
- int lsb = width * idx;
- int msb = lsb + (width - 1);
- char *expansion = xasprintf("%s%d[%d..%d]",
- parent_name, parent_idx, lsb, msb);
- expr_symtab_add_subfield(symtab, name, NULL, expansion);
- free(expansion);
-}
-
-static void
-add_ct_bit(const char *name, int index, struct shash *symtab)
-{
- char *expansion = xasprintf("ct_state[%d]", index);
- const char *prereqs = index == CS_TRACKED_BIT ? NULL : "ct.trk";
- expr_symtab_add_subfield(symtab, name, prereqs, expansion);
- free(expansion);
-}
-
-void
-ovn_init_symtab(struct shash *symtab)
-{
- shash_init(symtab);
-
- /* Reserve a pair of registers for the logical inport and outport. A full
- * 32-bit register each is bigger than we need, but the expression code
- * doesn't yet support string fields that occupy less than a full OXM. */
- expr_symtab_add_string(symtab, "inport", MFF_LOG_INPORT, NULL);
- expr_symtab_add_string(symtab, "outport", MFF_LOG_OUTPORT, NULL);
-
- /* Logical registers:
- * 128-bit xxregs
- * 64-bit xregs
- * 32-bit regs
- *
- * The expression language doesn't handle overlapping fields properly
- * unless they're formally defined as subfields. It's a little awkward. */
- for (int xxi = 0; xxi < MFF_N_LOG_REGS / 4; xxi++) {
- char *xxname = xasprintf("xxreg%d", xxi);
- expr_symtab_add_field(symtab, xxname, MFF_XXREG0 + xxi, NULL, false);
- free(xxname);
- }
- for (int xi = 0; xi < MFF_N_LOG_REGS / 2; xi++) {
- char *xname = xasprintf("xreg%d", xi);
- int xxi = xi / 2;
- if (xxi < MFF_N_LOG_REGS / 4) {
- add_subregister(xname, "xxreg", xxi, 64, 1 - xi % 2, symtab);
- } else {
- expr_symtab_add_field(symtab, xname, MFF_XREG0 + xi, NULL, false);
- }
- free(xname);
- }
- for (int i = 0; i < MFF_N_LOG_REGS; i++) {
- char *name = xasprintf("reg%d", i);
- int xxi = i / 4;
- int xi = i / 2;
- if (xxi < MFF_N_LOG_REGS / 4) {
- add_subregister(name, "xxreg", xxi, 32, 3 - i % 4, symtab);
- } else if (xi < MFF_N_LOG_REGS / 2) {
- add_subregister(name, "xreg", xi, 32, 1 - i % 2, symtab);
- } else {
- expr_symtab_add_field(symtab, name, MFF_REG0 + i, NULL, false);
- }
- free(name);
- }
-
- /* Flags used in logical to physical transformation. */
- expr_symtab_add_field(symtab, "flags", MFF_LOG_FLAGS, NULL, false);
- char flags_str[16];
- snprintf(flags_str, sizeof flags_str, "flags[%d]", MLF_ALLOW_LOOPBACK_BIT);
- expr_symtab_add_subfield(symtab, "flags.loopback", NULL, flags_str);
- snprintf(flags_str, sizeof flags_str, "flags[%d]",
- MLF_FORCE_SNAT_FOR_DNAT_BIT);
- expr_symtab_add_subfield(symtab, "flags.force_snat_for_dnat", NULL,
- flags_str);
- snprintf(flags_str, sizeof flags_str, "flags[%d]",
- MLF_FORCE_SNAT_FOR_LB_BIT);
- expr_symtab_add_subfield(symtab, "flags.force_snat_for_lb", NULL,
- flags_str);
-
- /* Connection tracking state. */
- expr_symtab_add_field(symtab, "ct_mark", MFF_CT_MARK, NULL, false);
-
- expr_symtab_add_field(symtab, "ct_label", MFF_CT_LABEL, NULL, false);
- expr_symtab_add_subfield(symtab, "ct_label.blocked", NULL, "ct_label[0]");
-
- expr_symtab_add_field(symtab, "ct_state", MFF_CT_STATE, NULL, false);
-
-#define CS_STATE(ENUM, INDEX, NAME) \
- add_ct_bit("ct."NAME, CS_##ENUM##_BIT, symtab);
- CS_STATES
-#undef CS_STATE
-
- /* Data fields. */
- expr_symtab_add_field(symtab, "eth.src", MFF_ETH_SRC, NULL, false);
- expr_symtab_add_field(symtab, "eth.dst", MFF_ETH_DST, NULL, false);
- expr_symtab_add_field(symtab, "eth.type", MFF_ETH_TYPE, NULL, true);
- expr_symtab_add_predicate(symtab, "eth.bcast",
- "eth.dst == ff:ff:ff:ff:ff:ff");
- expr_symtab_add_subfield(symtab, "eth.mcast", NULL, "eth.dst[40]");
-
- expr_symtab_add_field(symtab, "vlan.tci", MFF_VLAN_TCI, NULL, false);
- expr_symtab_add_predicate(symtab, "vlan.present", "vlan.tci[12]");
- expr_symtab_add_subfield(symtab, "vlan.pcp", "vlan.present",
- "vlan.tci[13..15]");
- expr_symtab_add_subfield(symtab, "vlan.vid", "vlan.present",
- "vlan.tci[0..11]");
-
- expr_symtab_add_predicate(symtab, "ip4", "eth.type == 0x800");
- expr_symtab_add_predicate(symtab, "ip6", "eth.type == 0x86dd");
- expr_symtab_add_predicate(symtab, "ip", "ip4 || ip6");
- expr_symtab_add_field(symtab, "ip.proto", MFF_IP_PROTO, "ip", true);
- expr_symtab_add_field(symtab, "ip.dscp", MFF_IP_DSCP_SHIFTED, "ip", false);
- expr_symtab_add_field(symtab, "ip.ecn", MFF_IP_ECN, "ip", false);
- expr_symtab_add_field(symtab, "ip.ttl", MFF_IP_TTL, "ip", false);
-
- expr_symtab_add_field(symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false);
- expr_symtab_add_field(symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false);
- expr_symtab_add_predicate(symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe");
-
- expr_symtab_add_predicate(symtab, "icmp4", "ip4 && ip.proto == 1");
- expr_symtab_add_field(symtab, "icmp4.type", MFF_ICMPV4_TYPE, "icmp4",
- false);
- expr_symtab_add_field(symtab, "icmp4.code", MFF_ICMPV4_CODE, "icmp4",
- false);
-
- expr_symtab_add_predicate(symtab, "igmp", "ip4 && ip.proto == 2");
-
- expr_symtab_add_field(symtab, "ip6.src", MFF_IPV6_SRC, "ip6", false);
- expr_symtab_add_field(symtab, "ip6.dst", MFF_IPV6_DST, "ip6", false);
- expr_symtab_add_field(symtab, "ip6.label", MFF_IPV6_LABEL, "ip6", false);
-
- expr_symtab_add_predicate(symtab, "icmp6", "ip6 && ip.proto == 58");
- expr_symtab_add_field(symtab, "icmp6.type", MFF_ICMPV6_TYPE, "icmp6",
- true);
- expr_symtab_add_field(symtab, "icmp6.code", MFF_ICMPV6_CODE, "icmp6",
- true);
-
- expr_symtab_add_predicate(symtab, "icmp", "icmp4 || icmp6");
-
- expr_symtab_add_field(symtab, "ip.frag", MFF_IP_FRAG, "ip", false);
- expr_symtab_add_predicate(symtab, "ip.is_frag", "ip.frag[0]");
- expr_symtab_add_predicate(symtab, "ip.later_frag", "ip.frag[1]");
- expr_symtab_add_predicate(symtab, "ip.first_frag",
- "ip.is_frag && !ip.later_frag");
-
- expr_symtab_add_predicate(symtab, "arp", "eth.type == 0x806");
- expr_symtab_add_field(symtab, "arp.op", MFF_ARP_OP, "arp", false);
- expr_symtab_add_field(symtab, "arp.spa", MFF_ARP_SPA, "arp", false);
- expr_symtab_add_field(symtab, "arp.sha", MFF_ARP_SHA, "arp", false);
- expr_symtab_add_field(symtab, "arp.tpa", MFF_ARP_TPA, "arp", false);
- expr_symtab_add_field(symtab, "arp.tha", MFF_ARP_THA, "arp", false);
-
- expr_symtab_add_predicate(symtab, "nd",
- "icmp6.type == {135, 136} && icmp6.code == 0 && ip.ttl == 255");
- expr_symtab_add_predicate(symtab, "nd_ns",
- "icmp6.type == 135 && icmp6.code == 0 && ip.ttl == 255");
- expr_symtab_add_predicate(symtab, "nd_na",
- "icmp6.type == 136 && icmp6.code == 0 && ip.ttl == 255");
- expr_symtab_add_predicate(symtab, "nd_rs",
- "icmp6.type == 133 && icmp6.code == 0 && ip.ttl == 255");
- expr_symtab_add_predicate(symtab, "nd_ra",
- "icmp6.type == 134 && icmp6.code == 0 && ip.ttl == 255");
- expr_symtab_add_field(symtab, "nd.target", MFF_ND_TARGET, "nd", false);
- expr_symtab_add_field(symtab, "nd.sll", MFF_ND_SLL, "nd_ns", false);
- expr_symtab_add_field(symtab, "nd.tll", MFF_ND_TLL, "nd_na", false);
-
- expr_symtab_add_predicate(symtab, "tcp", "ip.proto == 6");
- expr_symtab_add_field(symtab, "tcp.src", MFF_TCP_SRC, "tcp", false);
- expr_symtab_add_field(symtab, "tcp.dst", MFF_TCP_DST, "tcp", false);
- expr_symtab_add_field(symtab, "tcp.flags", MFF_TCP_FLAGS, "tcp", false);
-
- expr_symtab_add_predicate(symtab, "udp", "ip.proto == 17");
- expr_symtab_add_field(symtab, "udp.src", MFF_UDP_SRC, "udp", false);
- expr_symtab_add_field(symtab, "udp.dst", MFF_UDP_DST, "udp", false);
-
- expr_symtab_add_predicate(symtab, "sctp", "ip.proto == 132");
- expr_symtab_add_field(symtab, "sctp.src", MFF_SCTP_SRC, "sctp", false);
- expr_symtab_add_field(symtab, "sctp.dst", MFF_SCTP_DST, "sctp", false);
-
- shash_init(&ovnfield_by_name);
- for (int i = 0; i < OVN_FIELD_N_IDS; i++) {
- const struct ovn_field *of = &ovn_fields[i];
- ovs_assert(of->id == i); /* Fields must be in the enum order. */
- shash_add_once(&ovnfield_by_name, of->name, of);
- }
- expr_symtab_add_ovn_field(symtab, "icmp4.frag_mtu", OVN_ICMP4_FRAG_MTU);
-}
-
-const char *
-event_to_string(enum ovn_controller_event event)
-{
- switch (event) {
- case OVN_EVENT_EMPTY_LB_BACKENDS:
- return "empty_lb_backends";
- case OVN_EVENT_MAX:
- default:
- return "";
- }
-}
-
-int
-string_to_event(const char *s)
-{
- if (!strcmp(s, "empty_lb_backends")) {
- return OVN_EVENT_EMPTY_LB_BACKENDS;
- }
- return -1;
-}
-
-const struct ovn_field *
-ovn_field_from_name(const char *name)
-{
- return shash_find_data(&ovnfield_by_name, name);
-}
-
-void
-ovn_destroy_ovnfields(void)
-{
- shash_destroy(&ovnfield_by_name);
-}
diff --git a/ovn/lib/mcast-group-index.c b/ovn/lib/mcast-group-index.c
deleted file mode 100644
index 740311e00..000000000
--- a/ovn/lib/mcast-group-index.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Copyright (c) 2019, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include "ovn/lib/mcast-group-index.h"
-#include "ovn/lib/ovn-sb-idl.h"
-
-struct ovsdb_idl_index *
-mcast_group_index_create(struct ovsdb_idl *idl)
-{
- return ovsdb_idl_index_create2(idl, &sbrec_multicast_group_col_name,
- &sbrec_multicast_group_col_datapath);
-}
-
-const struct sbrec_multicast_group *
-mcast_group_lookup(struct ovsdb_idl_index *mcgroup_index,
- const char *name,
- const struct sbrec_datapath_binding *datapath)
-{
- struct sbrec_multicast_group *target =
- sbrec_multicast_group_index_init_row(mcgroup_index);
- sbrec_multicast_group_index_set_name(target, name);
- sbrec_multicast_group_index_set_datapath(target, datapath);
-
- struct sbrec_multicast_group *mcgroup =
- sbrec_multicast_group_index_find(mcgroup_index, target);
- sbrec_multicast_group_index_destroy_row(target);
-
- return mcgroup;
-}
diff --git a/ovn/lib/mcast-group-index.h b/ovn/lib/mcast-group-index.h
deleted file mode 100644
index 859e6a72f..000000000
--- a/ovn/lib/mcast-group-index.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (c) 2019, Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_MCAST_GROUP_INDEX_H
-#define OVN_MCAST_GROUP_INDEX_H 1
-
-struct ovsdb_idl;
-
-struct sbrec_datapath_binding;
-
-#define OVN_MCAST_FLOOD_TUNNEL_KEY 65535
-#define OVN_MCAST_UNKNOWN_TUNNEL_KEY (OVN_MCAST_FLOOD_TUNNEL_KEY - 1)
-
-struct ovsdb_idl_index *mcast_group_index_create(struct ovsdb_idl *);
-const struct sbrec_multicast_group *
-mcast_group_lookup(struct ovsdb_idl_index *mcgroup_index,
- const char *name,
- const struct sbrec_datapath_binding *datapath);
-
-#endif /* ovn/lib/mcast-group-index.h */
diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h
deleted file mode 100644
index c93def450..000000000
--- a/ovn/lib/ovn-l7.h
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (c) 2016 Red Hat, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef OVN_DHCP_H
-#define OVN_DHCP_H 1
-
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <netinet/icmp6.h>
-#include "openvswitch/hmap.h"
-#include "hash.h"
-#include "ovn/logical-fields.h"
-
-/* Generic options map which is used to store dhcpv4 opts and dhcpv6 opts. */
-struct gen_opts_map {
- struct hmap_node hmap_node;
- char *name;
- char *type;
- size_t code;
-};
-
-#define DHCP_OPTION(NAME, CODE, TYPE) \
- {.name = NAME, .code = CODE, .type = TYPE}
-
-#define OFFERIP DHCP_OPTION("offerip", 0, "ipv4")
-#define DHCP_OPT_NETMASK DHCP_OPTION("netmask", 1, "ipv4")
-#define DHCP_OPT_ROUTER DHCP_OPTION("router", 3, "ipv4")
-#define DHCP_OPT_DNS_SERVER DHCP_OPTION("dns_server", 6, "ipv4")
-#define DHCP_OPT_LOG_SERVER DHCP_OPTION("log_server", 7, "ipv4")
-#define DHCP_OPT_LPR_SERVER DHCP_OPTION("lpr_server", 9, "ipv4")
-#define DHCP_OPT_DOMAIN_NAME DHCP_OPTION("domain_name", 15, "str")
-#define DHCP_OPT_SWAP_SERVER DHCP_OPTION("swap_server", 16, "ipv4")
-
-#define DHCP_OPT_POLICY_FILTER \
- DHCP_OPTION("policy_filter", 21, "ipv4")
-
-#define DHCP_OPT_ROUTER_SOLICITATION \
- DHCP_OPTION("router_solicitation", 32, "ipv4")
-
-#define DHCP_OPT_NIS_SERVER DHCP_OPTION("nis_server", 41, "ipv4")
-#define DHCP_OPT_NTP_SERVER DHCP_OPTION("ntp_server", 42, "ipv4")
-#define DHCP_OPT_SERVER_ID DHCP_OPTION("server_id", 54, "ipv4")
-#define DHCP_OPT_TFTP_SERVER DHCP_OPTION("tftp_server", 66, "ipv4")
-
-#define DHCP_OPT_CLASSLESS_STATIC_ROUTE \
- DHCP_OPTION("classless_static_route", 121, "static_routes")
-#define DHCP_OPT_MS_CLASSLESS_STATIC_ROUTE \
- DHCP_OPTION("ms_classless_static_route", 249, "static_routes")
-
-#define DHCP_OPT_IP_FORWARD_ENABLE DHCP_OPTION("ip_forward_enable", 19, "bool")
-#define DHCP_OPT_ROUTER_DISCOVERY DHCP_OPTION("router_discovery", 31, "bool")
-#define DHCP_OPT_ETHERNET_ENCAP DHCP_OPTION("ethernet_encap", 36, "bool")
-
-#define DHCP_OPT_DEFAULT_TTL DHCP_OPTION("default_ttl", 23, "uint8")
-
-#define DHCP_OPT_TCP_TTL DHCP_OPTION("tcp_ttl", 37, "uint8")
-#define DHCP_OPT_MTU DHCP_OPTION("mtu", 26, "uint16")
-#define DHCP_OPT_LEASE_TIME DHCP_OPTION("lease_time", 51, "uint32")
-#define DHCP_OPT_T1 DHCP_OPTION("T1", 58, "uint32")
-#define DHCP_OPT_T2 DHCP_OPTION("T2", 59, "uint32")
-
-#define DHCP_OPT_BOOTFILE DHCP_OPTION("bootfile_name", 67, "str")
-#define DHCP_OPT_WPAD DHCP_OPTION("wpad", 252, "str")
-#define DHCP_OPT_PATH_PREFIX DHCP_OPTION("path_prefix", 210, "str")
-#define DHCP_OPT_TFTP_SERVER_ADDRESS \
- DHCP_OPTION("tftp_server_address", 150, "ipv4")
-
-static inline uint32_t
-gen_opt_hash(char *opt_name)
-{
- return hash_string(opt_name, 0);
-}
-
-static inline uint32_t
-dhcp_opt_hash(char *opt_name)
-{
- return gen_opt_hash(opt_name);
-}
-
-static inline struct gen_opts_map *
-gen_opts_find(const struct hmap *gen_opts, char *opt_name)
-{
- struct gen_opts_map *gen_opt;
- HMAP_FOR_EACH_WITH_HASH (gen_opt, hmap_node, gen_opt_hash(opt_name),
- gen_opts) {
- if (!strcmp(gen_opt->name, opt_name)) {
- return gen_opt;
- }
- }
-
- return NULL;
-}
-
-static inline struct gen_opts_map *
-dhcp_opts_find(const struct hmap *dhcp_opts, char *opt_name)
-{
- return gen_opts_find(dhcp_opts, opt_name);
-}
-
-static inline void
-gen_opt_add(struct hmap *gen_opts, char *opt_name, size_t code, char *type)
-{
- struct gen_opts_map *gen_opt = xzalloc(sizeof *gen_opt);
- gen_opt->name = xstrdup(opt_name);
- gen_opt->code = code;
- gen_opt->type = xstrdup(type);
- hmap_insert(gen_opts, &gen_opt->hmap_node, gen_opt_hash(opt_name));
-}
-
-static inline void
-dhcp_opt_add(struct hmap *dhcp_opts, char *opt_name, size_t code, char *type)
-{
- gen_opt_add(dhcp_opts, opt_name, code, type);
-}
-
-static inline void
-gen_opts_destroy(struct hmap *gen_opts)
-{
- struct gen_opts_map *gen_opt;
- HMAP_FOR_EACH_POP (gen_opt, hmap_node, gen_opts) {
- free(gen_opt->name);
- free(gen_opt->type);
- free(gen_opt);
- }
- hmap_destroy(gen_opts);
-}
-
-static inline void
-dhcp_opts_destroy(struct hmap *dhcp_opts)
-{
- gen_opts_destroy(dhcp_opts);
-}
-
-OVS_PACKED(
-struct dhcp_opt_header {
- uint8_t code;
- uint8_t len;
-});
-
-#define DHCP_OPT_PAYLOAD(hdr) \
- (void *)((char *)hdr + sizeof(struct dhcp_opt_header))
-
-/* Used in the OpenFlow PACKET_IN userdata */
-struct dhcp_opt6_header {
- ovs_be16 opt_code;
- ovs_be16 size;
-};
-
-/* Supported DHCPv6 Message Types */
-#define DHCPV6_MSG_TYPE_SOLICIT 1
-#define DHCPV6_MSG_TYPE_ADVT 2
-#define DHCPV6_MSG_TYPE_REQUEST 3
-#define DHCPV6_MSG_TYPE_CONFIRM 4
-#define DHCPV6_MSG_TYPE_REPLY 7
-#define DHCPV6_MSG_TYPE_DECLINE 9
-#define DHCPV6_MSG_TYPE_INFO_REQ 11
-
-
-/* DHCPv6 Option codes */
-#define DHCPV6_OPT_CLIENT_ID_CODE 1
-#define DHCPV6_OPT_SERVER_ID_CODE 2
-#define DHCPV6_OPT_IA_NA_CODE 3
-#define DHCPV6_OPT_IA_ADDR_CODE 5
-#define DHCPV6_OPT_DNS_SERVER_CODE 23
-#define DHCPV6_OPT_DOMAIN_SEARCH_CODE 24
-
-#define DHCPV6_OPT_SERVER_ID \
- DHCP_OPTION("server_id", DHCPV6_OPT_SERVER_ID_CODE, "mac")
-
-#define DHCPV6_OPT_IA_ADDR \
- DHCP_OPTION("ia_addr", DHCPV6_OPT_IA_ADDR_CODE, "ipv6")
-
-#define DHCPV6_OPT_DNS_SERVER \
- DHCP_OPTION("dns_server", DHCPV6_OPT_DNS_SERVER_CODE, "ipv6")
-
-#define DHCPV6_OPT_DOMAIN_SEARCH \
- DHCP_OPTION("domain_search", DHCPV6_OPT_DOMAIN_SEARCH_CODE, "str")
-
-OVS_PACKED(
-struct dhcpv6_opt_header {
- ovs_be16 code;
- ovs_be16 len;
-});
-
-OVS_PACKED(
-struct dhcpv6_opt_server_id {
- struct dhcpv6_opt_header opt;
- ovs_be16 duid_type;
- ovs_be16 hw_type;
- struct eth_addr mac;
-});
-
-
-OVS_PACKED(
-struct dhcpv6_opt_ia_addr {
- struct dhcpv6_opt_header opt;
- struct in6_addr ipv6;
- ovs_be32 t1;
- ovs_be32 t2;
-});
-
-OVS_PACKED(
-struct dhcpv6_opt_ia_na {
- struct dhcpv6_opt_header opt;
- ovs_be32 iaid;
- ovs_be32 t1;
- ovs_be32 t2;
-});
-
-#define DHCPV6_DUID_LL 3
-#define DHCPV6_HW_TYPE_ETH 1
-
-#define DHCPV6_OPT_PAYLOAD(opt) \
- (void *)((char *)opt + sizeof(struct dhcpv6_opt_header))
-
-static inline struct gen_opts_map *
-nd_ra_opts_find(const struct hmap *nd_ra_opts, char *opt_name)
-{
- return gen_opts_find(nd_ra_opts, opt_name);
-}
-
-static inline void
-nd_ra_opt_add(struct hmap *nd_ra_opts, char *opt_name, size_t code,
- char *type)
-{
- gen_opt_add(nd_ra_opts, opt_name, code, type);
-}
-
-static inline void
-nd_ra_opts_destroy(struct hmap *nd_ra_opts)
-{
- gen_opts_destroy(nd_ra_opts);
-}
-
-
-#define ND_RA_FLAG_ADDR_MODE 0
-
-
-/* Default values of various IPv6 Neighbor Discovery protocol options and
- * flags. See RFC 4861 for more information.
- * */
-#define IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG 0x80
-#define IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG 0x40
-
-#define IPV6_ND_RA_CUR_HOP_LIMIT 255
-#define IPV6_ND_RA_LIFETIME 0xffff
-#define IPV6_ND_RA_REACHABLE_TIME 0
-#define IPV6_ND_RA_RETRANSMIT_TIMER 0
-
-#define IPV6_ND_RA_OPT_PREFIX_ON_LINK 0x80
-#define IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS 0x40
-#define IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME 0xffffffff
-#define IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME 0xffffffff
-
-static inline void
-nd_ra_opts_init(struct hmap *nd_ra_opts)
-{
- nd_ra_opt_add(nd_ra_opts, "addr_mode", ND_RA_FLAG_ADDR_MODE, "str");
- nd_ra_opt_add(nd_ra_opts, "slla", ND_OPT_SOURCE_LINKADDR, "mac");
- nd_ra_opt_add(nd_ra_opts, "prefix", ND_OPT_PREFIX_INFORMATION, "ipv6");
- nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32");
-}
-
-#define EMPTY_LB_VIP 1
-#define EMPTY_LB_PROTOCOL 2
-#define EMPTY_LB_LOAD_BALANCER 3
-
-/* Used in the OpenFlow PACKET_IN userdata */
-struct controller_event_opt_header {
- ovs_be16 opt_code;
- ovs_be16 size;
-};
-
-struct controller_event_options {
- struct hmap event_opts[OVN_EVENT_MAX];
-};
-
-static inline void
-controller_event_opt_add(struct controller_event_options *event_opts,
- enum ovn_controller_event event_type, char *opt_name,
- size_t opt_code, char *opt_type)
-{
- gen_opt_add(&event_opts->event_opts[event_type], opt_name, opt_code,
- opt_type);
-}
-
-static inline void
-controller_event_opts_init(struct controller_event_options *opts)
-{
- for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
- hmap_init(&opts->event_opts[i]);
- }
- controller_event_opt_add(opts, OVN_EVENT_EMPTY_LB_BACKENDS, "vip",
- EMPTY_LB_VIP, "str");
- controller_event_opt_add(opts, OVN_EVENT_EMPTY_LB_BACKENDS, "protocol",
- EMPTY_LB_PROTOCOL, "str");
- controller_event_opt_add(opts, OVN_EVENT_EMPTY_LB_BACKENDS,
- "load_balancer", EMPTY_LB_LOAD_BALANCER, "str");
-}
-
-static inline void
-controller_event_opts_destroy(struct controller_event_options *opts)
-{
- for (size_t i = 0; i < OVN_EVENT_MAX; i++) {
- gen_opts_destroy(&opts->event_opts[i]);
- }
-}
-
-#endif /* OVN_DHCP_H */
diff --git a/ovn/northd/.gitignore b/ovn/northd/.gitignore
deleted file mode 100644
index 97a59801b..000000000
--- a/ovn/northd/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/ovn-northd
-/ovn-northd.8
diff --git a/ovn/northd/automake.mk b/ovn/northd/automake.mk
deleted file mode 100644
index 93aebe8b1..000000000
--- a/ovn/northd/automake.mk
+++ /dev/null
@@ -1,10 +0,0 @@
-# ovn-northd
-bin_PROGRAMS += ovn/northd/ovn-northd
-ovn_northd_ovn_northd_SOURCES = ovn/northd/ovn-northd.c
-ovn_northd_ovn_northd_LDADD = \
- ovn/lib/libovn.la \
- ovsdb/libovsdb.la \
- lib/libopenvswitch.la
-man_MANS += ovn/northd/ovn-northd.8
-EXTRA_DIST += ovn/northd/ovn-northd.8.xml
-CLEANFILES += ovn/northd/ovn-northd.8
diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
deleted file mode 100644
index d2267de0e..000000000
--- a/ovn/northd/ovn-northd.8.xml
+++ /dev/null
@@ -1,2544 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manpage program="ovn-northd" section="8" title="ovn-northd">
- <h1>Name</h1>
- <p>ovn-northd -- Open Virtual Network central control daemon</p>
-
- <h1>Synopsis</h1>
- <p><code>ovn-northd</code> [<var>options</var>]</p>
-
- <h1>Description</h1>
- <p>
- <code>ovn-northd</code> is a centralized daemon responsible for
- translating the high-level OVN configuration into logical
- configuration consumable by daemons such as
- <code>ovn-controller</code>. It translates the logical network
- configuration in terms of conventional network concepts, taken
- from the OVN Northbound Database (see <code>ovn-nb</code>(5)),
- into logical datapath flows in the OVN Southbound Database (see
- <code>ovn-sb</code>(5)) below it.
- </p>
-
- <h1>Options</h1>
- <dl>
- <dt><code>--ovnnb-db=<var>database</var></code></dt>
- <dd>
- The OVSDB database containing the OVN Northbound Database. If the
- <env>OVN_NB_DB</env> environment variable is set, its value is used
- as the default. Otherwise, the default is
- <code>unix:@RUNDIR@/ovnnb_db.sock</code>.
- </dd>
- <dt><code>--ovnsb-db=<var>database</var></code></dt>
- <dd>
- The OVSDB database containing the OVN Southbound Database. If the
- <env>OVN_SB_DB</env> environment variable is set, its value is used
- as the default. Otherwise, the default is
- <code>unix:@RUNDIR@/ovnsb_db.sock</code>.
- </dd>
- </dl>
- <p>
- <var>database</var> in the above options must be an OVSDB active or
- passive connection method, as described in <code>ovsdb</code>(7).
- </p>
-
- <h2>Daemon Options</h2>
- <xi:include href="lib/daemon.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>Logging Options</h2>
- <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>PKI Options</h2>
- <p>
- PKI configuration is required in order to use SSL for the connections to
- the Northbound and Southbound databases.
- </p>
- <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>Other Options</h2>
- <xi:include href="lib/unixctl.xml"
- xmlns:xi="http://www.w3.org/2003/XInclude"/>
- <h3></h3>
- <xi:include href="lib/common.xml"
- xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h1>Runtime Management Commands</h1>
- <p>
- <code>ovs-appctl</code> can send commands to a running
- <code>ovn-northd</code> process. The currently supported commands
- are described below.
- <dl>
- <dt><code>exit</code></dt>
- <dd>
- Causes <code>ovn-northd</code> to gracefully terminate.
- </dd>
- </dl>
- </p>
-
- <h1>Active-Standby for High Availability</h1>
- <p>
- You may run <code>ovn-northd</code> more than once in an OVN deployment.
- OVN will automatically ensure that only one of them is active at a time.
- If multiple instances of <code>ovn-northd</code> are running and the
- active <code>ovn-northd</code> fails, one of the hot standby instances
- of <code>ovn-northd</code> will automatically take over.
- </p>
-
- <h1>Logical Flow Table Structure</h1>
-
- <p>
- One of the main purposes of <code>ovn-northd</code> is to populate the
- <code>Logical_Flow</code> table in the <code>OVN_Southbound</code>
- database. This section describes how <code>ovn-northd</code> does this
- for switch and router logical datapaths.
- </p>
-
- <h2>Logical Switch Datapaths</h2>
-
- <h3>Ingress Table 0: Admission Control and Ingress Port Security - L2</h3>
-
- <p>
- Ingress table 0 contains these logical flows:
- </p>
-
- <ul>
- <li>
- Priority 100 flows to drop packets with VLAN tags or multicast Ethernet
- source addresses.
- </li>
-
- <li>
- Priority 50 flows that implement ingress port security for each enabled
- logical port. For logical ports on which port security is enabled,
- these match the <code>inport</code> and the valid <code>eth.src</code>
- address(es) and advance only those packets to the next flow table. For
- logical ports on which port security is not enabled, these advance all
- packets that match the <code>inport</code>.
- </li>
- </ul>
-
- <p>
- There are no flows for disabled logical ports because the default-drop
- behavior of logical flow tables causes packets that ingress from them to
- be dropped.
- </p>
-
- <h3>Ingress Table 1: Ingress Port Security - IP</h3>
-
- <p>
- Ingress table 1 contains these logical flows:
- </p>
-
- <ul>
- <li>
- <p>
- For each element in the port security set having one or more IPv4 or
- IPv6 addresses (or both),
- </p>
-
- <ul>
- <li>
- Priority 90 flow to allow IPv4 traffic if it has IPv4 addresses
- which match the <code>inport</code>, valid <code>eth.src</code>
- and valid <code>ip4.src</code> address(es).
- </li>
-
- <li>
- Priority 90 flow to allow IPv4 DHCP discovery traffic if it has a
- valid <code>eth.src</code>. This is necessary since DHCP discovery
- messages are sent from the unspecified IPv4 address (0.0.0.0) since
- the IPv4 address has not yet been assigned.
- </li>
-
- <li>
- Priority 90 flow to allow IPv6 traffic if it has IPv6 addresses
- which match the <code>inport</code>, valid <code>eth.src</code> and
- valid <code>ip6.src</code> address(es).
- </li>
-
- <li>
- Priority 90 flow to allow IPv6 DAD (Duplicate Address Detection)
- traffic if it has a valid <code>eth.src</code>. This is is
- necessary since DAD include requires joining an multicast group and
- sending neighbor solicitations for the newly assigned address. Since
- no address is yet assigned, these are sent from the unspecified
- IPv6 address (::).
- </li>
-
- <li>
- Priority 80 flow to drop IP (both IPv4 and IPv6) traffic which
- match the <code>inport</code> and valid <code>eth.src</code>.
- </li>
- </ul>
- </li>
-
- <li>
- One priority-0 fallback flow that matches all packets and advances to
- the next table.
- </li>
- </ul>
-
- <h3>Ingress Table 2: Ingress Port Security - Neighbor discovery</h3>
-
- <p>
- Ingress table 2 contains these logical flows:
- </p>
-
- <ul>
- <li>
- <p>
- For each element in the port security set,
- </p>
-
- <ul>
- <li>
- Priority 90 flow to allow ARP traffic which match the
- <code>inport</code> and valid <code>eth.src</code> and
- <code>arp.sha</code>. If the element has one or more
- IPv4 addresses, then it also matches the valid
- <code>arp.spa</code>.
- </li>
-
- <li>
- Priority 90 flow to allow IPv6 Neighbor Solicitation and
- Advertisement traffic which match the <code>inport</code>,
- valid <code>eth.src</code> and
- <code>nd.sll</code>/<code>nd.tll</code>.
- If the element has one or more IPv6 addresses, then it also
- matches the valid <code>nd.target</code> address(es) for Neighbor
- Advertisement traffic.
- </li>
-
- <li>
- Priority 80 flow to drop ARP and IPv6 Neighbor Solicitation and
- Advertisement traffic which match the <code>inport</code> and
- valid <code>eth.src</code>.
- </li>
- </ul>
- </li>
-
- <li>
- One priority-0 fallback flow that matches all packets and advances to
- the next table.
- </li>
- </ul>
-
- <h3>Ingress Table 3: <code>from-lport</code> Pre-ACLs</h3>
-
- <p>
- This table prepares flows for possible stateful ACL processing in
- ingress table <code>ACLs</code>. It contains a priority-0 flow that
- simply moves traffic to the next table. If stateful ACLs are used in the
- logical datapath, a priority-100 flow is added that sets a hint
- (with <code>reg0[0] = 1; next;</code>) for table
- <code>Pre-stateful</code> to send IP packets to the connection tracker
- before eventually advancing to ingress table <code>ACLs</code>. If
- special ports such as route ports or localnet ports can't use ct(), a
- priority-110 flow is added to skip over stateful ACLs.
- </p>
-
- <h3>Ingress Table 4: Pre-LB</h3>
-
- <p>
- This table prepares flows for possible stateful load balancing processing
- in ingress table <code>LB</code> and <code>Stateful</code>. It contains
- a priority-0 flow that simply moves traffic to the next table. Moreover
- it contains a priority-110 flow to move IPv6 Neighbor Discovery traffic
- to the next table. If load balancing rules with virtual IP addresses
- (and ports) are configured in <code>OVN_Northbound</code> database for a
- logical switch datapath, a priority-100 flow is added for each configured
- virtual IP address <var>VIP</var>. For IPv4 <var>VIPs</var>, the match is
- <code>ip &amp;&amp; ip4.dst == <var>VIP</var></code>. For IPv6
- <var>VIPs</var>, the match is <code>ip &amp;&amp;
- ip6.dst == <var>VIP</var></code>. The flow sets an action
- <code>reg0[0] = 1; next;</code> to act as a hint for table
- <code>Pre-stateful</code> to send IP packets to the connection tracker
- for packet de-fragmentation before eventually advancing to ingress table
- <code>LB</code>.
- </p>
-
- <h3>Ingress Table 5: Pre-stateful</h3>
-
- <p>
- This table prepares flows for all possible stateful processing
- in next tables. It contains a priority-0 flow that simply moves
- traffic to the next table. A priority-100 flow sends the packets to
- connection tracker based on a hint provided by the previous tables
- (with a match for <code>reg0[0] == 1</code>) by using the
- <code>ct_next;</code> action.
- </p>
-
- <h3>Ingress table 6: <code>from-lport</code> ACLs</h3>
-
- <p>
- Logical flows in this table closely reproduce those in the
- <code>ACL</code> table in the <code>OVN_Northbound</code> database
- for the <code>from-lport</code> direction. The <code>priority</code>
- values from the <code>ACL</code> table have a limited range and have
- 1000 added to them to leave room for OVN default flows at both
- higher and lower priorities.
- </p>
- <ul>
- <li>
- <code>allow</code> ACLs translate into logical flows with
- the <code>next;</code> action. If there are any stateful ACLs
- on this datapath, then <code>allow</code> ACLs translate to
- <code>ct_commit; next;</code> (which acts as a hint for the next tables
- to commit the connection to conntrack),
- </li>
- <li>
- <code>allow-related</code> ACLs translate into logical
- flows with the <code>ct_commit(ct_label=0/1); next;</code> actions
- for new connections and <code>reg0[1] = 1; next;</code> for existing
- connections.
- </li>
- <li>
- Other ACLs translate to <code>drop;</code> for new or untracked
- connections and <code>ct_commit(ct_label=1/1);</code> for known
- connections. Setting <code>ct_label</code> marks a connection
- as one that was previously allowed, but should no longer be
- allowed due to a policy change.
- </li>
- </ul>
-
- <p>
- This table also contains a priority 0 flow with action
- <code>next;</code>, so that ACLs allow packets by default. If the
- logical datapath has a statetful ACL, the following flows will
- also be added:
- </p>
-
- <ul>
- <li>
- A priority-1 flow that sets the hint to commit IP traffic to the
- connection tracker (with action <code>reg0[1] = 1; next;</code>). This
- is needed for the default allow policy because, while the initiator's
- direction may not have any stateful rules, the server's may and then
- its return traffic would not be known and marked as invalid.
- </li>
-
- <li>
- A priority-65535 flow that allows any traffic in the reply
- direction for a connection that has been committed to the
- connection tracker (i.e., established flows), as long as
- the committed flow does not have <code>ct_label.blocked</code> set.
- We only handle traffic in the reply direction here because
- we want all packets going in the request direction to still
- go through the flows that implement the currently defined
- policy based on ACLs. If a connection is no longer allowed by
- policy, <code>ct_label.blocked</code> will get set and packets in the
- reply direction will no longer be allowed, either.
- </li>
-
- <li>
- A priority-65535 flow that allows any traffic that is considered
- related to a committed flow in the connection tracker (e.g., an
- ICMP Port Unreachable from a non-listening UDP port), as long
- as the committed flow does not have <code>ct_label.blocked</code> set.
- </li>
-
- <li>
- A priority-65535 flow that drops all traffic marked by the
- connection tracker as invalid.
- </li>
-
- <li>
- A priority-65535 flow that drops all traffic in the reply direction
- with <code>ct_label.blocked</code> set meaning that the connection
- should no longer be allowed due to a policy change. Packets
- in the request direction are skipped here to let a newly created
- ACL re-allow this connection.
- </li>
- </ul>
-
- <h3>Ingress Table 7: <code>from-lport</code> QoS Marking</h3>
-
- <p>
- Logical flows in this table closely reproduce those in the
- <code>QoS</code> table with the <code>action</code> column set in
- the <code>OVN_Northbound</code> database for the
- <code>from-lport</code> direction.
- </p>
-
- <ul>
- <li>
- For every qos_rules entry in a logical switch with DSCP marking
- enabled, a flow will be added at the priority mentioned in the
- QoS table.
- </li>
-
- <li>
- One priority-0 fallback flow that matches all packets and advances to
- the next table.
- </li>
- </ul>
-
- <h3>Ingress Table 8: <code>from-lport</code> QoS Meter</h3>
-
- <p>
- Logical flows in this table closely reproduce those in the
- <code>QoS</code> table with the <code>bandwidth</code> column set
- in the <code>OVN_Northbound</code> database for the
- <code>from-lport</code> direction.
- </p>
-
- <ul>
- <li>
- For every qos_rules entry in a logical switch with metering
- enabled, a flow will be added at the priorirty mentioned in the
- QoS table.
- </li>
-
- <li>
- One priority-0 fallback flow that matches all packets and advances to
- the next table.
- </li>
- </ul>
-
- <h3>Ingress Table 9: LB</h3>
-
- <p>
- It contains a priority-0 flow that simply moves traffic to the next
- table. For established connections a priority 100 flow matches on
- <code>ct.est &amp;&amp; !ct.rel &amp;&amp; !ct.new &amp;&amp;
- !ct.inv</code> and sets an action <code>reg0[2] = 1; next;</code> to act
- as a hint for table <code>Stateful</code> to send packets through
- connection tracker to NAT the packets. (The packet will automatically
- get DNATed to the same IP address as the first packet in that
- connection.)
- </p>
-
- <h3>Ingress Table 10: Stateful</h3>
-
- <ul>
- <li>
- For all the configured load balancing rules for a switch in
- <code>OVN_Northbound</code> database that includes a L4 port
- <var>PORT</var> of protocol <var>P</var> and IP address
- <var>VIP</var>, a priority-120 flow is added. For IPv4 <var>VIPs
- </var>, the flow matches <code>ct.new &amp;&amp; ip &amp;&amp;
- ip4.dst == <var>VIP</var> &amp;&amp; <var>P</var> &amp;&amp;
- <var>P</var>.dst == <var>PORT</var></code>. For IPv6 <var>VIPs</var>,
- the flow matches <code>ct.new &amp;&amp; ip &amp;&amp; ip6.dst == <var>
- VIP </var>&amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>
- PORT</var></code>. The flow's action is <code>ct_lb(<var>args</var>)
- </code>, where <var>args</var> contains comma separated IP addresses
- (and optional port numbers) to load balance to. The address family of
- the IP addresses of <var>args</var> is the same as the address family
- of <var>VIP</var>
- </li>
- <li>
- For all the configured load balancing rules for a switch in
- <code>OVN_Northbound</code> database that includes just an IP address
- <var>VIP</var> to match on, OVN adds a priority-110 flow. For IPv4
- <var>VIPs</var>, the flow matches <code>ct.new &amp;&amp; ip &amp;&amp;
- ip4.dst == <var>VIP</var></code>. For IPv6 <var>VIPs</var>,
- the flow matches <code>ct.new &amp;&amp; ip &amp;&amp; ip6.dst == <var>
- VIP</var></code>. The action on this flow is <code>
- ct_lb(<var>args</var>)</code>, where <var>args</var> contains comma
- separated IP addresses of the same address family as <var>VIP</var>.
- </li>
- <li>
- A priority-100 flow commits packets to connection tracker using
- <code>ct_commit; next;</code> action based on a hint provided by
- the previous tables (with a match for <code>reg0[1] == 1</code>).
- </li>
- <li>
- A priority-100 flow sends the packets to connection tracker using
- <code>ct_lb;</code> as the action based on a hint provided by the
- previous tables (with a match for <code>reg0[2] == 1</code>).
- </li>
- <li>
- A priority-0 flow that simply moves traffic to the next table.
- </li>
- </ul>
-
- <h3>Ingress Table 11: ARP/ND responder</h3>
-
- <p>
- This table implements ARP/ND responder in a logical switch for known
- IPs. The advantage of the ARP responder flow is to limit ARP
- broadcasts by locally responding to ARP requests without the need to
- send to other hypervisors. One common case is when the inport is a
- logical port associated with a VIF and the broadcast is responded to
- on the local hypervisor rather than broadcast across the whole
- network and responded to by the destination VM. This behavior is
- proxy ARP.
- </p>
-
- <p>
- ARP requests arrive from VMs from a logical switch inport of type
- default. For this case, the logical switch proxy ARP rules can be
- for other VMs or logical router ports. Logical switch proxy ARP
- rules may be programmed both for mac binding of IP addresses on
- other logical switch VIF ports (which are of the default logical
- switch port type, representing connectivity to VMs or containers),
- and for mac binding of IP addresses on logical switch router type
- ports, representing their logical router port peers. In order to
- support proxy ARP for logical router ports, an IP address must be
- configured on the logical switch router type port, with the same
- value as the peer logical router port. The configured MAC addresses
- must match as well. When a VM sends an ARP request for a distributed
- logical router port and if the peer router type port of the attached
- logical switch does not have an IP address configured, the ARP request
- will be broadcast on the logical switch. One of the copies of the ARP
- request will go through the logical switch router type port to the
- logical router datapath, where the logical router ARP responder will
- generate a reply. The MAC binding of a distributed logical router,
- once learned by an associated VM, is used for all that VM's
- communication needing routing. Hence, the action of a VM re-arping for
- the mac binding of the logical router port should be rare.
- </p>
-
- <p>
- Logical switch ARP responder proxy ARP rules can also be hit when
- receiving ARP requests externally on a L2 gateway port. In this case,
- the hypervisor acting as an L2 gateway, responds to the ARP request on
- behalf of a destination VM.
- </p>
-
- <p>
- Note that ARP requests received from <code>localnet</code> or
- <code>vtep</code> logical inports can either go directly to VMs, in
- which case the VM responds or can hit an ARP responder for a logical
- router port if the packet is used to resolve a logical router port
- next hop address. In either case, logical switch ARP responder rules
- will not be hit. It contains these logical flows:
- </p>
-
- <ul>
- <li>
- Priority-100 flows to skip the ARP responder if inport is of type
- <code>localnet</code> or <code>vtep</code> and advances directly
- to the next table. ARP requests sent to <code>localnet</code> or
- <code>vtep</code> ports can be received by multiple hypervisors.
- Now, because the same mac binding rules are downloaded to all
- hypervisors, each of the multiple hypervisors will respond. This
- will confuse L2 learning on the source of the ARP requests. ARP
- requests received on an inport of type <code>router</code> are not
- expected to hit any logical switch ARP responder flows. However,
- no skip flows are installed for these packets, as there would be
- some additional flow cost for this and the value appears limited.
- </li>
-
- <li>
- <p>
- Priority-50 flows that match ARP requests to each known IP address
- <var>A</var> of every logical switch port, and respond with ARP
- replies directly with corresponding Ethernet address <var>E</var>:
- </p>
-
- <pre>
-eth.dst = eth.src;
-eth.src = <var>E</var>;
-arp.op = 2; /* ARP reply. */
-arp.tha = arp.sha;
-arp.sha = <var>E</var>;
-arp.tpa = arp.spa;
-arp.spa = <var>A</var>;
-outport = inport;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- These flows are omitted for logical ports (other than router ports or
- <code>localport</code> ports) that are down.
- </p>
- </li>
-
- <li>
- <p>
- Priority-50 flows that match IPv6 ND neighbor solicitations to
- each known IP address <var>A</var> (and <var>A</var>'s
- solicited node address) of every logical switch port except of type
- router, and respond with neighbor advertisements directly with
- corresponding Ethernet address <var>E</var>:
- </p>
-
- <pre>
-nd_na {
- eth.src = <var>E</var>;
- ip6.src = <var>A</var>;
- nd.target = <var>A</var>;
- nd.tll = <var>E</var>;
- outport = inport;
- flags.loopback = 1;
- output;
-};
- </pre>
-
- <p>
- Priority-50 flows that match IPv6 ND neighbor solicitations to
- each known IP address <var>A</var> (and <var>A</var>'s
- solicited node address) of logical switch port of type router, and
- respond with neighbor advertisements directly with
- corresponding Ethernet address <var>E</var>:
- </p>
-
- <pre>
-nd_na_router {
- eth.src = <var>E</var>;
- ip6.src = <var>A</var>;
- nd.target = <var>A</var>;
- nd.tll = <var>E</var>;
- outport = inport;
- flags.loopback = 1;
- output;
-};
- </pre>
-
- <p>
- These flows are omitted for logical ports (other than router ports or
- <code>localport</code> ports) that are down.
- </p>
- </li>
-
- <li>
- <p>
- Priority-100 flows with match criteria like the ARP and ND flows
- above, except that they only match packets from the
- <code>inport</code> that owns the IP addresses in question, with
- action <code>next;</code>. These flows prevent OVN from replying to,
- for example, an ARP request emitted by a VM for its own IP address.
- A VM only makes this kind of request to attempt to detect a duplicate
- IP address assignment, so sending a reply will prevent the VM from
- accepting the IP address that it owns.
- </p>
-
- <p>
- In place of <code>next;</code>, it would be reasonable to use
- <code>drop;</code> for the flows' actions. If everything is working
- as it is configured, then this would produce equivalent results,
- since no host should reply to the request. But ARPing for one's own
- IP address is intended to detect situations where the network is not
- working as configured, so dropping the request would frustrate that
- intent.
- </p>
- </li>
-
- <li>
- One priority-0 fallback flow that matches all packets and advances to
- the next table.
- </li>
- </ul>
-
- <h3>Ingress Table 12: DHCP option processing</h3>
-
- <p>
- This table adds the DHCPv4 options to a DHCPv4 packet from the
- logical ports configured with IPv4 address(es) and DHCPv4 options,
- and similarly for DHCPv6 options. This table also adds flows for the
- logical ports of type <code>external</code>.
- </p>
-
- <ul>
- <li>
- <p>
- A priority-100 logical flow is added for these logical ports
- which matches the IPv4 packet with <code>udp.src</code> = 68 and
- <code>udp.dst</code> = 67 and applies the action
- <code>put_dhcp_opts</code> and advances the packet to the next table.
- </p>
-
- <pre>
-reg0[3] = put_dhcp_opts(offer_ip = <var>ip</var>, <var>options</var>...);
-next;
- </pre>
-
- <p>
- For DHCPDISCOVER and DHCPREQUEST, this transforms the packet into a
- DHCP reply, adds the DHCP offer IP <var>ip</var> and options to the
- packet, and stores 1 into reg0[3]. For other kinds of packets, it
- just stores 0 into reg0[3]. Either way, it continues to the next
- table.
- </p>
-
- </li>
-
- <li>
- <p>
- A priority-100 logical flow is added for these logical ports
- which matches the IPv6 packet with <code>udp.src</code> = 546 and
- <code>udp.dst</code> = 547 and applies the action
- <code>put_dhcpv6_opts</code> and advances the packet to the next
- table.
- </p>
-
- <pre>
-reg0[3] = put_dhcpv6_opts(ia_addr = <var>ip</var>, <var>options</var>...);
-next;
- </pre>
-
- <p>
- For DHCPv6 Solicit/Request/Confirm packets, this transforms the
- packet into a DHCPv6 Advertise/Reply, adds the DHCPv6 offer IP
- <var>ip</var> and options to the packet, and stores 1 into reg0[3].
- For other kinds of packets, it just stores 0 into reg0[3]. Either
- way, it continues to the next table.
- </p>
- </li>
-
- <li>
- A priority-0 flow that matches all packets to advances to table 11.
- </li>
- </ul>
-
- <h3>Ingress Table 13: DHCP responses</h3>
-
- <p>
- This table implements DHCP responder for the DHCP replies generated by
- the previous table.
- </p>
-
- <ul>
- <li>
- <p>
- A priority 100 logical flow is added for the logical ports configured
- with DHCPv4 options which matches IPv4 packets with <code>udp.src == 68
- &amp;&amp; udp.dst == 67 &amp;&amp; reg0[3] == 1</code> and
- responds back to the <code>inport</code> after applying these
- actions. If <code>reg0[3]</code> is set to 1, it means that the
- action <code>put_dhcp_opts</code> was successful.
- </p>
-
- <pre>
-eth.dst = eth.src;
-eth.src = <var>E</var>;
-ip4.dst = <var>A</var>;
-ip4.src = <var>S</var>;
-udp.src = 67;
-udp.dst = 68;
-outport = <var>P</var>;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- where <var>E</var> is the server MAC address and <var>S</var> is the
- server IPv4 address defined in the DHCPv4 options and <var>A</var> is
- the IPv4 address defined in the logical port's addresses column.
- </p>
-
- <p>
- (This terminates ingress packet processing; the packet does not go
- to the next ingress table.)
- </p>
- </li>
-
- <li>
- <p>
- A priority 100 logical flow is added for the logical ports configured
- with DHCPv6 options which matches IPv6 packets with <code>udp.src == 546
- &amp;&amp; udp.dst == 547 &amp;&amp; reg0[3] == 1</code> and
- responds back to the <code>inport</code> after applying these
- actions. If <code>reg0[3]</code> is set to 1, it means that the
- action <code>put_dhcpv6_opts</code> was successful.
- </p>
-
- <pre>
-eth.dst = eth.src;
-eth.src = <var>E</var>;
-ip6.dst = <var>A</var>;
-ip6.src = <var>S</var>;
-udp.src = 547;
-udp.dst = 546;
-outport = <var>P</var>;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- where <var>E</var> is the server MAC address and <var>S</var> is the
- server IPv6 LLA address generated from the <code>server_id</code>
- defined in the DHCPv6 options and <var>A</var> is
- the IPv6 address defined in the logical port's addresses column.
- </p>
-
- <p>
- (This terminates packet processing; the packet does not go on the
- next ingress table.)
- </p>
- </li>
-
- <li>
- A priority-0 flow that matches all packets to advances to table 12.
- </li>
- </ul>
-
- <h3>Ingress Table 14 DNS Lookup</h3>
-
- <p>
- This table looks up and resolves the DNS names to the corresponding
- configured IP address(es).
- </p>
-
- <ul>
- <li>
- <p>
- A priority-100 logical flow for each logical switch datapath
- if it is configured with DNS records, which matches the IPv4 and IPv6
- packets with <code>udp.dst</code> = 53 and applies the action
- <code>dns_lookup</code> and advances the packet to the next table.
- </p>
-
- <pre>
-reg0[4] = dns_lookup(); next;
- </pre>
-
- <p>
- For valid DNS packets, this transforms the packet into a DNS
- reply if the DNS name can be resolved, and stores 1 into reg0[4].
- For failed DNS resolution or other kinds of packets, it just stores
- 0 into reg0[4]. Either way, it continues to the next table.
- </p>
- </li>
- </ul>
-
- <h3>Ingress Table 15 DNS Responses</h3>
-
- <p>
- This table implements DNS responder for the DNS replies generated by
- the previous table.
- </p>
-
- <ul>
- <li>
- <p>
- A priority-100 logical flow for each logical switch datapath
- if it is configured with DNS records, which matches the IPv4 and IPv6
- packets with <code>udp.dst = 53 &amp;&amp; reg0[4] == 1</code>
- and responds back to the <code>inport</code> after applying these
- actions. If <code>reg0[4]</code> is set to 1, it means that the
- action <code>dns_lookup</code> was successful.
- </p>
-
- <pre>
-eth.dst &lt;-&gt; eth.src;
-ip4.src &lt;-&gt; ip4.dst;
-udp.dst = udp.src;
-udp.src = 53;
-outport = <var>P</var>;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- (This terminates ingress packet processing; the packet does not go
- to the next ingress table.)
- </p>
- </li>
- </ul>
-
- <h3>Ingress table 16 External ports</h3>
-
- <p>
- Traffic from the <code>external</code> logical ports enter the ingress
- datapath pipeline via the <code>localnet</code> port. This table adds the
- below logical flows to handle the traffic from these ports.
- </p>
-
- <ul>
- <li>
- <p>
- A priority-100 flow is added for each <code>external</code> logical
- port which doesn't reside on a chassis to drop the ARP/IPv6 NS
- request to the router IP(s) (of the logical switch) which matches
- on the <code>inport</code> of the <code>external</code> logical port
- and the valid <code>eth.src</code> address(es) of the
- <code>external</code> logical port.
- </p>
-
- <p>
- This flow guarantees that the ARP/NS request to the router IP
- address from the external ports is responded by only the chassis
- which has claimed these external ports. All the other chassis,
- drops these packets.
- </p>
- </li>
-
- <li>
- A priority-0 flow that matches all packets to advances to table 17.
- </li>
- </ul>
-
- <h3>Ingress Table 17 Destination Lookup</h3>
-
- <p>
- This table implements switching behavior. It contains these logical
- flows:
- </p>
-
- <ul>
- <li>
- A priority-100 flow that outputs all packets with an Ethernet broadcast
- or multicast <code>eth.dst</code> to the <code>MC_FLOOD</code>
- multicast group, which <code>ovn-northd</code> populates with all
- enabled logical ports.
- </li>
-
- <li>
- <p>
- One priority-50 flow that matches each known Ethernet address against
- <code>eth.dst</code> and outputs the packet to the single associated
- output port.
- </p>
-
- <p>
- For the Ethernet address on a logical switch port of type
- <code>router</code>, when that logical switch port's
- <ref column="addresses" table="Logical_Switch_Port"
- db="OVN_Northbound"/> column is set to <code>router</code> and
- the connected logical router port specifies a
- <code>redirect-chassis</code>:
- </p>
-
- <ul>
- <li>
- The flow for the connected logical router port's Ethernet
- address is only programmed on the <code>redirect-chassis</code>.
- </li>
-
- <li>
- If the logical router has rules specified in
- <ref column="nat" table="Logical_Router" db="OVN_Northbound"/> with
- <ref column="external_mac" table="NAT" db="OVN_Northbound"/>, then
- those addresses are also used to populate the switch's destination
- lookup on the chassis where
- <ref column="logical_port" table="NAT" db="OVN_Northbound"/> is
- resident.
- </li>
- </ul>
-
- <p>
- For the Ethernet address on a logical switch port of type
- <code>router</code>, when that logical switch port's
- <ref column="addresses" table="Logical_Switch_Port"
- db="OVN_Northbound"/> column is set to <code>router</code> and
- the connected logical router port specifies a
- <code>reside-on-redirect-chassis</code> and the logical router
- to which the connected logical router port belongs to has a
- <code>redirect-chassis</code> distributed gateway logical router
- port:
- </p>
-
- <ul>
- <li>
- The flow for the connected logical router port's Ethernet
- address is only programmed on the <code>redirect-chassis</code>.
- </li>
- </ul>
- </li>
-
- <li>
- One priority-0 fallback flow that matches all packets and outputs them
- to the <code>MC_UNKNOWN</code> multicast group, which
- <code>ovn-northd</code> populates with all enabled logical ports that
- accept unknown destination packets. As a small optimization, if no
- logical ports accept unknown destination packets,
- <code>ovn-northd</code> omits this multicast group and logical flow.
- </li>
- </ul>
-
- <h3>Egress Table 0: Pre-LB</h3>
-
- <p>
- This table is similar to ingress table <code>Pre-LB</code>. It
- contains a priority-0 flow that simply moves traffic to the next table.
- Moreover it contains a priority-110 flow to move IPv6 Neighbor Discovery
- traffic to the next table. If any load balancing rules exist for the
- datapath, a priority-100 flow is added with a match of <code>ip</code>
- and action of <code>reg0[0] = 1; next;</code> to act as a hint for
- table <code>Pre-stateful</code> to send IP packets to the connection
- tracker for packet de-fragmentation.
- </p>
-
- <h3>Egress Table 1: <code>to-lport</code> Pre-ACLs</h3>
-
- <p>
- This is similar to ingress table <code>Pre-ACLs</code> except for
- <code>to-lport</code> traffic.
- </p>
-
- <h3>Egress Table 2: Pre-stateful</h3>
-
- <p>
- This is similar to ingress table <code>Pre-stateful</code>.
- </p>
-
- <h3>Egress Table 3: LB</h3>
- <p>
- This is similar to ingress table <code>LB</code>.
- </p>
-
- <h3>Egress Table 4: <code>to-lport</code> ACLs</h3>
-
- <p>
- This is similar to ingress table <code>ACLs</code> except for
- <code>to-lport</code> ACLs.
- </p>
-
- <p>
- In addition, the following flows are added.
- </p>
- <ul>
- <li>
- A priority 34000 logical flow is added for each logical port which
- has DHCPv4 options defined to allow the DHCPv4 reply packet and which has
- DHCPv6 options defined to allow the DHCPv6 reply packet from the
- <code>Ingress Table 13: DHCP responses</code>.
- </li>
-
- <li>
- A priority 34000 logical flow is added for each logical switch datapath
- configured with DNS records with the match <code>udp.dst = 53</code>
- to allow the DNS reply packet from the
- <code>Ingress Table 15:DNS responses</code>.
- </li>
- </ul>
-
- <h3>Egress Table 5: <code>to-lport</code> QoS Marking</h3>
-
- <p>
- This is similar to ingress table <code>QoS marking</code> except
- they apply to <code>to-lport</code> QoS rules.
- </p>
-
- <h3>Egress Table 6: <code>to-lport</code> QoS Meter</h3>
-
- <p>
- This is similar to ingress table <code>QoS meter</code> except
- they apply to <code>to-lport</code> QoS rules.
- </p>
-
- <h3>Egress Table 7: Stateful</h3>
-
- <p>
- This is similar to ingress table <code>Stateful</code> except that
- there are no rules added for load balancing new connections.
- </p>
-
- <h3>Egress Table 8: Egress Port Security - IP</h3>
-
- <p>
- This is similar to the port security logic in table
- <code>Ingress Port Security - IP</code> except that <code>outport</code>,
- <code>eth.dst</code>, <code>ip4.dst</code> and <code>ip6.dst</code>
- are checked instead of <code>inport</code>, <code>eth.src</code>,
- <code>ip4.src</code> and <code>ip6.src</code>
- </p>
-
- <h3>Egress Table 9: Egress Port Security - L2</h3>
-
- <p>
- This is similar to the ingress port security logic in ingress table
- <code>Admission Control and Ingress Port Security - L2</code>,
- but with important differences. Most obviously, <code>outport</code> and
- <code>eth.dst</code> are checked instead of <code>inport</code> and
- <code>eth.src</code>. Second, packets directed to broadcast or multicast
- <code>eth.dst</code> are always accepted instead of being subject to the
- port security rules; this is implemented through a priority-100 flow that
- matches on <code>eth.mcast</code> with action <code>output;</code>.
- Finally, to ensure that even broadcast and multicast packets are not
- delivered to disabled logical ports, a priority-150 flow for each
- disabled logical <code>outport</code> overrides the priority-100 flow
- with a <code>drop;</code> action.
- </p>
-
- <h2>Logical Router Datapaths</h2>
-
- <p>
- Logical router datapaths will only exist for <ref table="Logical_Router"
- db="OVN_Northbound"/> rows in the <ref db="OVN_Northbound"/> database
- that do not have <ref column="enabled" table="Logical_Router"
- db="OVN_Northbound"/> set to <code>false</code>
- </p>
-
- <h3>Ingress Table 0: L2 Admission Control</h3>
-
- <p>
- This table drops packets that the router shouldn't see at all based on
- their Ethernet headers. It contains the following flows:
- </p>
-
- <ul>
- <li>
- Priority-100 flows to drop packets with VLAN tags or multicast Ethernet
- source addresses.
- </li>
-
- <li>
- <p>
- For each enabled router port <var>P</var> with Ethernet address
- <var>E</var>, a priority-50 flow that matches <code>inport ==
- <var>P</var> &amp;&amp; (eth.mcast || eth.dst ==
- <var>E</var></code>), with action <code>next;</code>.
- </p>
-
- <p>
- For the gateway port on a distributed logical router (where
- one of the logical router ports specifies a
- <code>redirect-chassis</code>), the above flow matching
- <code>eth.dst == <var>E</var></code> is only programmed on
- the gateway port instance on the
- <code>redirect-chassis</code>.
- </p>
- </li>
-
- <li>
- <p>
- For each <code>dnat_and_snat</code> NAT rule on a distributed
- router that specifies an external Ethernet address <var>E</var>,
- a priority-50 flow that matches <code>inport == <var>GW</var>
- &amp;&amp; eth.dst == <var>E</var></code>, where <var>GW</var>
- is the logical router gateway port, with action
- <code>next;</code>.
- </p>
-
- <p>
- This flow is only programmed on the gateway port instance on
- the chassis where the <code>logical_port</code> specified in
- the NAT rule resides.
- </p>
- </li>
- </ul>
-
- <p>
- Other packets are implicitly dropped.
- </p>
-
- <h3>Ingress Table 1: IP Input</h3>
-
- <p>
- This table is the core of the logical router datapath functionality. It
- contains the following flows to implement very basic IP host
- functionality.
- </p>
-
- <ul>
- <li>
- <p>
- L3 admission control: A priority-100 flow drops packets that match
- any of the following:
- </p>
-
- <ul>
- <li>
- <code>ip4.src[28..31] == 0xe</code> (multicast source)
- </li>
- <li>
- <code>ip4.src == 255.255.255.255</code> (broadcast source)
- </li>
- <li>
- <code>ip4.src == 127.0.0.0/8 || ip4.dst == 127.0.0.0/8</code>
- (localhost source or destination)
- </li>
- <li>
- <code>ip4.src == 0.0.0.0/8 || ip4.dst == 0.0.0.0/8</code> (zero
- network source or destination)
- </li>
- <li>
- <code>ip4.src</code> or <code>ip6.src</code> is any IP
- address owned by the router, unless the packet was recirculated
- due to egress loopback as indicated by
- <code>REGBIT_EGRESS_LOOPBACK</code>.
- </li>
- <li>
- <code>ip4.src</code> is the broadcast address of any IP network
- known to the router.
- </li>
- </ul>
- </li>
-
- <li>
- <p>
- ICMP echo reply. These flows reply to ICMP echo requests received
- for the router's IP address. Let <var>A</var> be an IP address
- owned by a router port. Then, for each <var>A</var> that is
- an IPv4 address, a priority-90 flow matches on
- <code>ip4.dst == <var>A</var></code> and
- <code>icmp4.type == 8 &amp;&amp; icmp4.code == 0</code>
- (ICMP echo request). For each <var>A</var> that is an IPv6
- address, a priority-90 flow matches on
- <code>ip6.dst == <var>A</var></code> and
- <code>icmp6.type == 128 &amp;&amp; icmp6.code == 0</code>
- (ICMPv6 echo request). The port of the router that receives the
- echo request does not matter. Also, the <code>ip.ttl</code> of
- the echo request packet is not checked, so it complies with
- RFC 1812, section 4.2.2.9. Flows for ICMPv4 echo requests use the
- following actions:
- </p>
-
- <pre>
-ip4.dst &lt;-&gt; ip4.src;
-ip.ttl = 255;
-icmp4.type = 0;
-flags.loopback = 1;
-next;
- </pre>
-
- <p>
- Flows for ICMPv6 echo requests use the following actions:
- </p>
-
- <pre>
-ip6.dst &lt;-&gt; ip6.src;
-ip.ttl = 255;
-icmp6.type = 129;
-flags.loopback = 1;
-next;
- </pre>
- </li>
-
- <li>
- <p>
- Reply to ARP requests.
- </p>
-
- <p>
- These flows reply to ARP requests for the router's own IP address
- and populates mac binding table of the logical router port.
- The ARP requests are handled only if the requestor's IP belongs
- to the same subnets of the logical router port.
- For each router port <var>P</var> that owns IP address <var>A</var>,
- which belongs to subnet <var>S</var> with prefix length <var>L</var>,
- and Ethernet address <var>E</var>, a priority-90 flow matches
- <code>inport == <var>P</var> &amp;&amp;
- arp.spa == <var>S</var>/<var>L</var> &amp;&amp; arp.op == 1
- &amp;&amp; arp.tpa == <var>A</var></code> (ARP request) with the
- following actions:
- </p>
-
- <pre>
-put_arp(inport, arp.spa, arp.sha);
-eth.dst = eth.src;
-eth.src = <var>E</var>;
-arp.op = 2; /* ARP reply. */
-arp.tha = arp.sha;
-arp.sha = <var>E</var>;
-arp.tpa = arp.spa;
-arp.spa = <var>A</var>;
-outport = <var>P</var>;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- For the gateway port on a distributed logical router (where
- one of the logical router ports specifies a
- <code>redirect-chassis</code>), the above flows are only
- programmed on the gateway port instance on the
- <code>redirect-chassis</code>. This behavior avoids generation
- of multiple ARP responses from different chassis, and allows
- upstream MAC learning to point to the
- <code>redirect-chassis</code>.
- </p>
-
- <p>
- For the logical router port with the option
- <code>reside-on-redirect-chassis</code> set (which is centralized),
- the above flows are only programmed on the gateway port instance on
- the <code>redirect-chassis</code> (if the logical router has a
- distributed gateway port). This behavior avoids generation
- of multiple ARP responses from different chassis, and allows
- upstream MAC learning to point to the
- <code>redirect-chassis</code>.
- </p>
- </li>
-
- <li>
- <p>
- These flows handles ARP requests not for router's own IP address.
- They use the SPA and SHA to populate the logical router port's
- mac binding table, with priority 80. The typical use case of
- these flows are GARP requests handling. For the gateway port
- on a distributed logical router, these flows are only programmed
- on the gateway port instance on the <code>redirect-chassis</code>.
- </p>
- </li>
-
- <li>
- <p>
- These flows reply to ARP requests for the virtual IP addresses
- configured in the router for DNAT or load balancing. For a
- configured DNAT IP address or a load balancer IPv4 VIP <var>A</var>,
- for each router port <var>P</var> with Ethernet
- address <var>E</var>, a priority-90 flow matches
- <code>inport == <var>P</var> &amp;&amp; arp.op == 1 &amp;&amp;
- arp.tpa == <var>A</var></code> (ARP request)
- with the following actions:
- </p>
-
- <pre>
-eth.dst = eth.src;
-eth.src = <var>E</var>;
-arp.op = 2; /* ARP reply. */
-arp.tha = arp.sha;
-arp.sha = <var>E</var>;
-arp.tpa = arp.spa;
-arp.spa = <var>A</var>;
-outport = <var>P</var>;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- For the gateway port on a distributed logical router with NAT
- (where one of the logical router ports specifies a
- <code>redirect-chassis</code>):
- </p>
-
- <ul>
- <li>
- If the corresponding NAT rule cannot be handled in a
- distributed manner, then this flow is only programmed on
- the gateway port instance on the
- <code>redirect-chassis</code>. This behavior avoids
- generation of multiple ARP responses from different chassis,
- and allows upstream MAC learning to point to the
- <code>redirect-chassis</code>.
- </li>
-
- <li>
- <p>
- If the corresponding NAT rule can be handled in a distributed
- manner, then this flow is only programmed on the gateway port
- instance where the <code>logical_port</code> specified in the
- NAT rule resides.
- </p>
-
- <p>
- Some of the actions are different for this case, using the
- <code>external_mac</code> specified in the NAT rule rather
- than the gateway port's Ethernet address <var>E</var>:
- </p>
-
- <pre>
-eth.src = <var>external_mac</var>;
-arp.sha = <var>external_mac</var>;
- </pre>
-
- <p>
- This behavior avoids generation of multiple ARP responses
- from different chassis, and allows upstream MAC learning to
- point to the correct chassis.
- </p>
- </li>
- </ul>
- </li>
-
- <li>
- ARP reply handling. This flow uses ARP replies to populate the
- logical router's ARP table. A priority-90 flow with match <code>arp.op
- == 2</code> has actions <code>put_arp(inport, arp.spa,
- arp.sha);</code>.
- </li>
-
- <li>
- <p>
- Reply to IPv6 Neighbor Solicitations. These flows reply to
- Neighbor Solicitation requests for the router's own IPv6
- address and load balancing IPv6 VIPs and populate the logical
- router's mac binding table.
- </p>
-
- <p>
- For each router port <var>P</var> that
- owns IPv6 address <var>A</var>, solicited node address <var>S</var>,
- and Ethernet address <var>E</var>, a priority-90 flow matches
- <code>inport == <var>P</var> &amp;&amp;
- nd_ns &amp;&amp; ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp;
- nd.target == <var>A</var></code> with the following actions:
- </p>
-
- <pre>
-put_nd(inport, ip6.src, nd.sll);
-nd_na_router {
- eth.src = <var>E</var>;
- ip6.src = <var>A</var>;
- nd.target = <var>A</var>;
- nd.tll = <var>E</var>;
- outport = inport;
- flags.loopback = 1;
- output;
-};
- </pre>
-
- <p>
- For each router port <var>P</var> that has load balancing VIP
- <var>A</var>, solicited node address <var>S</var>, and Ethernet
- address <var>E</var>, a priority-90 flow matches
- <code>inport == <var>P</var> &amp;&amp;
- nd_ns &amp;&amp; ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp;
- nd.target == <var>A</var></code> with the following actions:
- </p>
-
- <pre>
-put_nd(inport, ip6.src, nd.sll);
-nd_na {
- eth.src = <var>E</var>;
- ip6.src = <var>A</var>;
- nd.target = <var>A</var>;
- nd.tll = <var>E</var>;
- outport = inport;
- flags.loopback = 1;
- output;
-};
- </pre>
-
- <p>
- For the gateway port on a distributed logical router (where
- one of the logical router ports specifies a
- <code>redirect-chassis</code>), the above flows replying to
- IPv6 Neighbor Solicitations are only programmed on the
- gateway port instance on the <code>redirect-chassis</code>.
- This behavior avoids generation of multiple replies from
- different chassis, and allows upstream MAC learning to point
- to the <code>redirect-chassis</code>.
- </p>
- </li>
-
- <li>
- IPv6 neighbor advertisement handling. This flow uses neighbor
- advertisements to populate the logical router's mac binding
- table. A priority-90 flow with match <code>nd_na</code>
- has actions <code>put_nd(inport, nd.target, nd.tll);</code>.
- </li>
-
- <li>
- IPv6 neighbor solicitation for non-hosted addresses handling.
- This flow uses neighbor solicitations to populate the logical
- router's mac binding table (ones that were directed at the
- logical router would have matched the priority-90 neighbor
- solicitation flow already). A priority-80 flow with match
- <code>nd_ns</code> has actions
- <code>put_nd(inport, ip6.src, nd.sll);</code>.
- </li>
-
- <li>
- <p>
- UDP port unreachable. Priority-80 flows generate ICMP port
- unreachable messages in reply to UDP datagrams directed to the
- router's IP address, except in the special case of gateways,
- which accept traffic directed to a router IP for load balancing
- and NAT purposes.
- </p>
-
- <p>
- These flows should not match IP fragments with nonzero offset.
- </p>
- </li>
-
- <li>
- <p>
- TCP reset. Priority-80 flows generate TCP reset messages in reply
- to TCP datagrams directed to the router's IP address, except in
- the special case of gateways, which accept traffic directed to a
- router IP for load balancing and NAT purposes.
- </p>
-
- <p>
- These flows should not match IP fragments with nonzero offset.
- </p>
- </li>
-
- <li>
- <p>
- Protocol or address unreachable. Priority-70 flows generate ICMP
- protocol or address unreachable messages for IPv4 and IPv6
- respectively in reply to packets directed to the router's IP
- address on IP protocols other than UDP, TCP, and ICMP, except in the
- special case of gateways, which accept traffic directed to a router
- IP for load balancing purposes.
- </p>
-
- <p>
- These flows should not match IP fragments with nonzero offset.
- </p>
- </li>
-
- <li>
- Drop other IP traffic to this router. These flows drop any other
- traffic destined to an IP address of this router that is not already
- handled by one of the flows above, which amounts to ICMP (other than
- echo requests) and fragments with nonzero offsets. For each IP address
- <var>A</var> owned by the router, a priority-60 flow matches
- <code>ip4.dst == <var>A</var></code> and drops the traffic. An
- exception is made and the above flow is not added if the router
- port's own IP address is used to SNAT packets passing through that
- router.
- </li>
- </ul>
-
- <p>
- The flows above handle all of the traffic that might be directed to the
- router itself. The following flows (with lower priorities) handle the
- remaining traffic, potentially for forwarding:
- </p>
-
- <ul>
- <li>
- Drop Ethernet local broadcast. A priority-50 flow with match
- <code>eth.bcast</code> drops traffic destined to the local Ethernet
- broadcast address. By definition this traffic should not be forwarded.
- </li>
-
- <li>
- <p>
- ICMP time exceeded. For each router port <var>P</var>, whose IP
- address is <var>A</var>, a priority-40 flow with match <code>inport
- == <var>P</var> &amp;&amp; ip.ttl == {0, 1} &amp;&amp;
- !ip.later_frag</code> matches packets whose TTL has expired, with the
- following actions to send an ICMP time exceeded reply for IPv4 and
- IPv6 respectively:
- </p>
-
- <pre>
-icmp4 {
- icmp4.type = 11; /* Time exceeded. */
- icmp4.code = 0; /* TTL exceeded in transit. */
- ip4.dst = ip4.src;
- ip4.src = <var>A</var>;
- ip.ttl = 255;
- next;
-};
-
-icmp6 {
- icmp6.type = 3; /* Time exceeded. */
- icmp6.code = 0; /* TTL exceeded in transit. */
- ip6.dst = ip6.src;
- ip6.src = <var>A</var>;
- ip.ttl = 255;
- next;
-};
- </pre>
- </li>
-
- <li>
- TTL discard. A priority-30 flow with match <code>ip.ttl == {0,
- 1}</code> and actions <code>drop;</code> drops other packets whose TTL
- has expired, that should not receive a ICMP error reply (i.e. fragments
- with nonzero offset).
- </li>
-
- <li>
- Next table. A priority-0 flows match all packets that aren't already
- handled and uses actions <code>next;</code> to feed them to the next
- table.
- </li>
- </ul>
-
- <h3>Ingress Table 2: DEFRAG</h3>
-
- <p>
- This is to send packets to connection tracker for tracking and
- defragmentation. It contains a priority-0 flow that simply moves traffic
- to the next table. If load balancing rules with virtual IP addresses
- (and ports) are configured in <code>OVN_Northbound</code> database for a
- Gateway router, a priority-100 flow is added for each configured virtual
- IP address <var>VIP</var>. For IPv4 <var>VIPs</var> the flow matches
- <code>ip &amp;&amp; ip4.dst == <var>VIP</var></code>. For IPv6
- <var>VIPs</var>, the flow matches <code>ip &amp;&amp; ip6.dst ==
- <var>VIP</var></code>. The flow uses the action <code>ct_next;</code>
- to send IP packets to the connection tracker for packet de-fragmentation
- and tracking before sending it to the next table.
- </p>
-
- <h3>Ingress Table 3: UNSNAT</h3>
-
- <p>
- This is for already established connections' reverse traffic.
- i.e., SNAT has already been done in egress pipeline and now the
- packet has entered the ingress pipeline as part of a reply. It is
- unSNATted here.
- </p>
-
- <p>Ingress Table 3: UNSNAT on Gateway Routers</p>
-
- <ul>
- <li>
- <p>
- If the Gateway router has been configured to force SNAT any
- previously DNATted packets to <var>B</var>, a priority-110 flow
- matches <code>ip &amp;&amp; ip4.dst == <var>B</var></code> with
- an action <code>ct_snat; </code>.
- </p>
-
- <p>
- If the Gateway router has been configured to force SNAT any
- previously load-balanced packets to <var>B</var>, a priority-100 flow
- matches <code>ip &amp;&amp; ip4.dst == <var>B</var></code> with
- an action <code>ct_snat; </code>.
- </p>
-
- <p>
- For each NAT configuration in the OVN Northbound database, that asks
- to change the source IP address of a packet from <var>A</var> to
- <var>B</var>, a priority-90 flow matches <code>ip &amp;&amp;
- ip4.dst == <var>B</var></code> with an action
- <code>ct_snat; </code>.
- </p>
-
- <p>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </p>
- </li>
- </ul>
-
- <p>Ingress Table 3: UNSNAT on Distributed Routers</p>
-
- <ul>
- <li>
- <p>
- For each configuration in the OVN Northbound database, that asks
- to change the source IP address of a packet from <var>A</var> to
- <var>B</var>, a priority-100 flow matches <code>ip &amp;&amp;
- ip4.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code>,
- where <var>GW</var> is the logical router gateway port, with an
- action <code>ct_snat;</code>.
- </p>
-
- <p>
- If the NAT rule cannot be handled in a distributed manner, then
- the priority-100 flow above is only programmed on the
- <code>redirect-chassis</code>.
- </p>
-
- <p>
- For each configuration in the OVN Northbound database, that asks
- to change the source IP address of a packet from <var>A</var> to
- <var>B</var>, a priority-50 flow matches <code>ip &amp;&amp;
- ip4.dst == <var>B</var></code> with an action
- <code>REGBIT_NAT_REDIRECT = 1; next;</code>. This flow is for
- east/west traffic to a NAT destination IPv4 address. By
- setting the <code>REGBIT_NAT_REDIRECT</code> flag, in the
- ingress table <code>Gateway Redirect</code> this will trigger a
- redirect to the instance of the gateway port on the
- <code>redirect-chassis</code>.
- </p>
-
- <p>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </p>
- </li>
- </ul>
-
- <h3>Ingress Table 4: DNAT</h3>
-
- <p>
- Packets enter the pipeline with destination IP address that needs to
- be DNATted from a virtual IP address to a real IP address. Packets
- in the reverse direction needs to be unDNATed.
- </p>
-
- <p>Ingress Table 4: Load balancing DNAT rules</p>
-
- <p>
- Following load balancing DNAT flows are added for Gateway router or
- Router with gateway port. These flows are programmed only on the
- <code>redirect-chassis</code>. These flows do not get programmed for
- load balancers with IPv6 <var>VIPs</var>.
- </p>
-
- <ul>
- <li>
- For all the configured load balancing rules for a Gateway router or
- Router with gateway port in <code>OVN_Northbound</code> database that
- includes a L4 port <var>PORT</var> of protocol <var>P</var> and IPv4
- address <var>VIP</var>, a priority-120 flow that matches on
- <code>ct.new &amp;&amp; ip &amp;&amp; ip4.dst == <var>VIP</var>
- &amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>PORT
- </var></code> with an action of <code>ct_lb(<var>args</var>)</code>,
- where <var>args</var> contains comma separated IPv4 addresses (and
- optional port numbers) to load balance to. If the router is configured
- to force SNAT any load-balanced packets, the above action will be
- replaced by <code>flags.force_snat_for_lb = 1;
- ct_lb(<var>args</var>);</code>.
- </li>
-
- <li>
- For all the configured load balancing rules for a router in
- <code>OVN_Northbound</code> database that includes a L4 port
- <var>PORT</var> of protocol <var>P</var> and IPv4 address
- <var>VIP</var>, a priority-120 flow that matches on
- <code>ct.est &amp;&amp; ip &amp;&amp; ip4.dst == <var>VIP</var>
- &amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>PORT
- </var></code> with an action of <code>ct_dnat;</code>. If the router is
- configured to force SNAT any load-balanced packets, the above action
- will be replaced by <code>flags.force_snat_for_lb = 1; ct_dnat;</code>.
- </li>
-
- <li>
- For all the configured load balancing rules for a router in
- <code>OVN_Northbound</code> database that includes just an IP address
- <var>VIP</var> to match on, a priority-110 flow that matches on
- <code>ct.new &amp;&amp; ip &amp;&amp; ip4.dst ==
- <var>VIP</var></code> with an action of
- <code>ct_lb(<var>args</var>)</code>, where <var>args</var> contains
- comma separated IPv4 addresses. If the router is configured to force
- SNAT any load-balanced packets, the above action will be replaced by
- <code>flags.force_snat_for_lb = 1; ct_lb(<var>args</var>);</code>.
- </li>
-
- <li>
- For all the configured load balancing rules for a router in
- <code>OVN_Northbound</code> database that includes just an IP address
- <var>VIP</var> to match on, a priority-110 flow that matches on
- <code>ct.est &amp;&amp; ip &amp;&amp; ip4.dst ==
- <var>VIP</var></code> with an action of <code>ct_dnat;</code>.
- If the router is configured to force SNAT any load-balanced
- packets, the above action will be replaced by
- <code>flags.force_snat_for_lb = 1; ct_dnat;</code>.
- </li>
- </ul>
-
- <p>Ingress Table 4: DNAT on Gateway Routers</p>
-
- <ul>
- <li>
- For each configuration in the OVN Northbound database, that asks
- to change the destination IP address of a packet from <var>A</var> to
- <var>B</var>, a priority-100 flow matches <code>ip &amp;&amp;
- ip4.dst == <var>A</var></code> with an action
- <code>flags.loopback = 1; ct_dnat(<var>B</var>);</code>. If the
- Gateway router is configured to force SNAT any DNATed packet,
- the above action will be replaced by
- <code>flags.force_snat_for_dnat = 1; flags.loopback = 1;
- ct_dnat(<var>B</var>);</code>.
- </li>
-
- <li>
- For all IP packets of a Gateway router, a priority-50 flow with an
- action <code>flags.loopback = 1; ct_dnat;</code>.
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <p>Ingress Table 4: DNAT on Distributed Routers</p>
-
- <p>
- On distributed routers, the DNAT table only handles packets
- with destination IP address that needs to be DNATted from a
- virtual IP address to a real IP address. The unDNAT processing
- in the reverse direction is handled in a separate table in the
- egress pipeline.
- </p>
-
- <ul>
- <li>
- <p>
- For each configuration in the OVN Northbound database, that asks
- to change the destination IP address of a packet from <var>A</var> to
- <var>B</var>, a priority-100 flow matches <code>ip &amp;&amp;
- ip4.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code>,
- where <var>GW</var> is the logical router gateway port, with an
- action <code>ct_dnat(<var>B</var>);</code>.
- </p>
-
- <p>
- If the NAT rule cannot be handled in a distributed manner, then
- the priority-100 flow above is only programmed on the
- <code>redirect-chassis</code>.
- </p>
-
- <p>
- For each configuration in the OVN Northbound database, that asks
- to change the destination IP address of a packet from <var>A</var> to
- <var>B</var>, a priority-50 flow matches <code>ip &amp;&amp;
- ip4.dst == <var>B</var></code> with an action
- <code>REGBIT_NAT_REDIRECT = 1; next;</code>. This flow is for
- east/west traffic to a NAT destination IPv4 address. By
- setting the <code>REGBIT_NAT_REDIRECT</code> flag, in the
- ingress table <code>Gateway Redirect</code> this will trigger a
- redirect to the instance of the gateway port on the
- <code>redirect-chassis</code>.
- </p>
-
- <p>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </p>
- </li>
- </ul>
-
- <h3>Ingress Table 5: IPv6 ND RA option processing</h3>
-
- <ul>
- <li>
- <p>
- A priority-50 logical flow is added for each logical router port
- configured with IPv6 ND RA options which matches IPv6 ND Router
- Solicitation packet and applies the action
- <code>put_nd_ra_opts</code> and advances the packet to the next
- table.
- </p>
-
- <pre>
-reg0[5] = put_nd_ra_opts(<var>options</var>);next;
- </pre>
-
- <p>
- For a valid IPv6 ND RS packet, this transforms the packet into an
- IPv6 ND RA reply and sets the RA options to the packet and stores 1
- into reg0[5]. For other kinds of packets, it just stores 0 into
- reg0[5]. Either way, it continues to the next table.
- </p>
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <h3>Ingress Table 6: IPv6 ND RA responder</h3>
-
- <p>
- This table implements IPv6 ND RA responder for the IPv6 ND RA replies
- generated by the previous table.
- </p>
-
- <ul>
- <li>
- <p>
- A priority-50 logical flow is added for each logical router port
- configured with IPv6 ND RA options which matches IPv6 ND RA
- packets and <code>reg0[5] == 1</code> and responds back to the
- <code>inport</code> after applying these actions.
- If <code>reg0[5]</code> is set to 1, it means that the action
- <code>put_nd_ra_opts</code> was successful.
- </p>
-
- <pre>
-eth.dst = eth.src;
-eth.src = <var>E</var>;
-ip6.dst = ip6.src;
-ip6.src = <var>I</var>;
-outport = <var>P</var>;
-flags.loopback = 1;
-output;
- </pre>
-
- <p>
- where <var>E</var> is the MAC address and <var>I</var> is the IPv6
- link local address of the logical router port.
- </p>
-
- <p>
- (This terminates packet processing in ingress pipeline; the packet
- does not go to the next ingress table.)
- </p>
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <h3>Ingress Table 7: IP Routing</h3>
-
- <p>
- A packet that arrives at this table is an IP packet that should be
- routed to the address in <code>ip4.dst</code> or
- <code>ip6.dst</code>. This table implements IP routing, setting
- <code>reg0</code> (or <code>xxreg0</code> for IPv6) to the next-hop IP
- address (leaving <code>ip4.dst</code> or <code>ip6.dst</code>, the
- packet's final destination, unchanged) and advances to the next
- table for ARP resolution. It also sets <code>reg1</code> (or
- <code>xxreg1</code>) to the IP address owned by the selected router
- port (ingress table <code>ARP Request</code> will generate an ARP
- request, if needed, with <code>reg0</code> as the target protocol
- address and <code>reg1</code> as the source protocol address).
- </p>
-
- <p>
- This table contains the following logical flows:
- </p>
-
- <ul>
- <li>
- <p>
- For distributed logical routers where one of the logical router
- ports specifies a <code>redirect-chassis</code>, a priority-400
- logical flow for each ip source/destination couple that matches the
- <code>dnat_and_snat</code> NAT rules configured. These flows will
- allow to properly forward traffic to the external connections if
- available and avoid sending it through the tunnel.
- Assuming the two following NAT rules have been configured:
- </p>
-
- <pre>
-external_ip{0,1} = <var>EIP{0,1}</var>;
-external_mac{0,1} = <var>MAC{0,1}</var>;
-logical_ip{0,1} = <var>LIP{0,1}</var>;
- </pre>
-
- <p>
- the following action will be applied:
- </p>
-
- <pre>
-eth.dst = <var>MAC0</var>;
-eth.src = <var>MAC1</var>;
-reg0 = ip4.dst;
-reg1 = <var>EIP1</var>;
-outport = <code>redirect-chassis-port</code>;
-<code>REGBIT_DISTRIBUTED_NAT = 1; next;</code>.
- </pre>
-
- <p>
- Morover a priority-400 logical flow is configured for each
- <code>dnat_and_snat</code> NAT rule configured in order to
- not send traffic for local FIP through the overlay tunnels
- but manage it in the local hypervisor
- </p>
- </li>
-
- <li>
- <p>
- For distributed logical routers where one of the logical router
- ports specifies a <code>redirect-chassis</code>, a priority-300
- logical flow with match <code>REGBIT_NAT_REDIRECT == 1</code> has
- actions <code>ip.ttl--; next;</code>. The <code>outport</code>
- will be set later in the Gateway Redirect table.
- </p>
- </li>
-
- <li>
- <p>
- IPv4 routing table. For each route to IPv4 network <var>N</var> with
- netmask <var>M</var>, on router port <var>P</var> with IP address
- <var>A</var> and Ethernet
- address <var>E</var>, a logical flow with match <code>ip4.dst ==
- <var>N</var>/<var>M</var></code>, whose priority is the number of
- 1-bits in <var>M</var>, has the following actions:
- </p>
-
- <pre>
-ip.ttl--;
-reg0 = <var>G</var>;
-reg1 = <var>A</var>;
-eth.src = <var>E</var>;
-outport = <var>P</var>;
-flags.loopback = 1;
-next;
- </pre>
-
- <p>
- (Ingress table 1 already verified that <code>ip.ttl--;</code> will
- not yield a TTL exceeded error.)
- </p>
-
- <p>
- If the route has a gateway, <var>G</var> is the gateway IP address.
- Instead, if the route is from a configured static route, <var>G</var>
- is the next hop IP address. Else it is <code>ip4.dst</code>.
- </p>
- </li>
-
- <li>
- <p>
- IPv6 routing table. For each route to IPv6 network
- <var>N</var> with netmask <var>M</var>, on router port
- <var>P</var> with IP address <var>A</var> and Ethernet address
- <var>E</var>, a logical flow with match in CIDR notation
- <code>ip6.dst == <var>N</var>/<var>M</var></code>,
- whose priority is the integer value of <var>M</var>, has the
- following actions:
- </p>
-
- <pre>
-ip.ttl--;
-xxreg0 = <var>G</var>;
-xxreg1 = <var>A</var>;
-eth.src = <var>E</var>;
-outport = <var>P</var>;
-flags.loopback = 1;
-next;
- </pre>
-
- <p>
- (Ingress table 1 already verified that <code>ip.ttl--;</code> will
- not yield a TTL exceeded error.)
- </p>
-
- <p>
- If the route has a gateway, <var>G</var> is the gateway IP address.
- Instead, if the route is from a configured static route, <var>G</var>
- is the next hop IP address. Else it is <code>ip6.dst</code>.
- </p>
-
- <p>
- If the address <var>A</var> is in the link-local scope, the
- route will be limited to sending on the ingress port.
- </p>
- </li>
- </ul>
-
- <h3>Ingress Table 8: ARP/ND Resolution</h3>
-
- <p>
- Any packet that reaches this table is an IP packet whose next-hop
- IPv4 address is in <code>reg0</code> or IPv6 address is in
- <code>xxreg0</code>. (<code>ip4.dst</code> or
- <code>ip6.dst</code> contains the final destination.) This table
- resolves the IP address in <code>reg0</code> (or
- <code>xxreg0</code>) into an output port in <code>outport</code>
- and an Ethernet address in <code>eth.dst</code>, using the
- following flows:
- </p>
-
- <ul>
- <li>
- <p>
- For distributed logical routers where one of the logical router
- ports specifies a <code>redirect-chassis</code>, a priority-400
- logical flow with match <code>REGBIT_DISTRIBUTED_NAT == 1</code>
- has action <code>next;</code>
- </p>
- <p>
- For distributed logical routers where one of the logical router
- ports specifies a <code>redirect-chassis</code>, a priority-200
- logical flow with match <code>REGBIT_NAT_REDIRECT == 1</code> has
- actions <code>eth.dst = <var>E</var>; next;</code>, where
- <var>E</var> is the ethernet address of the router's distributed
- gateway port.
- </p>
- </li>
-
- <li>
- <p>
- Static MAC bindings. MAC bindings can be known statically based on
- data in the <code>OVN_Northbound</code> database. For router ports
- connected to logical switches, MAC bindings can be known statically
- from the <code>addresses</code> column in the
- <code>Logical_Switch_Port</code> table. For router ports
- connected to other logical routers, MAC bindings can be known
- statically from the <code>mac</code> and <code>networks</code>
- column in the <code>Logical_Router_Port</code> table.
- </p>
-
- <p>
- For each IPv4 address <var>A</var> whose host is known to have
- Ethernet address <var>E</var> on router port <var>P</var>, a
- priority-100 flow with match <code>outport === <var>P</var>
- &amp;&amp; reg0 == <var>A</var></code> has actions
- <code>eth.dst = <var>E</var>; next;</code>.
- </p>
-
- <p>
- For each IPv6 address <var>A</var> whose host is known to have
- Ethernet address <var>E</var> on router port <var>P</var>, a
- priority-100 flow with match <code>outport === <var>P</var>
- &amp;&amp; xxreg0 == <var>A</var></code> has actions
- <code>eth.dst = <var>E</var>; next;</code>.
- </p>
-
- <p>
- For each logical router port with an IPv4 address <var>A</var> and
- a mac address of <var>E</var> that is reachable via a different
- logical router port <var>P</var>, a priority-100 flow with
- match <code>outport === <var>P</var> &amp;&amp; reg0 ==
- <var>A</var></code> has actions <code>eth.dst = <var>E</var>;
- next;</code>.
- </p>
-
- <p>
- For each logical router port with an IPv6 address <var>A</var> and
- a mac address of <var>E</var> that is reachable via a different
- logical router port <var>P</var>, a priority-100 flow with
- match <code>outport === <var>P</var> &amp;&amp; xxreg0 ==
- <var>A</var></code> has actions <code>eth.dst = <var>E</var>;
- next;</code>.
- </p>
- </li>
-
- <li>
- <p>
- Dynamic MAC bindings. These flows resolve MAC-to-IP bindings
- that have become known dynamically through ARP or neighbor
- discovery. (The ingress table <code>ARP Request</code> will
- issue an ARP or neighbor solicitation request for cases where
- the binding is not yet known.)
- </p>
-
- <p>
- A priority-0 logical flow with match <code>ip4</code> has actions
- <code>get_arp(outport, reg0); next;</code>.
- </p>
-
- <p>
- A priority-0 logical flow with match <code>ip6</code> has actions
- <code>get_nd(outport, xxreg0); next;</code>.
- </p>
- </li>
- </ul>
-
- <h3>Ingress Table 9: Check packet length</h3>
-
- <p>
- For distributed logical routers with distributed gateway port configured
- with <code>options:gateway_mtu</code> to a valid integer value, this
- table adds a priority-50 logical flow with the match
- <code>ip4 &amp;&amp; outport == <var>GW_PORT</var></code> where
- <var>GW_PORT</var> is the distributed gateway router port and applies the
- action <code>check_pkt_larger</code> and advances the packet to the
- next table.
- </p>
-
- <pre>
-REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
- </pre>
-
- <p>
- where <var>L</var> is the packet length to check for. If the packet
- is larger than <var>L</var>, it stores 1 in the register bit
- <code>REGBIT_PKT_LARGER</code>. The value of
- <var>L</var> is taken from <ref column="options:gateway_mtu"
- table="Logical_Router_Port" db="OVN_Northbound"/> column of
- <ref table="Logical_Router_Port" db="OVN_Northbound"/> row.
- </p>
-
- <p>
- This table adds one priority-0 fallback flow that matches all packets
- and advances to the next table.
- </p>
-
- <h3>Ingress Table 10: Handle larger packets</h3>
-
- <p>
- For distributed logical routers with distributed gateway port configured
- with <code>options:gateway_mtu</code> to a valid integer value, this
- table adds the following priority-50 logical flow for each
- logical router port with the match <code>ip4 &amp;&amp;
- inport == <var>LRP</var> &amp;&amp; outport == <var>GW_PORT</var>
- &amp;&amp; REGBIT_PKT_LARGER</code>, where <var>LRP</var> is the logical
- router port and <var>GW_PORT</var> is the distributed gateway router port
- and applies the following action
- </p>
-
- <pre>
-icmp4 {
- icmp4.type = 3; /* Destination Unreachable. */
- icmp4.code = 4; /* Frag Needed and DF was Set. */
- icmp4.frag_mtu = <var>M</var>;
- eth.dst = <var>E</var>;
- ip4.dst = ip4.src;
- ip4.src = <var>I</var>;
- ip.ttl = 255;
- REGBIT_EGRESS_LOOPBACK = 1;
- next(pipeline=ingress, table=0);
-};
- </pre>
-
- <ul>
- <li>
- Where <var>M</var> is the (fragment MTU - 58) whose value is taken from
- <ref column="options:gateway_mtu" table="Logical_Router_Port"
- db="OVN_Northbound"/> column of
- <ref table="Logical_Router_Port" db="OVN_Northbound"/> row.
- </li>
-
- <li>
- <var>E</var> is the Ethernet address of the logical router port.
- </li>
-
- <li>
- <var>I</var> is the IPv4 address of the logical router port.
- </li>
- </ul>
-
- <p>
- This table adds one priority-0 fallback flow that matches all packets
- and advances to the next table.
- </p>
-
- <h3>Ingress Table 11: Gateway Redirect</h3>
-
- <p>
- For distributed logical routers where one of the logical router
- ports specifies a <code>redirect-chassis</code>, this table redirects
- certain packets to the distributed gateway port instance on the
- <code>redirect-chassis</code>. This table has the following flows:
- </p>
-
- <ul>
- <li>
- A priority-300 logical flow with match
- <code>REGBIT_DISTRIBUTED_NAT == 1</code> has action
- <code>next;</code>
- </li>
- <li>
- A priority-200 logical flow with match
- <code>REGBIT_NAT_REDIRECT == 1</code> has actions
- <code>outport = <var>CR</var>; next;</code>, where <var>CR</var>
- is the <code>chassisredirect</code> port representing the instance
- of the logical router distributed gateway port on the
- <code>redirect-chassis</code>.
- </li>
-
- <li>
- A priority-150 logical flow with match
- <code>outport == <var>GW</var> &amp;&amp;
- eth.dst == 00:00:00:00:00:00</code> has actions
- <code>outport = <var>CR</var>; next;</code>, where
- <var>GW</var> is the logical router distributed gateway
- port and <var>CR</var> is the <code>chassisredirect</code>
- port representing the instance of the logical router
- distributed gateway port on the
- <code>redirect-chassis</code>.
- </li>
-
- <li>
- For each NAT rule in the OVN Northbound database that can
- be handled in a distributed manner, a priority-100 logical
- flow with match <code>ip4.src == <var>B</var> &amp;&amp;
- outport == <var>GW</var></code>, where <var>GW</var> is
- the logical router distributed gateway port, with actions
- <code>next;</code>.
- </li>
-
- <li>
- A priority-50 logical flow with match
- <code>outport == <var>GW</var></code> has actions
- <code>outport = <var>CR</var>; next;</code>, where
- <var>GW</var> is the logical router distributed gateway
- port and <var>CR</var> is the <code>chassisredirect</code>
- port representing the instance of the logical router
- distributed gateway port on the
- <code>redirect-chassis</code>.
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <h3>Ingress Table 12: ARP Request</h3>
-
- <p>
- In the common case where the Ethernet destination has been resolved, this
- table outputs the packet. Otherwise, it composes and sends an ARP or
- IPv6 Neighbor Solicitation request. It holds the following flows:
- </p>
-
- <ul>
- <li>
- <p>
- Unknown MAC address. A priority-100 flow for IPv4 packets with match
- <code>eth.dst == 00:00:00:00:00:00</code> has the following actions:
- </p>
-
- <pre>
-arp {
- eth.dst = ff:ff:ff:ff:ff:ff;
- arp.spa = reg1;
- arp.tpa = reg0;
- arp.op = 1; /* ARP request. */
- output;
-};
- </pre>
-
- <p>
- Unknown MAC address. For each IPv6 static route associated with the
- router with the nexthop IP: <var>G</var>, a priority-200 flow
- for IPv6 packets with match
- <code>eth.dst == 00:00:00:00:00:00 &amp;&amp;
- xxreg0 == <var>G</var></code>
- with the following actions is added:
- </p>
-
- <pre>
-nd_ns {
- eth.dst = <var>E</var>;
- ip6.dst = <var>I</var>
- nd.target = <var>G</var>;
- output;
-};
- </pre>
-
- <p>
- Where <var>E</var> is the multicast mac derived from the Gateway IP,
- <var>I</var> is the solicited-node multicast address corresponding
- to the target address <var>G</var>.
- </p>
-
- <p>
- Unknown MAC address. A priority-100 flow for IPv6 packets with match
- <code>eth.dst == 00:00:00:00:00:00</code> has the following actions:
- </p>
-
- <pre>
-nd_ns {
- nd.target = xxreg0;
- output;
-};
- </pre>
-
- <p>
- (Ingress table <code>IP Routing</code> initialized <code>reg1</code>
- with the IP address owned by <code>outport</code> and
- <code>(xx)reg0</code> with the next-hop IP address)
- </p>
-
- <p>
- The IP packet that triggers the ARP/IPv6 NS request is dropped.
- </p>
- </li>
-
- <li>
- Known MAC address. A priority-0 flow with match <code>1</code> has
- actions <code>output;</code>.
- </li>
- </ul>
-
- <h3>Egress Table 0: UNDNAT</h3>
-
- <p>
- This is for already established connections' reverse traffic.
- i.e., DNAT has already been done in ingress pipeline and now the
- packet has entered the egress pipeline as part of a reply. For
- NAT on a distributed router, it is unDNATted here. For Gateway
- routers, the unDNAT processing is carried out in the ingress DNAT
- table.
- </p>
-
- <ul>
- <li>
- <p>
- For all the configured load balancing rules for a router with gateway
- port in <code>OVN_Northbound</code> database that includes an IPv4
- address <code>VIP</code>, for every backend IPv4 address <var>B</var>
- defined for the <code>VIP</code> a priority-120 flow is programmed on
- <code>redirect-chassis</code> that matches
- <code>ip &amp;&amp; ip4.src == <var>B</var> &amp;&amp;
- outport == <var>GW</var></code>, where <var>GW</var> is the logical
- router gateway port with an action <code>ct_dnat;</code>. If the
- backend IPv4 address <var>B</var> is also configured with L4 port
- <var>PORT</var> of protocol <var>P</var>, then the
- match also includes <code>P.src</code> == <var>PORT</var>. These
- flows are not added for load balancers with IPv6 <var>VIPs</var>.
- </p>
-
- <p>
- If the router is configured to force SNAT any load-balanced packets,
- above action will be replaced by
- <code>flags.force_snat_for_lb = 1; ct_dnat;</code>.
- </p>
- </li>
-
- <li>
- <p>
- For each configuration in the OVN Northbound database that asks
- to change the destination IP address of a packet from an IP
- address of <var>A</var> to <var>B</var>, a priority-100 flow
- matches <code>ip &amp;&amp; ip4.src == <var>B</var>
- &amp;&amp; outport == <var>GW</var></code>, where <var>GW</var>
- is the logical router gateway port, with an action
- <code>ct_dnat;</code>.
- </p>
-
- <p>
- If the NAT rule cannot be handled in a distributed manner, then
- the priority-100 flow above is only programmed on the
- <code>redirect-chassis</code>.
- </p>
-
- <p>
- If the NAT rule can be handled in a distributed manner, then
- there is an additional action
- <code>eth.src = <var>EA</var>;</code>, where <var>EA</var>
- is the ethernet address associated with the IP address
- <var>A</var> in the NAT rule. This allows upstream MAC
- learning to point to the correct chassis.
- </p>
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <h3>Egress Table 1: SNAT</h3>
-
- <p>
- Packets that are configured to be SNATed get their source IP address
- changed based on the configuration in the OVN Northbound database.
- </p>
-
- <p>Egress Table 1: SNAT on Gateway Routers</p>
-
- <ul>
- <li>
- <p>
- If the Gateway router in the OVN Northbound database has been
- configured to force SNAT a packet (that has been previously DNATted)
- to <var>B</var>, a priority-100 flow matches
- <code>flags.force_snat_for_dnat == 1 &amp;&amp; ip</code> with an
- action <code>ct_snat(<var>B</var>);</code>.
- </p>
- <p>
- If the Gateway router in the OVN Northbound database has been
- configured to force SNAT a packet (that has been previously
- load-balanced) to <var>B</var>, a priority-100 flow matches
- <code>flags.force_snat_for_lb == 1 &amp;&amp; ip</code> with an
- action <code>ct_snat(<var>B</var>);</code>.
- </p>
- <p>
- For each configuration in the OVN Northbound database, that asks
- to change the source IP address of a packet from an IP address of
- <var>A</var> or to change the source IP address of a packet that
- belongs to network <var>A</var> to <var>B</var>, a flow matches
- <code>ip &amp;&amp; ip4.src == <var>A</var></code> with an action
- <code>ct_snat(<var>B</var>);</code>. The priority of the flow
- is calculated based on the mask of <var>A</var>, with matches
- having larger masks getting higher priorities.
- </p>
- <p>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </p>
- </li>
- </ul>
-
- <p>Egress Table 1: SNAT on Distributed Routers</p>
-
- <ul>
- <li>
- <p>
- For each configuration in the OVN Northbound database, that asks
- to change the source IP address of a packet from an IP address of
- <var>A</var> or to change the source IP address of a packet that
- belongs to network <var>A</var> to <var>B</var>, a flow matches
- <code>ip &amp;&amp; ip4.src == <var>A</var> &amp;&amp;
- outport == <var>GW</var></code>, where <var>GW</var> is the
- logical router gateway port, with an action
- <code>ct_snat(<var>B</var>);</code>. The priority of the flow
- is calculated based on the mask of <var>A</var>, with matches
- having larger masks getting higher priorities.
- </p>
-
- <p>
- If the NAT rule cannot be handled in a distributed manner, then
- the flow above is only programmed on the
- <code>redirect-chassis</code> increasing flow priority by 128 in
- order to be run first
- </p>
-
- <p>
- If the NAT rule can be handled in a distributed manner, then
- there is an additional action
- <code>eth.src = <var>EA</var>;</code>, where <var>EA</var>
- is the ethernet address associated with the IP address
- <var>A</var> in the NAT rule. This allows upstream MAC
- learning to point to the correct chassis.
- </p>
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <h3>Egress Table 2: Egress Loopback</h3>
-
- <p>
- For distributed logical routers where one of the logical router
- ports specifies a <code>redirect-chassis</code>.
- </p>
-
- <p>
- Earlier in the ingress pipeline, some east-west traffic was
- redirected to the <code>chassisredirect</code> port, based on
- flows in the <code>UNSNAT</code> and <code>DNAT</code> ingress
- tables setting the <code>REGBIT_NAT_REDIRECT</code> flag, which
- then triggered a match to a flow in the
- <code>Gateway Redirect</code> ingress table. The intention was
- not to actually send traffic out the distributed gateway port
- instance on the <code>redirect-chassis</code>. This traffic was
- sent to the distributed gateway port instance in order for DNAT
- and/or SNAT processing to be applied.
- </p>
-
- <p>
- While UNDNAT and SNAT processing have already occurred by this
- point, this traffic needs to be forced through egress loopback on
- this distributed gateway port instance, in order for UNSNAT and
- DNAT processing to be applied, and also for IP routing and ARP
- resolution after all of the NAT processing, so that the packet can
- be forwarded to the destination.
- </p>
-
- <p>
- This table has the following flows:
- </p>
-
- <ul>
- <li>
- <p>
- For each <code>dnat_and_snat</code> NAT rule couple in the
- OVN Northbound database on a distributed router,
- a priority-200 logical with match
- <code>ip4.dst == <var>external_ip0</var> &amp;&amp;
- ip4.src == <var>external_ip1</var></code>, has action
- <code>next;</code>
- </p>
-
- <p>
- For each NAT rule in the OVN Northbound database on a
- distributed router, a priority-100 logical flow with match
- <code>ip4.dst == <var>E</var> &amp;&amp;
- outport == <var>GW</var></code>, where <var>E</var> is the
- external IP address specified in the NAT rule, and <var>GW</var>
- is the logical router distributed gateway port, with the
- following actions:
- </p>
-
- <pre>
-clone {
- ct_clear;
- inport = outport;
- outport = "";
- flags = 0;
- flags.loopback = 1;
- reg0 = 0;
- reg1 = 0;
- ...
- reg9 = 0;
- REGBIT_EGRESS_LOOPBACK = 1;
- next(pipeline=ingress, table=0);
-};
- </pre>
-
- <p>
- <code>flags.loopback</code> is set since in_port is unchanged
- and the packet may return back to that port after NAT processing.
- <code>REGBIT_EGRESS_LOOPBACK</code> is set to indicate that
- egress loopback has occurred, in order to skip the source IP
- address check against the router address.
- </p>
- </li>
-
- <li>
- A priority-0 logical flow with match <code>1</code> has actions
- <code>next;</code>.
- </li>
- </ul>
-
- <h3>Egress Table 3: Delivery</h3>
-
- <p>
- Packets that reach this table are ready for delivery. It contains
- priority-100 logical flows that match packets on each enabled logical
- router port, with action <code>output;</code>.
- </p>
-
-</manpage>
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
deleted file mode 100644
index 77415ddd3..000000000
--- a/ovn/northd/ovn-northd.c
+++ /dev/null
@@ -1,9446 +0,0 @@
-/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "bitmap.h"
-#include "command-line.h"
-#include "daemon.h"
-#include "dirs.h"
-#include "openvswitch/dynamic-string.h"
-#include "fatal-signal.h"
-#include "hash.h"
-#include "openvswitch/hmap.h"
-#include "openvswitch/json.h"
-#include "ovn/lex.h"
-#include "ovn/lib/chassis-index.h"
-#include "ovn/lib/ip-mcast-index.h"
-#include "ovn/lib/mcast-group-index.h"
-#include "ovn/lib/ovn-l7.h"
-#include "ovn/lib/ovn-nb-idl.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn/lib/ovn-util.h"
-#include "ovn/actions.h"
-#include "ovn/logical-fields.h"
-#include "packets.h"
-#include "openvswitch/poll-loop.h"
-#include "smap.h"
-#include "sset.h"
-#include "svec.h"
-#include "stream.h"
-#include "stream-ssl.h"
-#include "unixctl.h"
-#include "util.h"
-#include "uuid.h"
-#include "openvswitch/vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(ovn_northd);
-
-static unixctl_cb_func ovn_northd_exit;
-
-struct northd_context {
- struct ovsdb_idl *ovnnb_idl;
- struct ovsdb_idl *ovnsb_idl;
- struct ovsdb_idl_txn *ovnnb_txn;
- struct ovsdb_idl_txn *ovnsb_txn;
- struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
- struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp;
- struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
-};
-
-static const char *ovnnb_db;
-static const char *ovnsb_db;
-static const char *unixctl_path;
-
-#define MAC_ADDR_SPACE 0xffffff
-
-/* MAC address management (macam) table of "struct eth_addr"s, that holds the
- * MAC addresses allocated by the OVN ipam module. */
-static struct hmap macam = HMAP_INITIALIZER(&macam);
-static struct eth_addr mac_prefix;
-
-static bool controller_event_en;
-
-#define MAX_OVN_TAGS 4096
-
-/* Pipeline stages. */
-
-/* The two pipelines in an OVN logical flow table. */
-enum ovn_pipeline {
- P_IN, /* Ingress pipeline. */
- P_OUT /* Egress pipeline. */
-};
-
-/* The two purposes for which ovn-northd uses OVN logical datapaths. */
-enum ovn_datapath_type {
- DP_SWITCH, /* OVN logical switch. */
- DP_ROUTER /* OVN logical router. */
-};
-
-/* Returns an "enum ovn_stage" built from the arguments.
- *
- * (It's better to use ovn_stage_build() for type-safety reasons, but inline
- * functions can't be used in enums or switch cases.) */
-#define OVN_STAGE_BUILD(DP_TYPE, PIPELINE, TABLE) \
- (((DP_TYPE) << 9) | ((PIPELINE) << 8) | (TABLE))
-
-/* A stage within an OVN logical switch or router.
- *
- * An "enum ovn_stage" indicates whether the stage is part of a logical switch
- * or router, whether the stage is part of the ingress or egress pipeline, and
- * the table within that pipeline. The first three components are combined to
- * form the stage's full name, e.g. S_SWITCH_IN_PORT_SEC_L2,
- * S_ROUTER_OUT_DELIVERY. */
-enum ovn_stage {
-#define PIPELINE_STAGES \
- /* Logical switch ingress stages. */ \
- PIPELINE_STAGE(SWITCH, IN, PORT_SEC_L2, 0, "ls_in_port_sec_l2") \
- PIPELINE_STAGE(SWITCH, IN, PORT_SEC_IP, 1, "ls_in_port_sec_ip") \
- PIPELINE_STAGE(SWITCH, IN, PORT_SEC_ND, 2, "ls_in_port_sec_nd") \
- PIPELINE_STAGE(SWITCH, IN, PRE_ACL, 3, "ls_in_pre_acl") \
- PIPELINE_STAGE(SWITCH, IN, PRE_LB, 4, "ls_in_pre_lb") \
- PIPELINE_STAGE(SWITCH, IN, PRE_STATEFUL, 5, "ls_in_pre_stateful") \
- PIPELINE_STAGE(SWITCH, IN, ACL, 6, "ls_in_acl") \
- PIPELINE_STAGE(SWITCH, IN, QOS_MARK, 7, "ls_in_qos_mark") \
- PIPELINE_STAGE(SWITCH, IN, QOS_METER, 8, "ls_in_qos_meter") \
- PIPELINE_STAGE(SWITCH, IN, LB, 9, "ls_in_lb") \
- PIPELINE_STAGE(SWITCH, IN, STATEFUL, 10, "ls_in_stateful") \
- PIPELINE_STAGE(SWITCH, IN, ARP_ND_RSP, 11, "ls_in_arp_rsp") \
- PIPELINE_STAGE(SWITCH, IN, DHCP_OPTIONS, 12, "ls_in_dhcp_options") \
- PIPELINE_STAGE(SWITCH, IN, DHCP_RESPONSE, 13, "ls_in_dhcp_response") \
- PIPELINE_STAGE(SWITCH, IN, DNS_LOOKUP, 14, "ls_in_dns_lookup") \
- PIPELINE_STAGE(SWITCH, IN, DNS_RESPONSE, 15, "ls_in_dns_response") \
- PIPELINE_STAGE(SWITCH, IN, EXTERNAL_PORT, 16, "ls_in_external_port") \
- PIPELINE_STAGE(SWITCH, IN, L2_LKUP, 17, "ls_in_l2_lkup") \
- \
- /* Logical switch egress stages. */ \
- PIPELINE_STAGE(SWITCH, OUT, PRE_LB, 0, "ls_out_pre_lb") \
- PIPELINE_STAGE(SWITCH, OUT, PRE_ACL, 1, "ls_out_pre_acl") \
- PIPELINE_STAGE(SWITCH, OUT, PRE_STATEFUL, 2, "ls_out_pre_stateful") \
- PIPELINE_STAGE(SWITCH, OUT, LB, 3, "ls_out_lb") \
- PIPELINE_STAGE(SWITCH, OUT, ACL, 4, "ls_out_acl") \
- PIPELINE_STAGE(SWITCH, OUT, QOS_MARK, 5, "ls_out_qos_mark") \
- PIPELINE_STAGE(SWITCH, OUT, QOS_METER, 6, "ls_out_qos_meter") \
- PIPELINE_STAGE(SWITCH, OUT, STATEFUL, 7, "ls_out_stateful") \
- PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_IP, 8, "ls_out_port_sec_ip") \
- PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2, 9, "ls_out_port_sec_l2") \
- \
- /* Logical router ingress stages. */ \
- PIPELINE_STAGE(ROUTER, IN, ADMISSION, 0, "lr_in_admission") \
- PIPELINE_STAGE(ROUTER, IN, IP_INPUT, 1, "lr_in_ip_input") \
- PIPELINE_STAGE(ROUTER, IN, DEFRAG, 2, "lr_in_defrag") \
- PIPELINE_STAGE(ROUTER, IN, UNSNAT, 3, "lr_in_unsnat") \
- PIPELINE_STAGE(ROUTER, IN, DNAT, 4, "lr_in_dnat") \
- PIPELINE_STAGE(ROUTER, IN, ND_RA_OPTIONS, 5, "lr_in_nd_ra_options") \
- PIPELINE_STAGE(ROUTER, IN, ND_RA_RESPONSE, 6, "lr_in_nd_ra_response") \
- PIPELINE_STAGE(ROUTER, IN, IP_ROUTING, 7, "lr_in_ip_routing") \
- PIPELINE_STAGE(ROUTER, IN, POLICY, 8, "lr_in_policy") \
- PIPELINE_STAGE(ROUTER, IN, ARP_RESOLVE, 9, "lr_in_arp_resolve") \
- PIPELINE_STAGE(ROUTER, IN, CHK_PKT_LEN , 10, "lr_in_chk_pkt_len") \
- PIPELINE_STAGE(ROUTER, IN, LARGER_PKTS, 11,"lr_in_larger_pkts") \
- PIPELINE_STAGE(ROUTER, IN, GW_REDIRECT, 12, "lr_in_gw_redirect") \
- PIPELINE_STAGE(ROUTER, IN, ARP_REQUEST, 13, "lr_in_arp_request") \
- \
- /* Logical router egress stages. */ \
- PIPELINE_STAGE(ROUTER, OUT, UNDNAT, 0, "lr_out_undnat") \
- PIPELINE_STAGE(ROUTER, OUT, SNAT, 1, "lr_out_snat") \
- PIPELINE_STAGE(ROUTER, OUT, EGR_LOOP, 2, "lr_out_egr_loop") \
- PIPELINE_STAGE(ROUTER, OUT, DELIVERY, 3, "lr_out_delivery")
-
-#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
- S_##DP_TYPE##_##PIPELINE##_##STAGE \
- = OVN_STAGE_BUILD(DP_##DP_TYPE, P_##PIPELINE, TABLE),
- PIPELINE_STAGES
-#undef PIPELINE_STAGE
-};
-
-/* Due to various hard-coded priorities need to implement ACLs, the
- * northbound database supports a smaller range of ACL priorities than
- * are available to logical flows. This value is added to an ACL
- * priority to determine the ACL's logical flow priority. */
-#define OVN_ACL_PRI_OFFSET 1000
-
-/* Register definitions specific to switches. */
-#define REGBIT_CONNTRACK_DEFRAG "reg0[0]"
-#define REGBIT_CONNTRACK_COMMIT "reg0[1]"
-#define REGBIT_CONNTRACK_NAT "reg0[2]"
-#define REGBIT_DHCP_OPTS_RESULT "reg0[3]"
-#define REGBIT_DNS_LOOKUP_RESULT "reg0[4]"
-#define REGBIT_ND_RA_OPTS_RESULT "reg0[5]"
-
-/* Register definitions for switches and routers. */
-#define REGBIT_NAT_REDIRECT "reg9[0]"
-/* Indicate that this packet has been recirculated using egress
- * loopback. This allows certain checks to be bypassed, such as a
- * logical router dropping packets with source IP address equals
- * one of the logical router's own IP addresses. */
-#define REGBIT_EGRESS_LOOPBACK "reg9[1]"
-#define REGBIT_DISTRIBUTED_NAT "reg9[2]"
-/* Register to store the result of check_pkt_larger action. */
-#define REGBIT_PKT_LARGER "reg9[3]"
-
-/* Returns an "enum ovn_stage" built from the arguments. */
-static enum ovn_stage
-ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline,
- uint8_t table)
-{
- return OVN_STAGE_BUILD(dp_type, pipeline, table);
-}
-
-/* Returns the pipeline to which 'stage' belongs. */
-static enum ovn_pipeline
-ovn_stage_get_pipeline(enum ovn_stage stage)
-{
- return (stage >> 8) & 1;
-}
-
-/* Returns the pipeline name to which 'stage' belongs. */
-static const char *
-ovn_stage_get_pipeline_name(enum ovn_stage stage)
-{
- return ovn_stage_get_pipeline(stage) == P_IN ? "ingress" : "egress";
-}
-
-/* Returns the table to which 'stage' belongs. */
-static uint8_t
-ovn_stage_get_table(enum ovn_stage stage)
-{
- return stage & 0xff;
-}
-
-/* Returns a string name for 'stage'. */
-static const char *
-ovn_stage_to_str(enum ovn_stage stage)
-{
- switch (stage) {
-#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
- case S_##DP_TYPE##_##PIPELINE##_##STAGE: return NAME;
- PIPELINE_STAGES
-#undef PIPELINE_STAGE
- default: return "<unknown>";
- }
-}
-
-/* Returns the type of the datapath to which a flow with the given 'stage' may
- * be added. */
-static enum ovn_datapath_type
-ovn_stage_to_datapath_type(enum ovn_stage stage)
-{
- switch (stage) {
-#define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME) \
- case S_##DP_TYPE##_##PIPELINE##_##STAGE: return DP_##DP_TYPE;
- PIPELINE_STAGES
-#undef PIPELINE_STAGE
- default: OVS_NOT_REACHED();
- }
-}
-
-static void
-usage(void)
-{
- printf("\
-%s: OVN northbound management daemon\n\
-usage: %s [OPTIONS]\n\
-\n\
-Options:\n\
- --ovnnb-db=DATABASE connect to ovn-nb database at DATABASE\n\
- (default: %s)\n\
- --ovnsb-db=DATABASE connect to ovn-sb database at DATABASE\n\
- (default: %s)\n\
- --unixctl=SOCKET override default control socket name\n\
- -h, --help display this help message\n\
- -o, --options list available options\n\
- -V, --version display version information\n\
-", program_name, program_name, default_nb_db(), default_sb_db());
- daemon_usage();
- vlog_usage();
- stream_usage("database", true, true, false);
-}
-
-struct tnlid_node {
- struct hmap_node hmap_node;
- uint32_t tnlid;
-};
-
-static void
-destroy_tnlids(struct hmap *tnlids)
-{
- struct tnlid_node *node;
- HMAP_FOR_EACH_POP (node, hmap_node, tnlids) {
- free(node);
- }
- hmap_destroy(tnlids);
-}
-
-static void
-add_tnlid(struct hmap *set, uint32_t tnlid)
-{
- struct tnlid_node *node = xmalloc(sizeof *node);
- hmap_insert(set, &node->hmap_node, hash_int(tnlid, 0));
- node->tnlid = tnlid;
-}
-
-static bool
-tnlid_in_use(const struct hmap *set, uint32_t tnlid)
-{
- const struct tnlid_node *node;
- HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_int(tnlid, 0), set) {
- if (node->tnlid == tnlid) {
- return true;
- }
- }
- return false;
-}
-
-static uint32_t
-next_tnlid(uint32_t tnlid, uint32_t min, uint32_t max)
-{
- return tnlid + 1 <= max ? tnlid + 1 : min;
-}
-
-static uint32_t
-allocate_tnlid(struct hmap *set, const char *name, uint32_t min, uint32_t max,
- uint32_t *hint)
-{
- for (uint32_t tnlid = next_tnlid(*hint, min, max); tnlid != *hint;
- tnlid = next_tnlid(tnlid, min, max)) {
- if (!tnlid_in_use(set, tnlid)) {
- add_tnlid(set, tnlid);
- *hint = tnlid;
- return tnlid;
- }
- }
-
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "all %s tunnel ids exhausted", name);
- return 0;
-}
-
-struct ovn_chassis_qdisc_queues {
- struct hmap_node key_node;
- uint32_t queue_id;
- struct uuid chassis_uuid;
-};
-
-static uint32_t
-hash_chassis_queue(const struct uuid *chassis_uuid, uint32_t queue_id)
-{
- return hash_2words(uuid_hash(chassis_uuid), queue_id);
-}
-
-static void
-destroy_chassis_queues(struct hmap *set)
-{
- struct ovn_chassis_qdisc_queues *node;
- HMAP_FOR_EACH_POP (node, key_node, set) {
- free(node);
- }
- hmap_destroy(set);
-}
-
-static void
-add_chassis_queue(struct hmap *set, struct uuid *chassis_uuid,
- uint32_t queue_id)
-{
- struct ovn_chassis_qdisc_queues *node = xmalloc(sizeof *node);
- node->queue_id = queue_id;
- node->chassis_uuid = *chassis_uuid;
- hmap_insert(set, &node->key_node,
- hash_chassis_queue(chassis_uuid, queue_id));
-}
-
-static bool
-chassis_queueid_in_use(const struct hmap *set, struct uuid *chassis_uuid,
- uint32_t queue_id)
-{
- const struct ovn_chassis_qdisc_queues *node;
- HMAP_FOR_EACH_WITH_HASH (node, key_node,
- hash_chassis_queue(chassis_uuid, queue_id), set) {
- if (uuid_equals(chassis_uuid, &node->chassis_uuid)
- && node->queue_id == queue_id) {
- return true;
- }
- }
- return false;
-}
-
-static uint32_t
-allocate_chassis_queueid(struct hmap *set, struct sbrec_chassis *chassis)
-{
- for (uint32_t queue_id = QDISC_MIN_QUEUE_ID + 1;
- queue_id <= QDISC_MAX_QUEUE_ID;
- queue_id++) {
- if (!chassis_queueid_in_use(set, &chassis->header_.uuid, queue_id)) {
- add_chassis_queue(set, &chassis->header_.uuid, queue_id);
- return queue_id;
- }
- }
-
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "all %s queue ids exhausted", chassis->name);
- return 0;
-}
-
-static void
-free_chassis_queueid(struct hmap *set, struct sbrec_chassis *chassis,
- uint32_t queue_id)
-{
- const struct uuid *chassis_uuid = &chassis->header_.uuid;
- struct ovn_chassis_qdisc_queues *node;
- HMAP_FOR_EACH_WITH_HASH (node, key_node,
- hash_chassis_queue(chassis_uuid, queue_id), set) {
- if (uuid_equals(chassis_uuid, &node->chassis_uuid)
- && node->queue_id == queue_id) {
- hmap_remove(set, &node->key_node);
- free(node);
- break;
- }
- }
-}
-
-static inline bool
-port_has_qos_params(const struct smap *opts)
-{
- return (smap_get(opts, "qos_max_rate") ||
- smap_get(opts, "qos_burst"));
-}
-
-
-struct ipam_info {
- uint32_t start_ipv4;
- size_t total_ipv4s;
- unsigned long *allocated_ipv4s; /* A bitmap of allocated IPv4s */
- bool ipv6_prefix_set;
- struct in6_addr ipv6_prefix;
- bool mac_only;
-};
-
-#define OVN_MIN_MULTICAST 32768
-#define OVN_MAX_MULTICAST OVN_MCAST_FLOOD_TUNNEL_KEY
-BUILD_ASSERT_DECL(OVN_MIN_MULTICAST < OVN_MAX_MULTICAST);
-
-#define OVN_MIN_IP_MULTICAST OVN_MIN_MULTICAST
-#define OVN_MAX_IP_MULTICAST (OVN_MCAST_UNKNOWN_TUNNEL_KEY - 1)
-BUILD_ASSERT_DECL(OVN_MAX_IP_MULTICAST >= OVN_MIN_MULTICAST);
-
-/*
- * Multicast snooping and querier per datapath configuration.
- */
-struct mcast_info {
- bool enabled;
- bool querier;
- bool flood_unregistered;
-
- int64_t table_size;
- int64_t idle_timeout;
- int64_t query_interval;
- char *eth_src;
- char *ipv4_src;
- int64_t query_max_response;
-
- struct hmap group_tnlids;
- uint32_t group_tnlid_hint;
- uint32_t active_flows;
-};
-
-static uint32_t
-ovn_mcast_group_allocate_key(struct mcast_info *mcast_info)
-{
- return allocate_tnlid(&mcast_info->group_tnlids, "multicast group",
- OVN_MIN_IP_MULTICAST, OVN_MAX_IP_MULTICAST,
- &mcast_info->group_tnlid_hint);
-}
-
-/* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
- * sb->external_ids:logical-switch. */
-struct ovn_datapath {
- struct hmap_node key_node; /* Index on 'key'. */
- struct uuid key; /* (nbs/nbr)->header_.uuid. */
-
- const struct nbrec_logical_switch *nbs; /* May be NULL. */
- const struct nbrec_logical_router *nbr; /* May be NULL. */
- const struct sbrec_datapath_binding *sb; /* May be NULL. */
-
- struct ovs_list list; /* In list of similar records. */
-
- /* Logical switch data. */
- struct ovn_port **router_ports;
- size_t n_router_ports;
-
- struct hmap port_tnlids;
- uint32_t port_key_hint;
-
- bool has_unknown;
-
- /* IPAM data. */
- struct ipam_info ipam_info;
-
- /* Multicast data. */
- struct mcast_info mcast_info;
-
- /* OVN northd only needs to know about the logical router gateway port for
- * NAT on a distributed router. This "distributed gateway port" is
- * populated only when there is a "redirect-chassis" specified for one of
- * the ports on the logical router. Otherwise this will be NULL. */
- struct ovn_port *l3dgw_port;
- /* The "derived" OVN port representing the instance of l3dgw_port on
- * the "redirect-chassis". */
- struct ovn_port *l3redirect_port;
- struct ovn_port *localnet_port;
-
- struct ovs_list lr_list; /* In list of logical router datapaths. */
- /* The logical router group to which this datapath belongs.
- * Valid only if it is logical router datapath. NULL otherwise. */
- struct lrouter_group *lr_group;
-
- /* Port groups related to the datapath, used only when nbs is NOT NULL. */
- struct hmap nb_pgs;
-};
-
-/* A group of logical router datapaths which are connected - either
- * directly or indirectly.
- * Each logical router can belong to only one group. */
-struct lrouter_group {
- struct ovn_datapath **router_dps;
- int n_router_dps;
- /* Set of ha_chassis_groups which are associated with the router dps. */
- struct sset ha_chassis_groups;
-};
-
-struct macam_node {
- struct hmap_node hmap_node;
- struct eth_addr mac_addr; /* Allocated MAC address. */
-};
-
-static void
-cleanup_macam(struct hmap *macam_)
-{
- struct macam_node *node;
- HMAP_FOR_EACH_POP (node, hmap_node, macam_) {
- free(node);
- }
-}
-
-static struct ovn_datapath *
-ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
- const struct nbrec_logical_switch *nbs,
- const struct nbrec_logical_router *nbr,
- const struct sbrec_datapath_binding *sb)
-{
- struct ovn_datapath *od = xzalloc(sizeof *od);
- od->key = *key;
- od->sb = sb;
- od->nbs = nbs;
- od->nbr = nbr;
- hmap_init(&od->port_tnlids);
- hmap_init(&od->nb_pgs);
- od->port_key_hint = 0;
- hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
- od->lr_group = NULL;
- return od;
-}
-
-static void ovn_ls_port_group_destroy(struct hmap *nb_pgs);
-
-static void
-ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
-{
- if (od) {
- /* Don't remove od->list. It is used within build_datapaths() as a
- * private list and once we've exited that function it is not safe to
- * use it. */
- hmap_remove(datapaths, &od->key_node);
- destroy_tnlids(&od->port_tnlids);
- bitmap_free(od->ipam_info.allocated_ipv4s);
- free(od->router_ports);
- ovn_ls_port_group_destroy(&od->nb_pgs);
-
- if (od->nbs) {
- free(od->mcast_info.eth_src);
- free(od->mcast_info.ipv4_src);
- destroy_tnlids(&od->mcast_info.group_tnlids);
- }
-
- free(od);
- }
-}
-
-/* Returns 'od''s datapath type. */
-static enum ovn_datapath_type
-ovn_datapath_get_type(const struct ovn_datapath *od)
-{
- return od->nbs ? DP_SWITCH : DP_ROUTER;
-}
-
-static struct ovn_datapath *
-ovn_datapath_find(struct hmap *datapaths, const struct uuid *uuid)
-{
- struct ovn_datapath *od;
-
- HMAP_FOR_EACH_WITH_HASH (od, key_node, uuid_hash(uuid), datapaths) {
- if (uuid_equals(uuid, &od->key)) {
- return od;
- }
- }
- return NULL;
-}
-
-static struct ovn_datapath *
-ovn_datapath_from_sbrec(struct hmap *datapaths,
- const struct sbrec_datapath_binding *sb)
-{
- struct uuid key;
-
- if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
- !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
- return NULL;
- }
- return ovn_datapath_find(datapaths, &key);
-}
-
-static bool
-lrouter_is_enabled(const struct nbrec_logical_router *lrouter)
-{
- return !lrouter->enabled || *lrouter->enabled;
-}
-
-static void
-init_ipam_info_for_datapath(struct ovn_datapath *od)
-{
- if (!od->nbs) {
- return;
- }
-
- const char *subnet_str = smap_get(&od->nbs->other_config, "subnet");
- const char *ipv6_prefix = smap_get(&od->nbs->other_config, "ipv6_prefix");
-
- if (ipv6_prefix) {
- od->ipam_info.ipv6_prefix_set = ipv6_parse(
- ipv6_prefix, &od->ipam_info.ipv6_prefix);
- }
-
- if (!subnet_str) {
- if (!ipv6_prefix) {
- od->ipam_info.mac_only = smap_get_bool(&od->nbs->other_config,
- "mac_only", false);
- }
- return;
- }
-
- ovs_be32 subnet, mask;
- char *error = ip_parse_masked(subnet_str, &subnet, &mask);
- if (error || mask == OVS_BE32_MAX || !ip_is_cidr(mask)) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'subnet' %s", subnet_str);
- free(error);
- return;
- }
-
- od->ipam_info.start_ipv4 = ntohl(subnet) + 1;
- od->ipam_info.total_ipv4s = ~ntohl(mask);
- od->ipam_info.allocated_ipv4s =
- bitmap_allocate(od->ipam_info.total_ipv4s);
-
- /* Mark first IP as taken */
- bitmap_set1(od->ipam_info.allocated_ipv4s, 0);
-
- /* Check if there are any reserver IPs (list) to be excluded from IPAM */
- const char *exclude_ip_list = smap_get(&od->nbs->other_config,
- "exclude_ips");
- if (!exclude_ip_list) {
- return;
- }
-
- struct lexer lexer;
- lexer_init(&lexer, exclude_ip_list);
- /* exclude_ip_list could be in the format -
- * "10.0.0.4 10.0.0.10 10.0.0.20..10.0.0.50 10.0.0.100..10.0.0.110".
- */
- lexer_get(&lexer);
- while (lexer.token.type != LEX_T_END) {
- if (lexer.token.type != LEX_T_INTEGER) {
- lexer_syntax_error(&lexer, "expecting address");
- break;
- }
- uint32_t start = ntohl(lexer.token.value.ipv4);
- lexer_get(&lexer);
-
- uint32_t end = start + 1;
- if (lexer_match(&lexer, LEX_T_ELLIPSIS)) {
- if (lexer.token.type != LEX_T_INTEGER) {
- lexer_syntax_error(&lexer, "expecting address range");
- break;
- }
- end = ntohl(lexer.token.value.ipv4) + 1;
- lexer_get(&lexer);
- }
-
- /* Clamp start...end to fit the subnet. */
- start = MAX(od->ipam_info.start_ipv4, start);
- end = MIN(od->ipam_info.start_ipv4 + od->ipam_info.total_ipv4s, end);
- if (end > start) {
- bitmap_set_multiple(od->ipam_info.allocated_ipv4s,
- start - od->ipam_info.start_ipv4,
- end - start, 1);
- } else {
- lexer_error(&lexer, "excluded addresses not in subnet");
- }
- }
- if (lexer.error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "logical switch "UUID_FMT": bad exclude_ips (%s)",
- UUID_ARGS(&od->key), lexer.error);
- }
- lexer_destroy(&lexer);
-}
-
-static void
-init_mcast_info_for_datapath(struct ovn_datapath *od)
-{
- if (!od->nbs) {
- return;
- }
-
- struct mcast_info *mcast_info = &od->mcast_info;
-
- mcast_info->enabled =
- smap_get_bool(&od->nbs->other_config, "mcast_snoop", false);
- mcast_info->querier =
- smap_get_bool(&od->nbs->other_config, "mcast_querier", true);
- mcast_info->flood_unregistered =
- smap_get_bool(&od->nbs->other_config, "mcast_flood_unregistered",
- false);
-
- mcast_info->table_size =
- smap_get_ullong(&od->nbs->other_config, "mcast_table_size",
- OVN_MCAST_DEFAULT_MAX_ENTRIES);
-
- uint32_t idle_timeout =
- smap_get_ullong(&od->nbs->other_config, "mcast_idle_timeout",
- OVN_MCAST_DEFAULT_IDLE_TIMEOUT_S);
- if (idle_timeout < OVN_MCAST_MIN_IDLE_TIMEOUT_S) {
- idle_timeout = OVN_MCAST_MIN_IDLE_TIMEOUT_S;
- } else if (idle_timeout > OVN_MCAST_MAX_IDLE_TIMEOUT_S) {
- idle_timeout = OVN_MCAST_MAX_IDLE_TIMEOUT_S;
- }
- mcast_info->idle_timeout = idle_timeout;
-
- uint32_t query_interval =
- smap_get_ullong(&od->nbs->other_config, "mcast_query_interval",
- mcast_info->idle_timeout / 2);
- if (query_interval < OVN_MCAST_MIN_QUERY_INTERVAL_S) {
- query_interval = OVN_MCAST_MIN_QUERY_INTERVAL_S;
- } else if (query_interval > OVN_MCAST_MAX_QUERY_INTERVAL_S) {
- query_interval = OVN_MCAST_MAX_QUERY_INTERVAL_S;
- }
- mcast_info->query_interval = query_interval;
-
- mcast_info->eth_src =
- nullable_xstrdup(smap_get(&od->nbs->other_config, "mcast_eth_src"));
- mcast_info->ipv4_src =
- nullable_xstrdup(smap_get(&od->nbs->other_config, "mcast_ip4_src"));
-
- mcast_info->query_max_response =
- smap_get_ullong(&od->nbs->other_config, "mcast_query_max_response",
- OVN_MCAST_DEFAULT_QUERY_MAX_RESPONSE_S);
-
- hmap_init(&mcast_info->group_tnlids);
- mcast_info->group_tnlid_hint = OVN_MIN_IP_MULTICAST;
- mcast_info->active_flows = 0;
-}
-
-static void
-store_mcast_info_for_datapath(const struct sbrec_ip_multicast *sb,
- struct ovn_datapath *od)
-{
- struct mcast_info *mcast_info = &od->mcast_info;
-
- sbrec_ip_multicast_set_datapath(sb, od->sb);
- sbrec_ip_multicast_set_enabled(sb, &mcast_info->enabled, 1);
- sbrec_ip_multicast_set_querier(sb, &mcast_info->querier, 1);
- sbrec_ip_multicast_set_table_size(sb, &mcast_info->table_size, 1);
- sbrec_ip_multicast_set_idle_timeout(sb, &mcast_info->idle_timeout, 1);
- sbrec_ip_multicast_set_query_interval(sb,
- &mcast_info->query_interval, 1);
- sbrec_ip_multicast_set_query_max_resp(sb,
- &mcast_info->query_max_response, 1);
-
- if (mcast_info->eth_src) {
- sbrec_ip_multicast_set_eth_src(sb, mcast_info->eth_src);
- }
-
- if (mcast_info->ipv4_src) {
- sbrec_ip_multicast_set_ip4_src(sb, mcast_info->ipv4_src);
- }
-}
-
-static void
-ovn_datapath_update_external_ids(struct ovn_datapath *od)
-{
- /* Get the logical-switch or logical-router UUID to set in
- * external-ids. */
- char uuid_s[UUID_LEN + 1];
- sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
- const char *key = od->nbs ? "logical-switch" : "logical-router";
-
- /* Get names to set in external-ids. */
- const char *name = od->nbs ? od->nbs->name : od->nbr->name;
- const char *name2 = (od->nbs
- ? smap_get(&od->nbs->external_ids,
- "neutron:network_name")
- : smap_get(&od->nbr->external_ids,
- "neutron:router_name"));
-
- /* Set external-ids. */
- struct smap ids = SMAP_INITIALIZER(&ids);
- smap_add(&ids, key, uuid_s);
- smap_add(&ids, "name", name);
- if (name2 && name2[0]) {
- smap_add(&ids, "name2", name2);
- }
- sbrec_datapath_binding_set_external_ids(od->sb, &ids);
- smap_destroy(&ids);
-}
-
-static void
-join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
- struct ovs_list *sb_only, struct ovs_list *nb_only,
- struct ovs_list *both, struct ovs_list *lr_list)
-{
- ovs_list_init(sb_only);
- ovs_list_init(nb_only);
- ovs_list_init(both);
-
- const struct sbrec_datapath_binding *sb, *sb_next;
- SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
- struct uuid key;
- if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
- !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
- ovsdb_idl_txn_add_comment(
- ctx->ovnsb_txn,
- "deleting Datapath_Binding "UUID_FMT" that lacks "
- "external-ids:logical-switch and "
- "external-ids:logical-router",
- UUID_ARGS(&sb->header_.uuid));
- sbrec_datapath_binding_delete(sb);
- continue;
- }
-
- if (ovn_datapath_find(datapaths, &key)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_INFO_RL(
- &rl, "deleting Datapath_Binding "UUID_FMT" with "
- "duplicate external-ids:logical-switch/router "UUID_FMT,
- UUID_ARGS(&sb->header_.uuid), UUID_ARGS(&key));
- sbrec_datapath_binding_delete(sb);
- continue;
- }
-
- struct ovn_datapath *od = ovn_datapath_create(datapaths, &key,
- NULL, NULL, sb);
- ovs_list_push_back(sb_only, &od->list);
- }
-
- const struct nbrec_logical_switch *nbs;
- NBREC_LOGICAL_SWITCH_FOR_EACH (nbs, ctx->ovnnb_idl) {
- struct ovn_datapath *od = ovn_datapath_find(datapaths,
- &nbs->header_.uuid);
- if (od) {
- od->nbs = nbs;
- ovs_list_remove(&od->list);
- ovs_list_push_back(both, &od->list);
- ovn_datapath_update_external_ids(od);
- } else {
- od = ovn_datapath_create(datapaths, &nbs->header_.uuid,
- nbs, NULL, NULL);
- ovs_list_push_back(nb_only, &od->list);
- }
-
- init_ipam_info_for_datapath(od);
- init_mcast_info_for_datapath(od);
- }
-
- const struct nbrec_logical_router *nbr;
- NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) {
- if (!lrouter_is_enabled(nbr)) {
- continue;
- }
-
- struct ovn_datapath *od = ovn_datapath_find(datapaths,
- &nbr->header_.uuid);
- if (od) {
- if (!od->nbs) {
- od->nbr = nbr;
- ovs_list_remove(&od->list);
- ovs_list_push_back(both, &od->list);
- ovn_datapath_update_external_ids(od);
- } else {
- /* Can't happen! */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl,
- "duplicate UUID "UUID_FMT" in OVN_Northbound",
- UUID_ARGS(&nbr->header_.uuid));
- continue;
- }
- } else {
- od = ovn_datapath_create(datapaths, &nbr->header_.uuid,
- NULL, nbr, NULL);
- ovs_list_push_back(nb_only, &od->list);
- }
- ovs_list_push_back(lr_list, &od->lr_list);
- }
-}
-
-static uint32_t
-ovn_datapath_allocate_key(struct hmap *dp_tnlids)
-{
- static uint32_t hint;
- return allocate_tnlid(dp_tnlids, "datapath", 1, (1u << 24) - 1, &hint);
-}
-
-/* Updates the southbound Datapath_Binding table so that it contains the
- * logical switches and routers specified by the northbound database.
- *
- * Initializes 'datapaths' to contain a "struct ovn_datapath" for every logical
- * switch and router. */
-static void
-build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
- struct ovs_list *lr_list)
-{
- struct ovs_list sb_only, nb_only, both;
-
- join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both, lr_list);
-
- if (!ovs_list_is_empty(&nb_only)) {
- /* First index the in-use datapath tunnel IDs. */
- struct hmap dp_tnlids = HMAP_INITIALIZER(&dp_tnlids);
- struct ovn_datapath *od;
- LIST_FOR_EACH (od, list, &both) {
- add_tnlid(&dp_tnlids, od->sb->tunnel_key);
- }
-
- /* Add southbound record for each unmatched northbound record. */
- LIST_FOR_EACH (od, list, &nb_only) {
- uint16_t tunnel_key = ovn_datapath_allocate_key(&dp_tnlids);
- if (!tunnel_key) {
- break;
- }
-
- od->sb = sbrec_datapath_binding_insert(ctx->ovnsb_txn);
- ovn_datapath_update_external_ids(od);
- sbrec_datapath_binding_set_tunnel_key(od->sb, tunnel_key);
- }
- destroy_tnlids(&dp_tnlids);
- }
-
- /* Delete southbound records without northbound matches. */
- struct ovn_datapath *od, *next;
- LIST_FOR_EACH_SAFE (od, next, list, &sb_only) {
- ovs_list_remove(&od->list);
- sbrec_datapath_binding_delete(od->sb);
- ovn_datapath_destroy(datapaths, od);
- }
-}
-
-struct ovn_port {
- struct hmap_node key_node; /* Index on 'key'. */
- char *key; /* nbs->name, nbr->name, sb->logical_port. */
- char *json_key; /* 'key', quoted for use in JSON. */
-
- const struct sbrec_port_binding *sb; /* May be NULL. */
-
- /* Logical switch port data. */
- const struct nbrec_logical_switch_port *nbsp; /* May be NULL. */
-
- struct lport_addresses *lsp_addrs; /* Logical switch port addresses. */
- unsigned int n_lsp_addrs;
-
- struct lport_addresses *ps_addrs; /* Port security addresses. */
- unsigned int n_ps_addrs;
-
- /* Logical router port data. */
- const struct nbrec_logical_router_port *nbrp; /* May be NULL. */
-
- struct lport_addresses lrp_networks;
-
- bool derived; /* Indicates whether this is an additional port
- * derived from nbsp or nbrp. */
-
- /* The port's peer:
- *
- * - A switch port S of type "router" has a router port R as a peer,
- * and R in turn has S has its peer.
- *
- * - Two connected logical router ports have each other as peer. */
- struct ovn_port *peer;
-
- struct ovn_datapath *od;
-
- struct ovs_list list; /* In list of similar records. */
-};
-
-static struct ovn_port *
-ovn_port_create(struct hmap *ports, const char *key,
- const struct nbrec_logical_switch_port *nbsp,
- const struct nbrec_logical_router_port *nbrp,
- const struct sbrec_port_binding *sb)
-{
- struct ovn_port *op = xzalloc(sizeof *op);
-
- struct ds json_key = DS_EMPTY_INITIALIZER;
- json_string_escape(key, &json_key);
- op->json_key = ds_steal_cstr(&json_key);
-
- op->key = xstrdup(key);
- op->sb = sb;
- op->nbsp = nbsp;
- op->nbrp = nbrp;
- op->derived = false;
- hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
- return op;
-}
-
-static void
-ovn_port_destroy(struct hmap *ports, struct ovn_port *port)
-{
- if (port) {
- /* Don't remove port->list. It is used within build_ports() as a
- * private list and once we've exited that function it is not safe to
- * use it. */
- hmap_remove(ports, &port->key_node);
-
- for (int i = 0; i < port->n_lsp_addrs; i++) {
- destroy_lport_addresses(&port->lsp_addrs[i]);
- }
- free(port->lsp_addrs);
-
- for (int i = 0; i < port->n_ps_addrs; i++) {
- destroy_lport_addresses(&port->ps_addrs[i]);
- }
- free(port->ps_addrs);
-
- destroy_lport_addresses(&port->lrp_networks);
- free(port->json_key);
- free(port->key);
- free(port);
- }
-}
-
-static struct ovn_port *
-ovn_port_find(const struct hmap *ports, const char *name)
-{
- struct ovn_port *op;
-
- HMAP_FOR_EACH_WITH_HASH (op, key_node, hash_string(name, 0), ports) {
- if (!strcmp(op->key, name)) {
- return op;
- }
- }
- return NULL;
-}
-
-static uint32_t
-ovn_port_allocate_key(struct ovn_datapath *od)
-{
- return allocate_tnlid(&od->port_tnlids, "port",
- 1, (1u << 15) - 1, &od->port_key_hint);
-}
-
-static char *
-chassis_redirect_name(const char *port_name)
-{
- return xasprintf("cr-%s", port_name);
-}
-
-static bool
-ipam_is_duplicate_mac(struct eth_addr *ea, uint64_t mac64, bool warn)
-{
- struct macam_node *macam_node;
- HMAP_FOR_EACH_WITH_HASH (macam_node, hmap_node, hash_uint64(mac64),
- &macam) {
- if (eth_addr_equals(*ea, macam_node->mac_addr)) {
- if (warn) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Duplicate MAC set: "ETH_ADDR_FMT,
- ETH_ADDR_ARGS(macam_node->mac_addr));
- }
- return true;
- }
- }
- return false;
-}
-
-static void
-ipam_insert_mac(struct eth_addr *ea, bool check)
-{
- if (!ea) {
- return;
- }
-
- uint64_t mac64 = eth_addr_to_uint64(*ea);
- uint64_t prefix = eth_addr_to_uint64(mac_prefix);
-
- /* If the new MAC was not assigned by this address management system or
- * check is true and the new MAC is a duplicate, do not insert it into the
- * macam hmap. */
- if (((mac64 ^ prefix) >> 24)
- || (check && ipam_is_duplicate_mac(ea, mac64, true))) {
- return;
- }
-
- struct macam_node *new_macam_node = xmalloc(sizeof *new_macam_node);
- new_macam_node->mac_addr = *ea;
- hmap_insert(&macam, &new_macam_node->hmap_node, hash_uint64(mac64));
-}
-
-static void
-ipam_insert_ip(struct ovn_datapath *od, uint32_t ip)
-{
- if (!od || !od->ipam_info.allocated_ipv4s) {
- return;
- }
-
- if (ip >= od->ipam_info.start_ipv4 &&
- ip < (od->ipam_info.start_ipv4 + od->ipam_info.total_ipv4s)) {
- if (bitmap_is_set(od->ipam_info.allocated_ipv4s,
- ip - od->ipam_info.start_ipv4)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Duplicate IP set on switch %s: "IP_FMT,
- od->nbs->name, IP_ARGS(htonl(ip)));
- }
- bitmap_set1(od->ipam_info.allocated_ipv4s,
- ip - od->ipam_info.start_ipv4);
- }
-}
-
-static void
-ipam_insert_lsp_addresses(struct ovn_datapath *od, struct ovn_port *op,
- char *address)
-{
- if (!od || !op || !address || !strcmp(address, "unknown")
- || !strcmp(address, "router") || is_dynamic_lsp_address(address)) {
- return;
- }
-
- struct lport_addresses laddrs;
- if (!extract_lsp_addresses(address, &laddrs)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Extract addresses failed.");
- return;
- }
- ipam_insert_mac(&laddrs.ea, true);
-
- /* IP is only added to IPAM if the switch's subnet option
- * is set, whereas MAC is always added to MACAM. */
- if (!od->ipam_info.allocated_ipv4s) {
- destroy_lport_addresses(&laddrs);
- return;
- }
-
- for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
- uint32_t ip = ntohl(laddrs.ipv4_addrs[j].addr);
- ipam_insert_ip(od, ip);
- }
-
- destroy_lport_addresses(&laddrs);
-}
-
-static void
-ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op)
-{
- if (!od || !op) {
- return;
- }
-
- if (op->nbsp) {
- /* Add all the port's addresses to address data structures. */
- for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
- ipam_insert_lsp_addresses(od, op, op->nbsp->addresses[i]);
- }
- } else if (op->nbrp) {
- struct lport_addresses lrp_networks;
- if (!extract_lrp_networks(op->nbrp, &lrp_networks)) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Extract addresses failed.");
- return;
- }
- ipam_insert_mac(&lrp_networks.ea, true);
-
- if (!op->peer || !op->peer->nbsp || !op->peer->od || !op->peer->od->nbs
- || !smap_get(&op->peer->od->nbs->other_config, "subnet")) {
- destroy_lport_addresses(&lrp_networks);
- return;
- }
-
- for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) {
- uint32_t ip = ntohl(lrp_networks.ipv4_addrs[i].addr);
- ipam_insert_ip(op->peer->od, ip);
- }
-
- destroy_lport_addresses(&lrp_networks);
- }
-}
-
-static uint64_t
-ipam_get_unused_mac(ovs_be32 ip)
-{
- uint32_t mac_addr_suffix, i, base_addr = ntohl(ip) & MAC_ADDR_SPACE;
- struct eth_addr mac;
- uint64_t mac64;
-
- for (i = 0; i < MAC_ADDR_SPACE - 1; i++) {
- /* The tentative MAC's suffix will be in the interval (1, 0xfffffe). */
- mac_addr_suffix = ((base_addr + i) % (MAC_ADDR_SPACE - 1)) + 1;
- mac64 = eth_addr_to_uint64(mac_prefix) | mac_addr_suffix;
- eth_addr_from_uint64(mac64, &mac);
- if (!ipam_is_duplicate_mac(&mac, mac64, true)) {
- break;
- }
- }
-
- if (i == MAC_ADDR_SPACE) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "MAC address space exhausted.");
- mac64 = 0;
- }
-
- return mac64;
-}
-
-static uint32_t
-ipam_get_unused_ip(struct ovn_datapath *od)
-{
- if (!od || !od->ipam_info.allocated_ipv4s) {
- return 0;
- }
-
- size_t new_ip_index = bitmap_scan(od->ipam_info.allocated_ipv4s, 0, 0,
- od->ipam_info.total_ipv4s - 1);
- if (new_ip_index == od->ipam_info.total_ipv4s - 1) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL( &rl, "Subnet address space has been exhausted.");
- return 0;
- }
-
- return od->ipam_info.start_ipv4 + new_ip_index;
-}
-
-enum dynamic_update_type {
- NONE, /* No change to the address */
- REMOVE, /* Address is no longer dynamic */
- STATIC, /* Use static address (MAC only) */
- DYNAMIC, /* Assign a new dynamic address */
-};
-
-struct dynamic_address_update {
- struct ovs_list node; /* In build_ipam()'s list of updates. */
-
- struct ovn_datapath *od;
- struct ovn_port *op;
-
- struct lport_addresses current_addresses;
- struct eth_addr static_mac;
- ovs_be32 static_ip;
- struct in6_addr static_ipv6;
- enum dynamic_update_type mac;
- enum dynamic_update_type ipv4;
- enum dynamic_update_type ipv6;
-};
-
-static enum dynamic_update_type
-dynamic_mac_changed(const char *lsp_addresses,
- struct dynamic_address_update *update)
-{
- struct eth_addr ea;
-
- if (ovs_scan(lsp_addresses, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
- if (eth_addr_equals(ea, update->current_addresses.ea)) {
- return NONE;
- } else {
- /* MAC is still static, but it has changed */
- update->static_mac = ea;
- return STATIC;
- }
- }
-
- uint64_t mac64 = eth_addr_to_uint64(update->current_addresses.ea);
- uint64_t prefix = eth_addr_to_uint64(mac_prefix);
-
- if ((mac64 ^ prefix) >> 24) {
- return DYNAMIC;
- } else {
- return NONE;
- }
-}
-
-static enum dynamic_update_type
-dynamic_ip4_changed(const char *lsp_addrs,
- struct dynamic_address_update *update)
-{
- const struct ipam_info *ipam = &update->op->od->ipam_info;
- const struct lport_addresses *cur_addresses = &update->current_addresses;
- bool dynamic_ip4 = ipam->allocated_ipv4s != NULL;
-
- if (!dynamic_ip4) {
- if (update->current_addresses.n_ipv4_addrs) {
- return REMOVE;
- } else {
- return NONE;
- }
- }
-
- if (!cur_addresses->n_ipv4_addrs) {
- /* IPv4 was previously static but now is dynamic */
- return DYNAMIC;
- }
-
- uint32_t ip4 = ntohl(cur_addresses->ipv4_addrs[0].addr);
- if (ip4 < ipam->start_ipv4) {
- return DYNAMIC;
- }
-
- uint32_t index = ip4 - ipam->start_ipv4;
- if (index > ipam->total_ipv4s ||
- bitmap_is_set(ipam->allocated_ipv4s, index)) {
- /* Previously assigned dynamic IPv4 address can no longer be used.
- * It's either outside the subnet, conflicts with an excluded IP,
- * or conflicts with a statically-assigned address on the switch
- */
- return DYNAMIC;
- } else {
- char ipv6_s[IPV6_SCAN_LEN + 1];
- ovs_be32 new_ip;
- int n = 0;
-
- if ((ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT"%n",
- IP_SCAN_ARGS(&new_ip), &n)
- && lsp_addrs[n] == '\0') ||
- (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT" "IPV6_SCAN_FMT"%n",
- IP_SCAN_ARGS(&new_ip), ipv6_s, &n)
- && lsp_addrs[n] == '\0')) {
- index = ntohl(new_ip) - ipam->start_ipv4;
- if (ntohl(new_ip) < ipam->start_ipv4 ||
- index > ipam->total_ipv4s ||
- bitmap_is_set(ipam->allocated_ipv4s, index)) {
- /* new static ip is not valid */
- return DYNAMIC;
- } else if (cur_addresses->ipv4_addrs[0].addr != new_ip) {
- update->ipv4 = STATIC;
- update->static_ip = new_ip;
- return STATIC;
- }
- }
- return NONE;
- }
-}
-
-static enum dynamic_update_type
-dynamic_ip6_changed(const char *lsp_addrs,
- struct dynamic_address_update *update)
-{
- bool dynamic_ip6 = update->op->od->ipam_info.ipv6_prefix_set;
- struct eth_addr ea;
-
- if (!dynamic_ip6) {
- if (update->current_addresses.n_ipv6_addrs) {
- /* IPv6 was dynamic but now is not */
- return REMOVE;
- } else {
- /* IPv6 has never been dynamic */
- return NONE;
- }
- }
-
- if (!update->current_addresses.n_ipv6_addrs ||
- ovs_scan(lsp_addrs, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
- /* IPv6 was previously static but now is dynamic */
- return DYNAMIC;
- }
-
- const struct lport_addresses *cur_addresses;
- char ipv6_s[IPV6_SCAN_LEN + 1];
- ovs_be32 new_ip;
- int n = 0;
-
- if ((ovs_scan(lsp_addrs, "dynamic "IPV6_SCAN_FMT"%n",
- ipv6_s, &n) && lsp_addrs[n] == '\0') ||
- (ovs_scan(lsp_addrs, "dynamic "IP_SCAN_FMT" "IPV6_SCAN_FMT"%n",
- IP_SCAN_ARGS(&new_ip), ipv6_s, &n)
- && lsp_addrs[n] == '\0')) {
- struct in6_addr ipv6;
-
- if (!ipv6_parse(ipv6_s, &ipv6)) {
- return DYNAMIC;
- }
-
- struct in6_addr masked = ipv6_addr_bitand(&ipv6,
- &update->op->od->ipam_info.ipv6_prefix);
- if (!IN6_ARE_ADDR_EQUAL(&masked,
- &update->op->od->ipam_info.ipv6_prefix)) {
- return DYNAMIC;
- }
-
- cur_addresses = &update->current_addresses;
-
- if (!IN6_ARE_ADDR_EQUAL(&cur_addresses->ipv6_addrs[0].addr,
- &ipv6)) {
- update->static_ipv6 = ipv6;
- return STATIC;
- }
- } else if (update->mac != NONE) {
- return DYNAMIC;
- }
-
- return NONE;
-}
-
-/* Check previously assigned dynamic addresses for validity. This will
- * check if the assigned addresses need to change.
- *
- * Returns true if any changes to dynamic addresses are required
- */
-static bool
-dynamic_addresses_check_for_updates(const char *lsp_addrs,
- struct dynamic_address_update *update)
-{
- update->mac = dynamic_mac_changed(lsp_addrs, update);
- update->ipv4 = dynamic_ip4_changed(lsp_addrs, update);
- update->ipv6 = dynamic_ip6_changed(lsp_addrs, update);
- if (update->mac == NONE &&
- update->ipv4 == NONE &&
- update->ipv6 == NONE) {
- return false;
- } else {
- return true;
- }
-}
-
-/* For addresses that do not need to be updated, go ahead and insert them
- * into IPAM. This way, their addresses will be claimed and cannot be assigned
- * elsewhere later.
- */
-static void
-update_unchanged_dynamic_addresses(struct dynamic_address_update *update)
-{
- if (update->mac == NONE) {
- ipam_insert_mac(&update->current_addresses.ea, false);
- }
- if (update->ipv4 == NONE && update->current_addresses.n_ipv4_addrs) {
- ipam_insert_ip(update->op->od,
- ntohl(update->current_addresses.ipv4_addrs[0].addr));
- }
-}
-
-static void
-set_lsp_dynamic_addresses(const char *dynamic_addresses, struct ovn_port *op)
-{
- extract_lsp_addresses(dynamic_addresses, &op->lsp_addrs[op->n_lsp_addrs]);
- op->n_lsp_addrs++;
-}
-
-/* Determines which components (MAC, IPv4, and IPv6) of dynamic
- * addresses need to be assigned. This is used exclusively for
- * ports that do not have dynamic addresses already assigned.
- */
-static void
-set_dynamic_updates(const char *addrspec,
- struct dynamic_address_update *update)
-{
- bool has_ipv4 = false, has_ipv6 = false;
- char ipv6_s[IPV6_SCAN_LEN + 1];
- struct eth_addr mac;
- ovs_be32 ip;
- int n = 0;
- if (ovs_scan(addrspec, ETH_ADDR_SCAN_FMT" dynamic%n",
- ETH_ADDR_SCAN_ARGS(mac), &n)
- && addrspec[n] == '\0') {
- update->mac = STATIC;
- update->static_mac = mac;
- } else {
- update->mac = DYNAMIC;
- }
-
- if ((ovs_scan(addrspec, "dynamic "IP_SCAN_FMT"%n",
- IP_SCAN_ARGS(&ip), &n) && addrspec[n] == '\0')) {
- has_ipv4 = true;
- } else if ((ovs_scan(addrspec, "dynamic "IPV6_SCAN_FMT"%n",
- ipv6_s, &n) && addrspec[n] == '\0')) {
- has_ipv6 = true;
- } else if ((ovs_scan(addrspec, "dynamic "IP_SCAN_FMT" "IPV6_SCAN_FMT"%n",
- IP_SCAN_ARGS(&ip), ipv6_s, &n)
- && addrspec[n] == '\0')) {
- has_ipv4 = has_ipv6 = true;
- }
-
- if (has_ipv4) {
- update->ipv4 = STATIC;
- update->static_ip = ip;
- } else if (update->op->od->ipam_info.allocated_ipv4s) {
- update->ipv4 = DYNAMIC;
- } else {
- update->ipv4 = NONE;
- }
-
- if (has_ipv6 && ipv6_parse(ipv6_s, &update->static_ipv6)) {
- update->ipv6 = STATIC;
- } else if (update->op->od->ipam_info.ipv6_prefix_set) {
- update->ipv6 = DYNAMIC;
- } else {
- update->ipv6 = NONE;
- }
-}
-
-static void
-update_dynamic_addresses(struct dynamic_address_update *update)
-{
- ovs_be32 ip4 = 0;
- switch (update->ipv4) {
- case NONE:
- if (update->current_addresses.n_ipv4_addrs) {
- ip4 = update->current_addresses.ipv4_addrs[0].addr;
- }
- break;
- case REMOVE:
- break;
- case STATIC:
- ip4 = update->static_ip;
- break;
- case DYNAMIC:
- ip4 = htonl(ipam_get_unused_ip(update->od));
- }
-
- struct eth_addr mac;
- switch (update->mac) {
- case NONE:
- mac = update->current_addresses.ea;
- break;
- case REMOVE:
- OVS_NOT_REACHED();
- case STATIC:
- mac = update->static_mac;
- break;
- case DYNAMIC:
- eth_addr_from_uint64(ipam_get_unused_mac(ip4), &mac);
- break;
- }
-
- struct in6_addr ip6 = in6addr_any;
- switch (update->ipv6) {
- case NONE:
- if (update->current_addresses.n_ipv6_addrs) {
- ip6 = update->current_addresses.ipv6_addrs[0].addr;
- }
- break;
- case REMOVE:
- break;
- case STATIC:
- ip6 = update->static_ipv6;
- break;
- case DYNAMIC:
- in6_generate_eui64(mac, &update->od->ipam_info.ipv6_prefix, &ip6);
- break;
- }
-
- struct ds new_addr = DS_EMPTY_INITIALIZER;
- ds_put_format(&new_addr, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
- ipam_insert_mac(&mac, true);
-
- if (ip4) {
- ipam_insert_ip(update->od, ntohl(ip4));
- ds_put_format(&new_addr, " "IP_FMT, IP_ARGS(ip4));
- }
- if (!IN6_ARE_ADDR_EQUAL(&ip6, &in6addr_any)) {
- char ip6_s[INET6_ADDRSTRLEN + 1];
- ipv6_string_mapped(ip6_s, &ip6);
- ds_put_format(&new_addr, " %s", ip6_s);
- }
- nbrec_logical_switch_port_set_dynamic_addresses(update->op->nbsp,
- ds_cstr(&new_addr));
- set_lsp_dynamic_addresses(ds_cstr(&new_addr), update->op);
- ds_destroy(&new_addr);
-}
-
-static void
-build_ipam(struct hmap *datapaths, struct hmap *ports)
-{
- /* IPAM generally stands for IP address management. In non-virtualized
- * world, MAC addresses come with the hardware. But, with virtualized
- * workloads, they need to be assigned and managed. This function
- * does both IP address management (ipam) and MAC address management
- * (macam). */
-
- /* If the switch's other_config:subnet is set, allocate new addresses for
- * ports that have the "dynamic" keyword in their addresses column. */
- struct ovn_datapath *od;
- struct ovs_list updates;
-
- ovs_list_init(&updates);
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- for (size_t i = 0; i < od->nbs->n_ports; i++) {
- const struct nbrec_logical_switch_port *nbsp = od->nbs->ports[i];
-
- if (!od->ipam_info.allocated_ipv4s &&
- !od->ipam_info.ipv6_prefix_set &&
- !od->ipam_info.mac_only) {
- if (nbsp->dynamic_addresses) {
- nbrec_logical_switch_port_set_dynamic_addresses(nbsp,
- NULL);
- }
- continue;
- }
-
- struct ovn_port *op = ovn_port_find(ports, nbsp->name);
- if (!op || op->nbsp != nbsp || op->peer) {
- /* Do not allocate addresses for logical switch ports that
- * have a peer. */
- continue;
- }
-
- int num_dynamic_addresses = 0;
- for (size_t j = 0; j < nbsp->n_addresses; j++) {
- if (!is_dynamic_lsp_address(nbsp->addresses[j])) {
- continue;
- }
- if (num_dynamic_addresses) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "More than one dynamic address "
- "configured for logical switch port '%s'",
- nbsp->name);
- continue;
- }
- num_dynamic_addresses++;
- struct dynamic_address_update *update
- = xzalloc(sizeof *update);
- update->op = op;
- update->od = od;
- if (nbsp->dynamic_addresses) {
- bool any_changed;
- extract_lsp_addresses(nbsp->dynamic_addresses,
- &update->current_addresses);
- any_changed = dynamic_addresses_check_for_updates(
- nbsp->addresses[j], update);
- update_unchanged_dynamic_addresses(update);
- if (any_changed) {
- ovs_list_push_back(&updates, &update->node);
- } else {
- /* No changes to dynamic addresses */
- set_lsp_dynamic_addresses(nbsp->dynamic_addresses, op);
- destroy_lport_addresses(&update->current_addresses);
- free(update);
- }
- } else {
- set_dynamic_updates(nbsp->addresses[j], update);
- ovs_list_push_back(&updates, &update->node);
- }
- }
-
- if (!num_dynamic_addresses && nbsp->dynamic_addresses) {
- nbrec_logical_switch_port_set_dynamic_addresses(nbsp, NULL);
- }
- }
-
- }
-
- /* After retaining all unchanged dynamic addresses, now assign
- * new ones.
- */
- struct dynamic_address_update *update;
- LIST_FOR_EACH_POP (update, node, &updates) {
- update_dynamic_addresses(update);
- destroy_lport_addresses(&update->current_addresses);
- free(update);
- }
-}
-
-/* Tag allocation for nested containers.
- *
- * For a logical switch port with 'parent_name' and a request to allocate tags,
- * keeps a track of all allocated tags. */
-struct tag_alloc_node {
- struct hmap_node hmap_node;
- char *parent_name;
- unsigned long *allocated_tags; /* A bitmap to track allocated tags. */
-};
-
-static void
-tag_alloc_destroy(struct hmap *tag_alloc_table)
-{
- struct tag_alloc_node *node;
- HMAP_FOR_EACH_POP (node, hmap_node, tag_alloc_table) {
- bitmap_free(node->allocated_tags);
- free(node->parent_name);
- free(node);
- }
- hmap_destroy(tag_alloc_table);
-}
-
-static struct tag_alloc_node *
-tag_alloc_get_node(struct hmap *tag_alloc_table, const char *parent_name)
-{
- /* If a node for the 'parent_name' exists, return it. */
- struct tag_alloc_node *tag_alloc_node;
- HMAP_FOR_EACH_WITH_HASH (tag_alloc_node, hmap_node,
- hash_string(parent_name, 0),
- tag_alloc_table) {
- if (!strcmp(tag_alloc_node->parent_name, parent_name)) {
- return tag_alloc_node;
- }
- }
-
- /* Create a new node. */
- tag_alloc_node = xmalloc(sizeof *tag_alloc_node);
- tag_alloc_node->parent_name = xstrdup(parent_name);
- tag_alloc_node->allocated_tags = bitmap_allocate(MAX_OVN_TAGS);
- /* Tag 0 is invalid for nested containers. */
- bitmap_set1(tag_alloc_node->allocated_tags, 0);
- hmap_insert(tag_alloc_table, &tag_alloc_node->hmap_node,
- hash_string(parent_name, 0));
-
- return tag_alloc_node;
-}
-
-static void
-tag_alloc_add_existing_tags(struct hmap *tag_alloc_table,
- const struct nbrec_logical_switch_port *nbsp)
-{
- /* Add the tags of already existing nested containers. If there is no
- * 'nbsp->parent_name' or no 'nbsp->tag' set, there is nothing to do. */
- if (!nbsp->parent_name || !nbsp->parent_name[0] || !nbsp->tag) {
- return;
- }
-
- struct tag_alloc_node *tag_alloc_node;
- tag_alloc_node = tag_alloc_get_node(tag_alloc_table, nbsp->parent_name);
- bitmap_set1(tag_alloc_node->allocated_tags, *nbsp->tag);
-}
-
-static void
-tag_alloc_create_new_tag(struct hmap *tag_alloc_table,
- const struct nbrec_logical_switch_port *nbsp)
-{
- if (!nbsp->tag_request) {
- return;
- }
-
- if (nbsp->parent_name && nbsp->parent_name[0]
- && *nbsp->tag_request == 0) {
- /* For nested containers that need allocation, do the allocation. */
-
- if (nbsp->tag) {
- /* This has already been allocated. */
- return;
- }
-
- struct tag_alloc_node *tag_alloc_node;
- int64_t tag;
- tag_alloc_node = tag_alloc_get_node(tag_alloc_table,
- nbsp->parent_name);
- tag = bitmap_scan(tag_alloc_node->allocated_tags, 0, 1, MAX_OVN_TAGS);
- if (tag == MAX_OVN_TAGS) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_ERR_RL(&rl, "out of vlans for logical switch ports with "
- "parent %s", nbsp->parent_name);
- return;
- }
- bitmap_set1(tag_alloc_node->allocated_tags, tag);
- nbrec_logical_switch_port_set_tag(nbsp, &tag, 1);
- } else if (*nbsp->tag_request != 0) {
- /* For everything else, copy the contents of 'tag_request' to 'tag'. */
- nbrec_logical_switch_port_set_tag(nbsp, nbsp->tag_request, 1);
- }
-}
-
-
-static void
-join_logical_ports(struct northd_context *ctx,
- struct hmap *datapaths, struct hmap *ports,
- struct hmap *chassis_qdisc_queues,
- struct hmap *tag_alloc_table, struct ovs_list *sb_only,
- struct ovs_list *nb_only, struct ovs_list *both)
-{
- ovs_list_init(sb_only);
- ovs_list_init(nb_only);
- ovs_list_init(both);
-
- const struct sbrec_port_binding *sb;
- SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
- struct ovn_port *op = ovn_port_create(ports, sb->logical_port,
- NULL, NULL, sb);
- ovs_list_push_back(sb_only, &op->list);
- }
-
- struct ovn_datapath *od;
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (od->nbs) {
- for (size_t i = 0; i < od->nbs->n_ports; i++) {
- const struct nbrec_logical_switch_port *nbsp
- = od->nbs->ports[i];
- struct ovn_port *op = ovn_port_find(ports, nbsp->name);
- if (op) {
- if (op->nbsp || op->nbrp) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "duplicate logical port %s",
- nbsp->name);
- continue;
- }
- op->nbsp = nbsp;
- ovs_list_remove(&op->list);
-
- uint32_t queue_id = smap_get_int(&op->sb->options,
- "qdisc_queue_id", 0);
- if (queue_id && op->sb->chassis) {
- add_chassis_queue(
- chassis_qdisc_queues, &op->sb->chassis->header_.uuid,
- queue_id);
- }
-
- ovs_list_push_back(both, &op->list);
-
- /* This port exists due to a SB binding, but should
- * not have been initialized fully. */
- ovs_assert(!op->n_lsp_addrs && !op->n_ps_addrs);
- } else {
- op = ovn_port_create(ports, nbsp->name, nbsp, NULL, NULL);
- ovs_list_push_back(nb_only, &op->list);
- }
-
- if (!strcmp(nbsp->type, "localnet")) {
- od->localnet_port = op;
- }
-
- op->lsp_addrs
- = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses);
- for (size_t j = 0; j < nbsp->n_addresses; j++) {
- if (!strcmp(nbsp->addresses[j], "unknown")
- || !strcmp(nbsp->addresses[j], "router")) {
- continue;
- }
- if (is_dynamic_lsp_address(nbsp->addresses[j])) {
- continue;
- } else if (!extract_lsp_addresses(nbsp->addresses[j],
- &op->lsp_addrs[op->n_lsp_addrs])) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "invalid syntax '%s' in logical "
- "switch port addresses. No MAC "
- "address found",
- op->nbsp->addresses[j]);
- continue;
- }
- op->n_lsp_addrs++;
- }
-
- op->ps_addrs
- = xmalloc(sizeof *op->ps_addrs * nbsp->n_port_security);
- for (size_t j = 0; j < nbsp->n_port_security; j++) {
- if (!extract_lsp_addresses(nbsp->port_security[j],
- &op->ps_addrs[op->n_ps_addrs])) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_INFO_RL(&rl, "invalid syntax '%s' in port "
- "security. No MAC address found",
- op->nbsp->port_security[j]);
- continue;
- }
- op->n_ps_addrs++;
- }
-
- op->od = od;
- tag_alloc_add_existing_tags(tag_alloc_table, nbsp);
- }
- } else {
- for (size_t i = 0; i < od->nbr->n_ports; i++) {
- const struct nbrec_logical_router_port *nbrp
- = od->nbr->ports[i];
-
- struct lport_addresses lrp_networks;
- if (!extract_lrp_networks(nbrp, &lrp_networks)) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'mac' %s", nbrp->mac);
- continue;
- }
-
- if (!lrp_networks.n_ipv4_addrs && !lrp_networks.n_ipv6_addrs) {
- continue;
- }
-
- struct ovn_port *op = ovn_port_find(ports, nbrp->name);
- if (op) {
- if (op->nbsp || op->nbrp) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "duplicate logical router port %s",
- nbrp->name);
- continue;
- }
- op->nbrp = nbrp;
- ovs_list_remove(&op->list);
- ovs_list_push_back(both, &op->list);
-
- /* This port exists but should not have been
- * initialized fully. */
- ovs_assert(!op->lrp_networks.n_ipv4_addrs
- && !op->lrp_networks.n_ipv6_addrs);
- } else {
- op = ovn_port_create(ports, nbrp->name, NULL, nbrp, NULL);
- ovs_list_push_back(nb_only, &op->list);
- }
-
- op->lrp_networks = lrp_networks;
- op->od = od;
-
- const char *redirect_chassis = smap_get(&op->nbrp->options,
- "redirect-chassis");
- if (op->nbrp->ha_chassis_group || redirect_chassis ||
- op->nbrp->n_gateway_chassis) {
- /* Additional "derived" ovn_port crp represents the
- * instance of op on the "redirect-chassis". */
- const char *gw_chassis = smap_get(&op->od->nbr->options,
- "chassis");
- if (gw_chassis) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Bad configuration: "
- "redirect-chassis configured on port %s "
- "on L3 gateway router", nbrp->name);
- continue;
- }
- if (od->l3dgw_port || od->l3redirect_port) {
- static struct vlog_rate_limit rl
- = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Bad configuration: multiple ports "
- "with redirect-chassis on same logical "
- "router %s", od->nbr->name);
- continue;
- }
-
- char *redirect_name = chassis_redirect_name(nbrp->name);
- struct ovn_port *crp = ovn_port_find(ports, redirect_name);
- if (crp) {
- crp->derived = true;
- crp->nbrp = nbrp;
- ovs_list_remove(&crp->list);
- ovs_list_push_back(both, &crp->list);
- } else {
- crp = ovn_port_create(ports, redirect_name,
- NULL, nbrp, NULL);
- crp->derived = true;
- ovs_list_push_back(nb_only, &crp->list);
- }
- crp->od = od;
- free(redirect_name);
-
- /* Set l3dgw_port and l3redirect_port in od, for later
- * use during flow creation. */
- od->l3dgw_port = op;
- od->l3redirect_port = crp;
- }
- }
- }
- }
-
- /* Connect logical router ports, and logical switch ports of type "router",
- * to their peers. */
- struct ovn_port *op;
- HMAP_FOR_EACH (op, key_node, ports) {
- if (op->nbsp && !strcmp(op->nbsp->type, "router") && !op->derived) {
- const char *peer_name = smap_get(&op->nbsp->options, "router-port");
- if (!peer_name) {
- continue;
- }
-
- struct ovn_port *peer = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbrp) {
- continue;
- }
-
- peer->peer = op;
- op->peer = peer;
- op->od->router_ports = xrealloc(
- op->od->router_ports,
- sizeof *op->od->router_ports * (op->od->n_router_ports + 1));
- op->od->router_ports[op->od->n_router_ports++] = op;
-
- /* Fill op->lsp_addrs for op->nbsp->addresses[] with
- * contents "router", which was skipped in the loop above. */
- for (size_t j = 0; j < op->nbsp->n_addresses; j++) {
- if (!strcmp(op->nbsp->addresses[j], "router")) {
- if (extract_lrp_networks(peer->nbrp,
- &op->lsp_addrs[op->n_lsp_addrs])) {
- op->n_lsp_addrs++;
- }
- break;
- }
- }
- } else if (op->nbrp && op->nbrp->peer && !op->derived) {
- struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
- if (peer) {
- if (peer->nbrp) {
- op->peer = peer;
- } else if (peer->nbsp) {
- /* An ovn_port for a switch port of type "router" does have
- * a router port as its peer (see the case above for
- * "router" ports), but this is set via options:router-port
- * in Logical_Switch_Port and does not involve the
- * Logical_Router_Port's 'peer' column. */
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "Bad configuration: The peer of router "
- "port %s is a switch port", op->key);
- }
- }
- }
- }
-
- /* Wait until all ports have been connected to add to IPAM since
- * it relies on proper peers to be set
- */
- HMAP_FOR_EACH (op, key_node, ports) {
- ipam_add_port_addresses(op->od, op);
- }
-}
-
-static void
-ip_address_and_port_from_lb_key(const char *key, char **ip_address,
- uint16_t *port, int *addr_family);
-
-static void
-get_router_load_balancer_ips(const struct ovn_datapath *od,
- struct sset *all_ips, int *addr_family)
-{
- if (!od->nbr) {
- return;
- }
-
- for (int i = 0; i < od->nbr->n_load_balancer; i++) {
- struct nbrec_load_balancer *lb = od->nbr->load_balancer[i];
- struct smap *vips = &lb->vips;
- struct smap_node *node;
-
- SMAP_FOR_EACH (node, vips) {
- /* node->key contains IP:port or just IP. */
- char *ip_address = NULL;
- uint16_t port;
-
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
- addr_family);
- if (!ip_address) {
- continue;
- }
-
- if (!sset_contains(all_ips, ip_address)) {
- sset_add(all_ips, ip_address);
- }
-
- free(ip_address);
- }
- }
-}
-
-/* Returns an array of strings, each consisting of a MAC address followed
- * by one or more IP addresses, and if the port is a distributed gateway
- * port, followed by 'is_chassis_resident("LPORT_NAME")', where the
- * LPORT_NAME is the name of the L3 redirect port or the name of the
- * logical_port specified in a NAT rule. These strings include the
- * external IP addresses of all NAT rules defined on that router, and all
- * of the IP addresses used in load balancer VIPs defined on that router.
- *
- * The caller must free each of the n returned strings with free(),
- * and must free the returned array when it is no longer needed. */
-static char **
-get_nat_addresses(const struct ovn_port *op, size_t *n)
-{
- size_t n_nats = 0;
- struct eth_addr mac;
- if (!op->nbrp || !op->od || !op->od->nbr
- || (!op->od->nbr->n_nat && !op->od->nbr->n_load_balancer)
- || !eth_addr_from_string(op->nbrp->mac, &mac)) {
- *n = n_nats;
- return NULL;
- }
-
- struct ds c_addresses = DS_EMPTY_INITIALIZER;
- ds_put_format(&c_addresses, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
- bool central_ip_address = false;
-
- char **addresses;
- addresses = xmalloc(sizeof *addresses * (op->od->nbr->n_nat + 1));
-
- /* Get NAT IP addresses. */
- for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
- const struct nbrec_nat *nat = op->od->nbr->nat[i];
- ovs_be32 ip, mask;
-
- char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
- if (error || mask != OVS_BE32_MAX) {
- free(error);
- continue;
- }
-
- /* Determine whether this NAT rule satisfies the conditions for
- * distributed NAT processing. */
- if (op->od->l3redirect_port && !strcmp(nat->type, "dnat_and_snat")
- && nat->logical_port && nat->external_mac) {
- /* Distributed NAT rule. */
- if (eth_addr_from_string(nat->external_mac, &mac)) {
- struct ds address = DS_EMPTY_INITIALIZER;
- ds_put_format(&address, ETH_ADDR_FMT, ETH_ADDR_ARGS(mac));
- ds_put_format(&address, " %s", nat->external_ip);
- ds_put_format(&address, " is_chassis_resident(\"%s\")",
- nat->logical_port);
- addresses[n_nats++] = ds_steal_cstr(&address);
- }
- } else {
- /* Centralized NAT rule, either on gateway router or distributed
- * router.
- * Check if external_ip is same as router ip. If so, then there
- * is no need to add this to the nat_addresses. The router IPs
- * will be added separately. */
- bool is_router_ip = false;
- for (size_t j = 0; j < op->lrp_networks.n_ipv4_addrs; j++) {
- if (!strcmp(nat->external_ip,
- op->lrp_networks.ipv4_addrs[j].addr_s)) {
- is_router_ip = true;
- break;
- }
- }
-
- if (!is_router_ip) {
- ds_put_format(&c_addresses, " %s", nat->external_ip);
- central_ip_address = true;
- }
- }
- }
-
- /* A set to hold all load-balancer vips. */
- struct sset all_ips = SSET_INITIALIZER(&all_ips);
- int addr_family;
- get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
-
- const char *ip_address;
- SSET_FOR_EACH (ip_address, &all_ips) {
- ds_put_format(&c_addresses, " %s", ip_address);
- central_ip_address = true;
- }
- sset_destroy(&all_ips);
-
- if (central_ip_address) {
- /* Gratuitous ARP for centralized NAT rules on distributed gateway
- * ports should be restricted to the "redirect-chassis". */
- if (op->od->l3redirect_port) {
- ds_put_format(&c_addresses, " is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
-
- addresses[n_nats++] = ds_steal_cstr(&c_addresses);
- }
-
- *n = n_nats;
-
- return addresses;
-}
-
-static bool
-sbpb_gw_chassis_needs_update(
- const struct sbrec_port_binding *pb,
- const struct nbrec_logical_router_port *lrp,
- struct ovsdb_idl_index *sbrec_chassis_by_name)
-{
- if (!lrp || !pb) {
- return false;
- }
-
- if (lrp->n_gateway_chassis && !pb->ha_chassis_group) {
- /* If there are gateway chassis in the NB DB, but there is
- * no corresponding HA chassis group in SB DB we need to
- * create the HA chassis group in SB DB for this lrp. */
- return true;
- }
-
- if (strcmp(pb->ha_chassis_group->name, lrp->name)) {
- /* Name doesn't match. */
- return true;
- }
-
- if (lrp->n_gateway_chassis != pb->ha_chassis_group->n_ha_chassis) {
- return true;
- }
-
- for (size_t i = 0; i < lrp->n_gateway_chassis; i++) {
- struct nbrec_gateway_chassis *nbgw_ch = lrp->gateway_chassis[i];
- bool found = false;
- for (size_t j = 0; j < pb->ha_chassis_group->n_ha_chassis; j++) {
- struct sbrec_ha_chassis *sbha_ch =
- pb->ha_chassis_group->ha_chassis[j];
- const char *chassis_name = smap_get(&sbha_ch->external_ids,
- "chassis-name");
- if (!chassis_name) {
- return true;
- }
-
- if (strcmp(chassis_name, nbgw_ch->chassis_name)) {
- continue;
- }
-
- found = true;
-
- if (nbgw_ch->priority != sbha_ch->priority) {
- return true;
- }
-
- if (sbha_ch->chassis &&
- strcmp(nbgw_ch->chassis_name, sbha_ch->chassis->name)) {
- /* sbha_ch->chassis's name is different from the one
- * in sbha_ch->external_ids:chassis-name. */
- return true;
- }
-
- if (!sbha_ch->chassis &&
- chassis_lookup_by_name(sbrec_chassis_by_name,
- nbgw_ch->chassis_name)) {
- /* sbha_ch->chassis is NULL, but the chassis is
- * present in Chassis table. */
- return true;
- }
- }
-
- if (!found) {
- return true;
- }
- }
-
- /* No need to update SB DB. Its in sync. */
- return false;
-}
-
-static struct sbrec_ha_chassis *
-create_sb_ha_chassis(struct northd_context *ctx,
- const struct sbrec_chassis *chassis,
- const char *chassis_name, int priority)
-{
- struct sbrec_ha_chassis *sb_ha_chassis =
- sbrec_ha_chassis_insert(ctx->ovnsb_txn);
- sbrec_ha_chassis_set_chassis(sb_ha_chassis, chassis);
- sbrec_ha_chassis_set_priority(sb_ha_chassis, priority);
- /* Store the chassis_name in external_ids. If the chassis
- * entry doesn't exist in the Chassis table then we can
- * figure out the chassis to which this ha_chassis
- * maps to. */
- const struct smap external_ids =
- SMAP_CONST1(&external_ids, "chassis-name", chassis_name);
- sbrec_ha_chassis_set_external_ids(sb_ha_chassis, &external_ids);
- return sb_ha_chassis;
-}
-
-static bool
-chassis_group_list_changed(
- const struct nbrec_ha_chassis_group *nb_ha_grp,
- const struct sbrec_ha_chassis_group *sb_ha_grp,
- struct ovsdb_idl_index *sbrec_chassis_by_name)
-{
- if (nb_ha_grp->n_ha_chassis != sb_ha_grp->n_ha_chassis) {
- return true;
- }
-
- struct shash nb_ha_chassis_list = SHASH_INITIALIZER(&nb_ha_chassis_list);
- for (size_t i = 0; i < nb_ha_grp->n_ha_chassis; i++) {
- shash_add(&nb_ha_chassis_list,
- nb_ha_grp->ha_chassis[i]->chassis_name,
- nb_ha_grp->ha_chassis[i]);
- }
-
- bool changed = false;
- const struct sbrec_ha_chassis *sb_ha_chassis;
- const struct nbrec_ha_chassis *nb_ha_chassis;
- for (size_t i = 0; i < sb_ha_grp->n_ha_chassis; i++) {
- sb_ha_chassis = sb_ha_grp->ha_chassis[i];
- const char *chassis_name = smap_get(&sb_ha_chassis->external_ids,
- "chassis-name");
-
- if (!chassis_name) {
- changed = true;
- break;
- }
-
- nb_ha_chassis = shash_find_and_delete(&nb_ha_chassis_list,
- chassis_name);
- if (!nb_ha_chassis ||
- nb_ha_chassis->priority != sb_ha_chassis->priority) {
- changed = true;
- break;
- }
-
- if (sb_ha_chassis->chassis &&
- strcmp(sb_ha_chassis->chassis->name, chassis_name)) {
- /* sb_ha_chassis->chassis's name is different from the one
- * in sb_ha_chassis->external_ids:chassis-name. */
- changed = true;
- break;
- }
-
- if (!sb_ha_chassis->chassis &&
- chassis_lookup_by_name(sbrec_chassis_by_name,
- chassis_name)) {
- /* sb_ha_chassis->chassis is NULL, but the chassis is
- * present in Chassis table. */
- changed = true;
- break;
- }
- }
-
- struct shash_node *node, *next;
- SHASH_FOR_EACH_SAFE (node, next, &nb_ha_chassis_list) {
- shash_delete(&nb_ha_chassis_list, node);
- changed = true;
- }
- shash_destroy(&nb_ha_chassis_list);
-
- return changed;
-}
-
-static void
-sync_ha_chassis_group_for_sbpb(struct northd_context *ctx,
- const struct nbrec_ha_chassis_group *nb_ha_grp,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- const struct sbrec_port_binding *pb)
-{
- bool new_sb_chassis_group = false;
- const struct sbrec_ha_chassis_group *sb_ha_grp =
- ha_chassis_group_lookup_by_name(
- ctx->sbrec_ha_chassis_grp_by_name, nb_ha_grp->name);
-
- if (!sb_ha_grp) {
- sb_ha_grp = sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
- sbrec_ha_chassis_group_set_name(sb_ha_grp, nb_ha_grp->name);
- new_sb_chassis_group = true;
- }
-
- if (new_sb_chassis_group ||
- chassis_group_list_changed(nb_ha_grp, sb_ha_grp,
- sbrec_chassis_by_name)) {
- struct sbrec_ha_chassis **sb_ha_chassis = NULL;
- size_t n_ha_chassis = nb_ha_grp->n_ha_chassis;
- sb_ha_chassis = xcalloc(n_ha_chassis, sizeof *sb_ha_chassis);
- for (size_t i = 0; i < nb_ha_grp->n_ha_chassis; i++) {
- const struct nbrec_ha_chassis *nb_ha_chassis
- = nb_ha_grp->ha_chassis[i];
- const struct sbrec_chassis *chassis =
- chassis_lookup_by_name(sbrec_chassis_by_name,
- nb_ha_chassis->chassis_name);
- sb_ha_chassis[i] = sbrec_ha_chassis_insert(ctx->ovnsb_txn);
- /* It's perfectly ok if the chassis is NULL. This could
- * happen when ovn-controller exits and removes its row
- * from the chassis table in OVN SB DB. */
- sbrec_ha_chassis_set_chassis(sb_ha_chassis[i], chassis);
- sbrec_ha_chassis_set_priority(sb_ha_chassis[i],
- nb_ha_chassis->priority);
- const struct smap external_ids =
- SMAP_CONST1(&external_ids, "chassis-name",
- nb_ha_chassis->chassis_name);
- sbrec_ha_chassis_set_external_ids(sb_ha_chassis[i], &external_ids);
- }
- sbrec_ha_chassis_group_set_ha_chassis(sb_ha_grp, sb_ha_chassis,
- n_ha_chassis);
- free(sb_ha_chassis);
- }
-
- sbrec_port_binding_set_ha_chassis_group(pb, sb_ha_grp);
-}
-
-/* This functions translates the gw chassis on the nb database
- * to HA chassis group in the sb database entries.
- */
-static void
-copy_gw_chassis_from_nbrp_to_sbpb(
- struct northd_context *ctx,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- const struct nbrec_logical_router_port *lrp,
- const struct sbrec_port_binding *port_binding)
-{
-
- /* Make use of the new HA chassis group table to support HA
- * for the distributed gateway router port. */
- const struct sbrec_ha_chassis_group *sb_ha_chassis_group =
- ha_chassis_group_lookup_by_name(
- ctx->sbrec_ha_chassis_grp_by_name, lrp->name);
- if (!sb_ha_chassis_group) {
- sb_ha_chassis_group = sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
- sbrec_ha_chassis_group_set_name(sb_ha_chassis_group, lrp->name);
- }
-
- struct sbrec_ha_chassis **sb_ha_chassis = xcalloc(lrp->n_gateway_chassis,
- sizeof *sb_ha_chassis);
- size_t n_sb_ha_ch = 0;
- for (size_t n = 0; n < lrp->n_gateway_chassis; n++) {
- struct nbrec_gateway_chassis *lrp_gwc = lrp->gateway_chassis[n];
- if (!lrp_gwc->chassis_name) {
- continue;
- }
-
- const struct sbrec_chassis *chassis =
- chassis_lookup_by_name(sbrec_chassis_by_name,
- lrp_gwc->chassis_name);
-
- sb_ha_chassis[n_sb_ha_ch] =
- create_sb_ha_chassis(ctx, chassis, lrp_gwc->chassis_name,
- lrp_gwc->priority);
- n_sb_ha_ch++;
- }
-
- sbrec_ha_chassis_group_set_ha_chassis(sb_ha_chassis_group,
- sb_ha_chassis, n_sb_ha_ch);
- sbrec_port_binding_set_ha_chassis_group(port_binding, sb_ha_chassis_group);
- free(sb_ha_chassis);
-}
-
-static void
-ovn_port_update_sbrec(struct northd_context *ctx,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- const struct ovn_port *op,
- struct hmap *chassis_qdisc_queues,
- struct sset *active_ha_chassis_grps)
-{
- sbrec_port_binding_set_datapath(op->sb, op->od->sb);
- if (op->nbrp) {
- /* If the router is for l3 gateway, it resides on a chassis
- * and its port type is "l3gateway". */
- const char *chassis_name = smap_get(&op->od->nbr->options, "chassis");
- if (op->derived) {
- sbrec_port_binding_set_type(op->sb, "chassisredirect");
- } else if (chassis_name) {
- sbrec_port_binding_set_type(op->sb, "l3gateway");
- } else {
- sbrec_port_binding_set_type(op->sb, "patch");
- }
-
- struct smap new;
- smap_init(&new);
- if (op->derived) {
- const char *redirect_chassis = smap_get(&op->nbrp->options,
- "redirect-chassis");
- int n_gw_options_set = 0;
- if (op->nbrp->ha_chassis_group) {
- n_gw_options_set++;
- }
- if (op->nbrp->n_gateway_chassis) {
- n_gw_options_set++;
- }
- if (redirect_chassis) {
- n_gw_options_set++;
- }
- if (n_gw_options_set > 1) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(
- &rl, "Multiple gatway options set for the logical router "
- "port %s. The first preferred option is "
- "ha_chassis_group; the second is gateway_chassis; "
- "and the last is redirect-chassis.", op->nbrp->name);
- }
-
- if (op->nbrp->ha_chassis_group) {
- /* HA Chassis group is set. Ignore 'gateway_chassis'
- * column and redirect-chassis option. */
- sync_ha_chassis_group_for_sbpb(ctx, op->nbrp->ha_chassis_group,
- sbrec_chassis_by_name, op->sb);
- sset_add(active_ha_chassis_grps,
- op->nbrp->ha_chassis_group->name);
- } else if (op->nbrp->n_gateway_chassis) {
- /* Legacy gateway_chassis support.
- * Create ha_chassis_group for the Northbound gateway_chassis
- * associated with the lrp. */
- if (sbpb_gw_chassis_needs_update(op->sb, op->nbrp,
- sbrec_chassis_by_name)) {
- copy_gw_chassis_from_nbrp_to_sbpb(ctx,
- sbrec_chassis_by_name,
- op->nbrp, op->sb);
- }
-
- sset_add(active_ha_chassis_grps, op->nbrp->name);
- } else if (redirect_chassis) {
- /* Handle ports that had redirect-chassis option attached
- * to them, and for backwards compatibility convert them
- * to a single HA Chassis group entry */
- const struct sbrec_chassis *chassis =
- chassis_lookup_by_name(sbrec_chassis_by_name,
- redirect_chassis);
- if (chassis) {
- /* If we found the chassis, and the gw chassis on record
- * differs from what we expect go ahead and update */
- char *gwc_name = xasprintf("%s_%s", op->nbrp->name,
- chassis->name);
- const struct sbrec_ha_chassis_group *sb_ha_ch_grp;
- sb_ha_ch_grp = ha_chassis_group_lookup_by_name(
- ctx->sbrec_ha_chassis_grp_by_name, gwc_name);
- if (!sb_ha_ch_grp) {
- sb_ha_ch_grp =
- sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
- sbrec_ha_chassis_group_set_name(sb_ha_ch_grp,
- gwc_name);
- }
-
- if (sb_ha_ch_grp->n_ha_chassis != 1) {
- struct sbrec_ha_chassis **sb_ha_ch =
- xcalloc(1, sizeof *sb_ha_ch);
- sb_ha_ch[0] = create_sb_ha_chassis(ctx, chassis,
- chassis->name, 0);
- sbrec_ha_chassis_group_set_ha_chassis(sb_ha_ch_grp,
- sb_ha_ch, 1);
- }
- sbrec_port_binding_set_ha_chassis_group(op->sb,
- sb_ha_ch_grp);
- sset_add(active_ha_chassis_grps, gwc_name);
- free(gwc_name);
- } else {
- VLOG_WARN("chassis name '%s' from redirect from logical "
- " router port '%s' redirect-chassis not found",
- redirect_chassis, op->nbrp->name);
- if (op->sb->ha_chassis_group) {
- sbrec_port_binding_set_ha_chassis_group(op->sb, NULL);
- }
- }
- } else {
- /* Nothing is set. Clear ha_chassis_group from pb. */
- if (op->sb->ha_chassis_group) {
- sbrec_port_binding_set_ha_chassis_group(op->sb, NULL);
- }
- }
-
- if (op->sb->n_gateway_chassis) {
- /* Delete the legacy gateway_chassis from the pb. */
- sbrec_port_binding_set_gateway_chassis(op->sb, NULL, 0);
- }
- smap_add(&new, "distributed-port", op->nbrp->name);
- } else {
- if (op->peer) {
- smap_add(&new, "peer", op->peer->key);
- }
- if (chassis_name) {
- smap_add(&new, "l3gateway-chassis", chassis_name);
- }
- }
- sbrec_port_binding_set_options(op->sb, &new);
- smap_destroy(&new);
-
- sbrec_port_binding_set_parent_port(op->sb, NULL);
- sbrec_port_binding_set_tag(op->sb, NULL, 0);
-
- struct ds s = DS_EMPTY_INITIALIZER;
- ds_put_cstr(&s, op->nbrp->mac);
- for (int i = 0; i < op->nbrp->n_networks; ++i) {
- ds_put_format(&s, " %s", op->nbrp->networks[i]);
- }
- const char *addresses = ds_cstr(&s);
- sbrec_port_binding_set_mac(op->sb, &addresses, 1);
- ds_destroy(&s);
-
- struct smap ids = SMAP_INITIALIZER(&ids);
- sbrec_port_binding_set_external_ids(op->sb, &ids);
-
- sbrec_port_binding_set_nat_addresses(op->sb, NULL, 0);
- } else {
- if (strcmp(op->nbsp->type, "router")) {
- uint32_t queue_id = smap_get_int(
- &op->sb->options, "qdisc_queue_id", 0);
- bool has_qos = port_has_qos_params(&op->nbsp->options);
- struct smap options;
-
- if (op->sb->chassis && has_qos && !queue_id) {
- queue_id = allocate_chassis_queueid(chassis_qdisc_queues,
- op->sb->chassis);
- } else if (!has_qos && queue_id) {
- free_chassis_queueid(chassis_qdisc_queues,
- op->sb->chassis,
- queue_id);
- queue_id = 0;
- }
-
- smap_clone(&options, &op->nbsp->options);
- if (queue_id) {
- smap_add_format(&options,
- "qdisc_queue_id", "%d", queue_id);
- }
- sbrec_port_binding_set_options(op->sb, &options);
- smap_destroy(&options);
- if (ovn_is_known_nb_lsp_type(op->nbsp->type)) {
- sbrec_port_binding_set_type(op->sb, op->nbsp->type);
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(
- &rl, "Unknown port type '%s' set on logical switch '%s'.",
- op->nbsp->type, op->nbsp->name);
- }
-
- sbrec_port_binding_set_nat_addresses(op->sb, NULL, 0);
-
- if (!strcmp(op->nbsp->type, "external")) {
- if (op->nbsp->ha_chassis_group) {
- sync_ha_chassis_group_for_sbpb(
- ctx, op->nbsp->ha_chassis_group,
- sbrec_chassis_by_name, op->sb);
- sset_add(active_ha_chassis_grps,
- op->nbsp->ha_chassis_group->name);
- } else {
- sbrec_port_binding_set_ha_chassis_group(op->sb, NULL);
- }
- }
- } else {
- const char *chassis = NULL;
- if (op->peer && op->peer->od && op->peer->od->nbr) {
- chassis = smap_get(&op->peer->od->nbr->options, "chassis");
- }
-
- /* A switch port connected to a gateway router is also of
- * type "l3gateway". */
- if (chassis) {
- sbrec_port_binding_set_type(op->sb, "l3gateway");
- } else {
- sbrec_port_binding_set_type(op->sb, "patch");
- }
-
- const char *router_port = smap_get(&op->nbsp->options,
- "router-port");
- if (router_port || chassis) {
- struct smap new;
- smap_init(&new);
- if (router_port) {
- smap_add(&new, "peer", router_port);
- }
- if (chassis) {
- smap_add(&new, "l3gateway-chassis", chassis);
- }
- sbrec_port_binding_set_options(op->sb, &new);
- smap_destroy(&new);
- } else {
- sbrec_port_binding_set_options(op->sb, NULL);
- }
-
- const char *nat_addresses = smap_get(&op->nbsp->options,
- "nat-addresses");
- size_t n_nats = 0;
- char **nats = NULL;
- if (nat_addresses && !strcmp(nat_addresses, "router")) {
- if (op->peer && op->peer->od
- && (chassis || op->peer->od->l3redirect_port)) {
- nats = get_nat_addresses(op->peer, &n_nats);
- }
- /* Only accept manual specification of ethernet address
- * followed by IPv4 addresses on type "l3gateway" ports. */
- } else if (nat_addresses && chassis) {
- struct lport_addresses laddrs;
- if (!extract_lsp_addresses(nat_addresses, &laddrs)) {
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "Error extracting nat-addresses.");
- } else {
- destroy_lport_addresses(&laddrs);
- n_nats = 1;
- nats = xcalloc(1, sizeof *nats);
- nats[0] = xstrdup(nat_addresses);
- }
- }
-
- /* Add the router mac and IPv4 addresses to
- * Port_Binding.nat_addresses so that GARP is sent for these
- * IPs by the ovn-controller on which the distributed gateway
- * router port resides if:
- *
- * - op->peer has 'reside-on-gateway-chassis' set and the
- * the logical router datapath has distributed router port.
- *
- * - op->peer is distributed gateway router port.
- *
- * - op->peer's router is a gateway router and op has a localnet
- * port.
- *
- * Note: Port_Binding.nat_addresses column is also used for
- * sending the GARPs for the router port IPs.
- * */
- bool add_router_port_garp = false;
- if (op->peer && op->peer->nbrp && op->peer->od->l3dgw_port &&
- op->peer->od->l3redirect_port &&
- (smap_get_bool(&op->peer->nbrp->options,
- "reside-on-redirect-chassis", false) ||
- op->peer == op->peer->od->l3dgw_port)) {
- add_router_port_garp = true;
- } else if (chassis && op->od->localnet_port) {
- add_router_port_garp = true;
- }
-
- if (add_router_port_garp) {
- struct ds garp_info = DS_EMPTY_INITIALIZER;
- ds_put_format(&garp_info, "%s", op->peer->lrp_networks.ea_s);
- for (size_t i = 0; i < op->peer->lrp_networks.n_ipv4_addrs;
- i++) {
- ds_put_format(&garp_info, " %s",
- op->peer->lrp_networks.ipv4_addrs[i].addr_s);
- }
-
- if (op->peer->od->l3redirect_port) {
- ds_put_format(&garp_info, " is_chassis_resident(%s)",
- op->peer->od->l3redirect_port->json_key);
- }
-
- n_nats++;
- nats = xrealloc(nats, (n_nats * sizeof *nats));
- nats[n_nats - 1] = ds_steal_cstr(&garp_info);
- ds_destroy(&garp_info);
- }
-
- sbrec_port_binding_set_nat_addresses(op->sb,
- (const char **) nats, n_nats);
- for (size_t i = 0; i < n_nats; i++) {
- free(nats[i]);
- }
- free(nats);
- }
-
- sbrec_port_binding_set_parent_port(op->sb, op->nbsp->parent_name);
- sbrec_port_binding_set_tag(op->sb, op->nbsp->tag, op->nbsp->n_tag);
- sbrec_port_binding_set_mac(op->sb, (const char **) op->nbsp->addresses,
- op->nbsp->n_addresses);
-
- struct smap ids = SMAP_INITIALIZER(&ids);
- smap_clone(&ids, &op->nbsp->external_ids);
- const char *name = smap_get(&ids, "neutron:port_name");
- if (name && name[0]) {
- smap_add(&ids, "name", name);
- }
- sbrec_port_binding_set_external_ids(op->sb, &ids);
- smap_destroy(&ids);
- }
-}
-
-/* Remove mac_binding entries that refer to logical_ports which are
- * deleted. */
-static void
-cleanup_mac_bindings(struct northd_context *ctx, struct hmap *ports)
-{
- const struct sbrec_mac_binding *b, *n;
- SBREC_MAC_BINDING_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
- if (!ovn_port_find(ports, b->logical_port)) {
- sbrec_mac_binding_delete(b);
- }
- }
-}
-
-static void
-cleanup_sb_ha_chassis_groups(struct northd_context *ctx,
- struct sset *active_ha_chassis_groups)
-{
- const struct sbrec_ha_chassis_group *b, *n;
- SBREC_HA_CHASSIS_GROUP_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
- if (!sset_contains(active_ha_chassis_groups, b->name)) {
- sbrec_ha_chassis_group_delete(b);
- }
- }
-}
-
-/* Updates the southbound Port_Binding table so that it contains the logical
- * switch ports specified by the northbound database.
- *
- * Initializes 'ports' to contain a "struct ovn_port" for every logical port,
- * using the "struct ovn_datapath"s in 'datapaths' to look up logical
- * datapaths. */
-static void
-build_ports(struct northd_context *ctx,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- struct hmap *datapaths, struct hmap *ports)
-{
- struct ovs_list sb_only, nb_only, both;
- struct hmap tag_alloc_table = HMAP_INITIALIZER(&tag_alloc_table);
- struct hmap chassis_qdisc_queues = HMAP_INITIALIZER(&chassis_qdisc_queues);
-
- /* sset which stores the set of ha chassis group names used. */
- struct sset active_ha_chassis_grps =
- SSET_INITIALIZER(&active_ha_chassis_grps);
-
- join_logical_ports(ctx, datapaths, ports, &chassis_qdisc_queues,
- &tag_alloc_table, &sb_only, &nb_only, &both);
-
- struct ovn_port *op, *next;
- /* For logical ports that are in both databases, update the southbound
- * record based on northbound data. Also index the in-use tunnel_keys.
- * For logical ports that are in NB database, do any tag allocation
- * needed. */
- LIST_FOR_EACH_SAFE (op, next, list, &both) {
- if (op->nbsp) {
- tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);
- }
- ovn_port_update_sbrec(ctx, sbrec_chassis_by_name,
- op, &chassis_qdisc_queues,
- &active_ha_chassis_grps);
- add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);
- if (op->sb->tunnel_key > op->od->port_key_hint) {
- op->od->port_key_hint = op->sb->tunnel_key;
- }
- }
-
- /* Add southbound record for each unmatched northbound record. */
- LIST_FOR_EACH_SAFE (op, next, list, &nb_only) {
- uint16_t tunnel_key = ovn_port_allocate_key(op->od);
- if (!tunnel_key) {
- continue;
- }
-
- op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
- ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, op,
- &chassis_qdisc_queues,
- &active_ha_chassis_grps);
- sbrec_port_binding_set_logical_port(op->sb, op->key);
- sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key);
- }
-
- bool remove_mac_bindings = false;
- if (!ovs_list_is_empty(&sb_only)) {
- remove_mac_bindings = true;
- }
-
- /* Delete southbound records without northbound matches. */
- LIST_FOR_EACH_SAFE(op, next, list, &sb_only) {
- ovs_list_remove(&op->list);
- sbrec_port_binding_delete(op->sb);
- ovn_port_destroy(ports, op);
- }
- if (remove_mac_bindings) {
- cleanup_mac_bindings(ctx, ports);
- }
-
- tag_alloc_destroy(&tag_alloc_table);
- destroy_chassis_queues(&chassis_qdisc_queues);
- cleanup_sb_ha_chassis_groups(ctx, &active_ha_chassis_grps);
- sset_destroy(&active_ha_chassis_grps);
-}
-
-struct multicast_group {
- const char *name;
- uint16_t key; /* OVN_MIN_MULTICAST...OVN_MAX_MULTICAST. */
-};
-
-#define MC_FLOOD "_MC_flood"
-static const struct multicast_group mc_flood =
- { MC_FLOOD, OVN_MCAST_FLOOD_TUNNEL_KEY };
-
-#define MC_UNKNOWN "_MC_unknown"
-static const struct multicast_group mc_unknown =
- { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY };
-
-static bool
-multicast_group_equal(const struct multicast_group *a,
- const struct multicast_group *b)
-{
- return !strcmp(a->name, b->name) && a->key == b->key;
-}
-
-/* Multicast group entry. */
-struct ovn_multicast {
- struct hmap_node hmap_node; /* Index on 'datapath' and 'key'. */
- struct ovn_datapath *datapath;
- const struct multicast_group *group;
-
- struct ovn_port **ports;
- size_t n_ports, allocated_ports;
-};
-
-static uint32_t
-ovn_multicast_hash(const struct ovn_datapath *datapath,
- const struct multicast_group *group)
-{
- return hash_pointer(datapath, group->key);
-}
-
-static struct ovn_multicast *
-ovn_multicast_find(struct hmap *mcgroups, struct ovn_datapath *datapath,
- const struct multicast_group *group)
-{
- struct ovn_multicast *mc;
-
- HMAP_FOR_EACH_WITH_HASH (mc, hmap_node,
- ovn_multicast_hash(datapath, group), mcgroups) {
- if (mc->datapath == datapath
- && multicast_group_equal(mc->group, group)) {
- return mc;
- }
- }
- return NULL;
-}
-
-static void
-ovn_multicast_add_ports(struct hmap *mcgroups, struct ovn_datapath *od,
- const struct multicast_group *group,
- struct ovn_port **ports, size_t n_ports)
-{
- struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, group);
- if (!mc) {
- mc = xmalloc(sizeof *mc);
- hmap_insert(mcgroups, &mc->hmap_node, ovn_multicast_hash(od, group));
- mc->datapath = od;
- mc->group = group;
- mc->n_ports = 0;
- mc->allocated_ports = 4;
- mc->ports = xmalloc(mc->allocated_ports * sizeof *mc->ports);
- }
-
- size_t n_ports_total = mc->n_ports + n_ports;
-
- if (n_ports_total > 2 * mc->allocated_ports) {
- mc->allocated_ports = n_ports_total;
- mc->ports = xrealloc(mc->ports,
- mc->allocated_ports * sizeof *mc->ports);
- } else if (n_ports_total > mc->allocated_ports) {
- mc->ports = x2nrealloc(mc->ports, &mc->allocated_ports,
- sizeof *mc->ports);
- }
-
- memcpy(&mc->ports[mc->n_ports], &ports[0], n_ports * sizeof *ports);
- mc->n_ports += n_ports;
-}
-
-static void
-ovn_multicast_add(struct hmap *mcgroups, const struct multicast_group *group,
- struct ovn_port *port)
-{
- ovn_multicast_add_ports(mcgroups, port->od, group, &port, 1);
-}
-
-static void
-ovn_multicast_destroy(struct hmap *mcgroups, struct ovn_multicast *mc)
-{
- if (mc) {
- hmap_remove(mcgroups, &mc->hmap_node);
- free(mc->ports);
- free(mc);
- }
-}
-
-static void
-ovn_multicast_update_sbrec(const struct ovn_multicast *mc,
- const struct sbrec_multicast_group *sb)
-{
- struct sbrec_port_binding **ports = xmalloc(mc->n_ports * sizeof *ports);
- for (size_t i = 0; i < mc->n_ports; i++) {
- ports[i] = CONST_CAST(struct sbrec_port_binding *, mc->ports[i]->sb);
- }
- sbrec_multicast_group_set_ports(sb, ports, mc->n_ports);
- free(ports);
-}
-
-/*
- * IGMP group entry (1:1 mapping to SB database).
- */
-struct ovn_igmp_group_entry {
- struct ovs_list list_node; /* Linkage in the list of entries. */
- const struct sbrec_igmp_group *sb;
-};
-
-/*
- * IGMP group entry (aggregate of all entries from the SB database
- * corresponding to the multicast group).
- */
-struct ovn_igmp_group {
- struct hmap_node hmap_node; /* Index on 'datapath' and 'address'. */
-
- struct ovn_datapath *datapath;
- struct in6_addr address; /* Multicast IPv6-mapped-IPv4 or IPv4 address. */
- struct multicast_group mcgroup;
-
- struct ovs_list sb_entries; /* List of SB entries for this group. */
-};
-
-static uint32_t
-ovn_igmp_group_hash(const struct ovn_datapath *datapath,
- const struct in6_addr *address)
-{
- return hash_pointer(datapath, hash_bytes(address, sizeof *address, 0));
-}
-
-static struct ovn_igmp_group *
-ovn_igmp_group_find(struct hmap *igmp_groups,
- const struct ovn_datapath *datapath,
- const struct in6_addr *address)
-{
- struct ovn_igmp_group *group;
-
- HMAP_FOR_EACH_WITH_HASH (group, hmap_node,
- ovn_igmp_group_hash(datapath, address),
- igmp_groups) {
- if (group->datapath == datapath &&
- ipv6_addr_equals(&group->address, address)) {
- return group;
- }
- }
- return NULL;
-}
-
-static void
-ovn_igmp_group_add(struct northd_context *ctx, struct hmap *igmp_groups,
- struct ovn_datapath *datapath,
- const struct sbrec_igmp_group *sb_igmp_group)
-{
- struct in6_addr group_address;
- ovs_be32 ipv4;
-
- if (ip_parse(sb_igmp_group->address, &ipv4)) {
- group_address = in6_addr_mapped_ipv4(ipv4);
- } else if (!ipv6_parse(sb_igmp_group->address, &group_address)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "invalid IGMP group address: %s",
- sb_igmp_group->address);
- return;
- }
-
- struct ovn_igmp_group *igmp_group =
- ovn_igmp_group_find(igmp_groups, datapath, &group_address);
-
- if (!igmp_group) {
- igmp_group = xmalloc(sizeof *igmp_group);
-
- const struct sbrec_multicast_group *mcgroup =
- mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp,
- sb_igmp_group->address, datapath->sb);
-
- igmp_group->datapath = datapath;
- igmp_group->address = group_address;
- if (mcgroup) {
- igmp_group->mcgroup.key = mcgroup->tunnel_key;
- add_tnlid(&datapath->mcast_info.group_tnlids, mcgroup->tunnel_key);
- } else {
- igmp_group->mcgroup.key = 0;
- }
- igmp_group->mcgroup.name = sb_igmp_group->address;
- ovs_list_init(&igmp_group->sb_entries);
-
- hmap_insert(igmp_groups, &igmp_group->hmap_node,
- ovn_igmp_group_hash(datapath, &group_address));
- }
-
- struct ovn_igmp_group_entry *entry = xmalloc(sizeof *entry);
-
- entry->sb = sb_igmp_group;
- ovs_list_push_back(&igmp_group->sb_entries , &entry->list_node);
-}
-
-static void
-ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group,
- struct hmap *ovn_ports,
- struct hmap *mcast_groups)
-{
- struct ovn_igmp_group_entry *entry;
-
- LIST_FOR_EACH_POP (entry, list_node, &igmp_group->sb_entries) {
- size_t n_oports = 0;
- struct ovn_port **oports =
- xmalloc(entry->sb->n_ports * sizeof *oports);
-
- for (size_t i = 0; i < entry->sb->n_ports; i++) {
- oports[n_oports] =
- ovn_port_find(ovn_ports, entry->sb->ports[i]->logical_port);
- if (oports[n_oports]) {
- n_oports++;
- }
- }
-
- ovn_multicast_add_ports(mcast_groups, igmp_group->datapath,
- &igmp_group->mcgroup, oports, n_oports);
- free(oports);
- free(entry);
- }
-}
-
-static void
-ovn_igmp_group_destroy(struct hmap *igmp_groups,
- struct ovn_igmp_group *igmp_group)
-{
- if (igmp_group) {
- struct ovn_igmp_group_entry *entry;
-
- LIST_FOR_EACH_POP (entry, list_node, &igmp_group->sb_entries) {
- free(entry);
- }
- hmap_remove(igmp_groups, &igmp_group->hmap_node);
- free(igmp_group);
- }
-}
-
-/* Logical flow generation.
- *
- * This code generates the Logical_Flow table in the southbound database, as a
- * function of most of the northbound database.
- */
-
-struct ovn_lflow {
- struct hmap_node hmap_node;
-
- struct ovn_datapath *od;
- enum ovn_stage stage;
- uint16_t priority;
- char *match;
- char *actions;
- char *stage_hint;
- const char *where;
-};
-
-static size_t
-ovn_lflow_hash(const struct ovn_lflow *lflow)
-{
- return ovn_logical_flow_hash(&lflow->od->sb->header_.uuid,
- ovn_stage_get_table(lflow->stage),
- ovn_stage_get_pipeline_name(lflow->stage),
- lflow->priority, lflow->match,
- lflow->actions);
-}
-
-static bool
-ovn_lflow_equal(const struct ovn_lflow *a, const struct ovn_lflow *b)
-{
- return (a->od == b->od
- && a->stage == b->stage
- && a->priority == b->priority
- && !strcmp(a->match, b->match)
- && !strcmp(a->actions, b->actions));
-}
-
-static void
-ovn_lflow_init(struct ovn_lflow *lflow, struct ovn_datapath *od,
- enum ovn_stage stage, uint16_t priority,
- char *match, char *actions, char *stage_hint,
- const char *where)
-{
- lflow->od = od;
- lflow->stage = stage;
- lflow->priority = priority;
- lflow->match = match;
- lflow->actions = actions;
- lflow->stage_hint = stage_hint;
- lflow->where = where;
-}
-
-/* Adds a row with the specified contents to the Logical_Flow table. */
-static void
-ovn_lflow_add_at(struct hmap *lflow_map, struct ovn_datapath *od,
- enum ovn_stage stage, uint16_t priority,
- const char *match, const char *actions,
- const char *stage_hint, const char *where)
-{
- ovs_assert(ovn_stage_to_datapath_type(stage) == ovn_datapath_get_type(od));
-
- struct ovn_lflow *lflow = xmalloc(sizeof *lflow);
- ovn_lflow_init(lflow, od, stage, priority,
- xstrdup(match), xstrdup(actions),
- nullable_xstrdup(stage_hint), where);
- hmap_insert(lflow_map, &lflow->hmap_node, ovn_lflow_hash(lflow));
-}
-
-/* Adds a row with the specified contents to the Logical_Flow table. */
-#define ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \
- ACTIONS, STAGE_HINT) \
- ovn_lflow_add_at(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS, \
- STAGE_HINT, OVS_SOURCE_LOCATOR)
-
-#define ovn_lflow_add(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, ACTIONS) \
- ovn_lflow_add_with_hint(LFLOW_MAP, OD, STAGE, PRIORITY, MATCH, \
- ACTIONS, NULL)
-
-static struct ovn_lflow *
-ovn_lflow_find(struct hmap *lflows, struct ovn_datapath *od,
- enum ovn_stage stage, uint16_t priority,
- const char *match, const char *actions, uint32_t hash)
-{
- struct ovn_lflow target;
- ovn_lflow_init(&target, od, stage, priority,
- CONST_CAST(char *, match), CONST_CAST(char *, actions),
- NULL, NULL);
-
- struct ovn_lflow *lflow;
- HMAP_FOR_EACH_WITH_HASH (lflow, hmap_node, hash, lflows) {
- if (ovn_lflow_equal(lflow, &target)) {
- return lflow;
- }
- }
- return NULL;
-}
-
-static void
-ovn_lflow_destroy(struct hmap *lflows, struct ovn_lflow *lflow)
-{
- if (lflow) {
- hmap_remove(lflows, &lflow->hmap_node);
- free(lflow->match);
- free(lflow->actions);
- free(lflow->stage_hint);
- free(lflow);
- }
-}
-
-/* Appends port security constraints on L2 address field 'eth_addr_field'
- * (e.g. "eth.src" or "eth.dst") to 'match'. 'ps_addrs', with 'n_ps_addrs'
- * elements, is the collection of port_security constraints from an
- * OVN_NB Logical_Switch_Port row generated by extract_lsp_addresses(). */
-static void
-build_port_security_l2(const char *eth_addr_field,
- struct lport_addresses *ps_addrs,
- unsigned int n_ps_addrs,
- struct ds *match)
-{
- if (!n_ps_addrs) {
- return;
- }
-
- ds_put_format(match, " && %s == {", eth_addr_field);
-
- for (size_t i = 0; i < n_ps_addrs; i++) {
- ds_put_format(match, "%s ", ps_addrs[i].ea_s);
- }
- ds_chomp(match, ' ');
- ds_put_cstr(match, "}");
-}
-
-static void
-build_port_security_ipv6_nd_flow(
- struct ds *match, struct eth_addr ea, struct ipv6_netaddr *ipv6_addrs,
- int n_ipv6_addrs)
-{
- ds_put_format(match, " && ip6 && nd && ((nd.sll == "ETH_ADDR_FMT" || "
- "nd.sll == "ETH_ADDR_FMT") || ((nd.tll == "ETH_ADDR_FMT" || "
- "nd.tll == "ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth_addr_zero),
- ETH_ADDR_ARGS(ea), ETH_ADDR_ARGS(eth_addr_zero),
- ETH_ADDR_ARGS(ea));
- if (!n_ipv6_addrs) {
- ds_put_cstr(match, "))");
- return;
- }
-
- char ip6_str[INET6_ADDRSTRLEN + 1];
- struct in6_addr lla;
- in6_generate_lla(ea, &lla);
- memset(ip6_str, 0, sizeof(ip6_str));
- ipv6_string_mapped(ip6_str, &lla);
- ds_put_format(match, " && (nd.target == %s", ip6_str);
-
- for(int i = 0; i < n_ipv6_addrs; i++) {
- memset(ip6_str, 0, sizeof(ip6_str));
- ipv6_string_mapped(ip6_str, &ipv6_addrs[i].addr);
- ds_put_format(match, " || nd.target == %s", ip6_str);
- }
-
- ds_put_format(match, ")))");
-}
-
-static void
-build_port_security_ipv6_flow(
- enum ovn_pipeline pipeline, struct ds *match, struct eth_addr ea,
- struct ipv6_netaddr *ipv6_addrs, int n_ipv6_addrs)
-{
- char ip6_str[INET6_ADDRSTRLEN + 1];
-
- ds_put_format(match, " && %s == {",
- pipeline == P_IN ? "ip6.src" : "ip6.dst");
-
- /* Allow link-local address. */
- struct in6_addr lla;
- in6_generate_lla(ea, &lla);
- ipv6_string_mapped(ip6_str, &lla);
- ds_put_format(match, "%s, ", ip6_str);
-
- /* Allow ip6.dst=ff00::/8 for multicast packets */
- if (pipeline == P_OUT) {
- ds_put_cstr(match, "ff00::/8, ");
- }
- for(int i = 0; i < n_ipv6_addrs; i++) {
- ipv6_string_mapped(ip6_str, &ipv6_addrs[i].addr);
- ds_put_format(match, "%s, ", ip6_str);
- }
- /* Replace ", " by "}". */
- ds_chomp(match, ' ');
- ds_chomp(match, ',');
- ds_put_cstr(match, "}");
-}
-
-/**
- * Build port security constraints on ARP and IPv6 ND fields
- * and add logical flows to S_SWITCH_IN_PORT_SEC_ND stage.
- *
- * For each port security of the logical port, following
- * logical flows are added
- * - If the port security has no IP (both IPv4 and IPv6) or
- * if it has IPv4 address(es)
- * - Priority 90 flow to allow ARP packets for known MAC addresses
- * in the eth.src and arp.spa fields. If the port security
- * has IPv4 addresses, allow known IPv4 addresses in the arp.tpa field.
- *
- * - If the port security has no IP (both IPv4 and IPv6) or
- * if it has IPv6 address(es)
- * - Priority 90 flow to allow IPv6 ND packets for known MAC addresses
- * in the eth.src and nd.sll/nd.tll fields. If the port security
- * has IPv6 addresses, allow known IPv6 addresses in the nd.target field
- * for IPv6 Neighbor Advertisement packet.
- *
- * - Priority 80 flow to drop ARP and IPv6 ND packets.
- */
-static void
-build_port_security_nd(struct ovn_port *op, struct hmap *lflows)
-{
- struct ds match = DS_EMPTY_INITIALIZER;
-
- for (size_t i = 0; i < op->n_ps_addrs; i++) {
- struct lport_addresses *ps = &op->ps_addrs[i];
-
- bool no_ip = !(ps->n_ipv4_addrs || ps->n_ipv6_addrs);
-
- ds_clear(&match);
- if (ps->n_ipv4_addrs || no_ip) {
- ds_put_format(&match,
- "inport == %s && eth.src == %s && arp.sha == %s",
- op->json_key, ps->ea_s, ps->ea_s);
-
- if (ps->n_ipv4_addrs) {
- ds_put_cstr(&match, " && arp.spa == {");
- for (size_t j = 0; j < ps->n_ipv4_addrs; j++) {
- /* When the netmask is applied, if the host portion is
- * non-zero, the host can only use the specified
- * address in the arp.spa. If zero, the host is allowed
- * to use any address in the subnet. */
- if (ps->ipv4_addrs[j].plen == 32
- || ps->ipv4_addrs[j].addr & ~ps->ipv4_addrs[j].mask) {
- ds_put_cstr(&match, ps->ipv4_addrs[j].addr_s);
- } else {
- ds_put_format(&match, "%s/%d",
- ps->ipv4_addrs[j].network_s,
- ps->ipv4_addrs[j].plen);
- }
- ds_put_cstr(&match, ", ");
- }
- ds_chomp(&match, ' ');
- ds_chomp(&match, ',');
- ds_put_cstr(&match, "}");
- }
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
- ds_cstr(&match), "next;");
- }
-
- if (ps->n_ipv6_addrs || no_ip) {
- ds_clear(&match);
- ds_put_format(&match, "inport == %s && eth.src == %s",
- op->json_key, ps->ea_s);
- build_port_security_ipv6_nd_flow(&match, ps->ea, ps->ipv6_addrs,
- ps->n_ipv6_addrs);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 90,
- ds_cstr(&match), "next;");
- }
- }
-
- ds_clear(&match);
- ds_put_format(&match, "inport == %s && (arp || nd)", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_ND, 80,
- ds_cstr(&match), "drop;");
- ds_destroy(&match);
-}
-
-/**
- * Build port security constraints on IPv4 and IPv6 src and dst fields
- * and add logical flows to S_SWITCH_(IN/OUT)_PORT_SEC_IP stage.
- *
- * For each port security of the logical port, following
- * logical flows are added
- * - If the port security has IPv4 addresses,
- * - Priority 90 flow to allow IPv4 packets for known IPv4 addresses
- *
- * - If the port security has IPv6 addresses,
- * - Priority 90 flow to allow IPv6 packets for known IPv6 addresses
- *
- * - If the port security has IPv4 addresses or IPv6 addresses or both
- * - Priority 80 flow to drop all IPv4 and IPv6 traffic
- */
-static void
-build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
- struct hmap *lflows)
-{
- char *port_direction;
- enum ovn_stage stage;
- if (pipeline == P_IN) {
- port_direction = "inport";
- stage = S_SWITCH_IN_PORT_SEC_IP;
- } else {
- port_direction = "outport";
- stage = S_SWITCH_OUT_PORT_SEC_IP;
- }
-
- for (size_t i = 0; i < op->n_ps_addrs; i++) {
- struct lport_addresses *ps = &op->ps_addrs[i];
-
- if (!(ps->n_ipv4_addrs || ps->n_ipv6_addrs)) {
- continue;
- }
-
- if (ps->n_ipv4_addrs) {
- struct ds match = DS_EMPTY_INITIALIZER;
- if (pipeline == P_IN) {
- /* Permit use of the unspecified address for DHCP discovery */
- struct ds dhcp_match = DS_EMPTY_INITIALIZER;
- ds_put_format(&dhcp_match, "inport == %s"
- " && eth.src == %s"
- " && ip4.src == 0.0.0.0"
- " && ip4.dst == 255.255.255.255"
- " && udp.src == 68 && udp.dst == 67",
- op->json_key, ps->ea_s);
- ovn_lflow_add(lflows, op->od, stage, 90,
- ds_cstr(&dhcp_match), "next;");
- ds_destroy(&dhcp_match);
- ds_put_format(&match, "inport == %s && eth.src == %s"
- " && ip4.src == {", op->json_key,
- ps->ea_s);
- } else {
- ds_put_format(&match, "outport == %s && eth.dst == %s"
- " && ip4.dst == {255.255.255.255, 224.0.0.0/4, ",
- op->json_key, ps->ea_s);
- }
-
- for (int j = 0; j < ps->n_ipv4_addrs; j++) {
- ovs_be32 mask = ps->ipv4_addrs[j].mask;
- /* When the netmask is applied, if the host portion is
- * non-zero, the host can only use the specified
- * address. If zero, the host is allowed to use any
- * address in the subnet.
- */
- if (ps->ipv4_addrs[j].plen == 32
- || ps->ipv4_addrs[j].addr & ~mask) {
- ds_put_format(&match, "%s", ps->ipv4_addrs[j].addr_s);
- if (pipeline == P_OUT && ps->ipv4_addrs[j].plen != 32) {
- /* Host is also allowed to receive packets to the
- * broadcast address in the specified subnet. */
- ds_put_format(&match, ", %s",
- ps->ipv4_addrs[j].bcast_s);
- }
- } else {
- /* host portion is zero */
- ds_put_format(&match, "%s/%d", ps->ipv4_addrs[j].network_s,
- ps->ipv4_addrs[j].plen);
- }
- ds_put_cstr(&match, ", ");
- }
-
- /* Replace ", " by "}". */
- ds_chomp(&match, ' ');
- ds_chomp(&match, ',');
- ds_put_cstr(&match, "}");
- ovn_lflow_add(lflows, op->od, stage, 90, ds_cstr(&match), "next;");
- ds_destroy(&match);
- }
-
- if (ps->n_ipv6_addrs) {
- struct ds match = DS_EMPTY_INITIALIZER;
- if (pipeline == P_IN) {
- /* Permit use of unspecified address for duplicate address
- * detection */
- struct ds dad_match = DS_EMPTY_INITIALIZER;
- ds_put_format(&dad_match, "inport == %s"
- " && eth.src == %s"
- " && ip6.src == ::"
- " && ip6.dst == ff02::/16"
- " && icmp6.type == {131, 135, 143}", op->json_key,
- ps->ea_s);
- ovn_lflow_add(lflows, op->od, stage, 90,
- ds_cstr(&dad_match), "next;");
- ds_destroy(&dad_match);
- }
- ds_put_format(&match, "%s == %s && %s == %s",
- port_direction, op->json_key,
- pipeline == P_IN ? "eth.src" : "eth.dst", ps->ea_s);
- build_port_security_ipv6_flow(pipeline, &match, ps->ea,
- ps->ipv6_addrs, ps->n_ipv6_addrs);
- ovn_lflow_add(lflows, op->od, stage, 90,
- ds_cstr(&match), "next;");
- ds_destroy(&match);
- }
-
- char *match = xasprintf("%s == %s && %s == %s && ip",
- port_direction, op->json_key,
- pipeline == P_IN ? "eth.src" : "eth.dst",
- ps->ea_s);
- ovn_lflow_add(lflows, op->od, stage, 80, match, "drop;");
- free(match);
- }
-
-}
-
-static bool
-lsp_is_enabled(const struct nbrec_logical_switch_port *lsp)
-{
- return !lsp->enabled || *lsp->enabled;
-}
-
-static bool
-lsp_is_up(const struct nbrec_logical_switch_port *lsp)
-{
- return !lsp->up || *lsp->up;
-}
-
-static bool
-lsp_is_external(const struct nbrec_logical_switch_port *nbsp)
-{
- return !strcmp(nbsp->type, "external");
-}
-
-static bool
-build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
- struct ds *options_action, struct ds *response_action,
- struct ds *ipv4_addr_match)
-{
- if (!op->nbsp->dhcpv4_options) {
- /* CMS has disabled native DHCPv4 for this lport. */
- return false;
- }
-
- ovs_be32 host_ip, mask;
- char *error = ip_parse_masked(op->nbsp->dhcpv4_options->cidr, &host_ip,
- &mask);
- if (error || ((offer_ip ^ host_ip) & mask)) {
- /* Either
- * - cidr defined is invalid or
- * - the offer ip of the logical port doesn't belong to the cidr
- * defined in the DHCPv4 options.
- * */
- free(error);
- return false;
- }
-
- const char *server_ip = smap_get(
- &op->nbsp->dhcpv4_options->options, "server_id");
- const char *server_mac = smap_get(
- &op->nbsp->dhcpv4_options->options, "server_mac");
- const char *lease_time = smap_get(
- &op->nbsp->dhcpv4_options->options, "lease_time");
-
- if (!(server_ip && server_mac && lease_time)) {
- /* "server_id", "server_mac" and "lease_time" should be
- * present in the dhcp_options. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Required DHCPv4 options not defined for lport - %s",
- op->json_key);
- return false;
- }
-
- struct smap dhcpv4_options = SMAP_INITIALIZER(&dhcpv4_options);
- smap_clone(&dhcpv4_options, &op->nbsp->dhcpv4_options->options);
-
- /* server_mac is not DHCPv4 option, delete it from the smap. */
- smap_remove(&dhcpv4_options, "server_mac");
- char *netmask = xasprintf(IP_FMT, IP_ARGS(mask));
- smap_add(&dhcpv4_options, "netmask", netmask);
- free(netmask);
-
- ds_put_format(options_action,
- REGBIT_DHCP_OPTS_RESULT" = put_dhcp_opts(offerip = "
- IP_FMT", ", IP_ARGS(offer_ip));
-
- /* We're not using SMAP_FOR_EACH because we want a consistent order of the
- * options on different architectures (big or little endian, SSE4.2) */
- const struct smap_node **sorted_opts = smap_sort(&dhcpv4_options);
- for (size_t i = 0; i < smap_count(&dhcpv4_options); i++) {
- const struct smap_node *node = sorted_opts[i];
- ds_put_format(options_action, "%s = %s, ", node->key, node->value);
- }
- free(sorted_opts);
-
- ds_chomp(options_action, ' ');
- ds_chomp(options_action, ',');
- ds_put_cstr(options_action, "); next;");
-
- ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
- "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; "
- "udp.dst = 68; outport = inport; flags.loopback = 1; "
- "output;",
- server_mac, IP_ARGS(offer_ip), server_ip);
-
- ds_put_format(ipv4_addr_match,
- "ip4.src == "IP_FMT" && ip4.dst == {%s, 255.255.255.255}",
- IP_ARGS(offer_ip), server_ip);
- smap_destroy(&dhcpv4_options);
- return true;
-}
-
-static bool
-build_dhcpv6_action(struct ovn_port *op, struct in6_addr *offer_ip,
- struct ds *options_action, struct ds *response_action)
-{
- if (!op->nbsp->dhcpv6_options) {
- /* CMS has disabled native DHCPv6 for this lport. */
- return false;
- }
-
- struct in6_addr host_ip, mask;
-
- char *error = ipv6_parse_masked(op->nbsp->dhcpv6_options->cidr, &host_ip,
- &mask);
- if (error) {
- free(error);
- return false;
- }
- struct in6_addr ip6_mask = ipv6_addr_bitxor(offer_ip, &host_ip);
- ip6_mask = ipv6_addr_bitand(&ip6_mask, &mask);
- if (!ipv6_mask_is_any(&ip6_mask)) {
- /* offer_ip doesn't belongs to the cidr defined in lport's DHCPv6
- * options.*/
- return false;
- }
-
- const struct smap *options_map = &op->nbsp->dhcpv6_options->options;
- /* "server_id" should be the MAC address. */
- const char *server_mac = smap_get(options_map, "server_id");
- struct eth_addr ea;
- if (!server_mac || !eth_addr_from_string(server_mac, &ea)) {
- /* "server_id" should be present in the dhcpv6_options. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "server_id not present in the DHCPv6 options"
- " for lport %s", op->json_key);
- return false;
- }
-
- /* Get the link local IP of the DHCPv6 server from the server MAC. */
- struct in6_addr lla;
- in6_generate_lla(ea, &lla);
-
- char server_ip[INET6_ADDRSTRLEN + 1];
- ipv6_string_mapped(server_ip, &lla);
-
- char ia_addr[INET6_ADDRSTRLEN + 1];
- ipv6_string_mapped(ia_addr, offer_ip);
-
- ds_put_format(options_action,
- REGBIT_DHCP_OPTS_RESULT" = put_dhcpv6_opts(");
-
- /* Check whether the dhcpv6 options should be configured as stateful.
- * Only reply with ia_addr option for dhcpv6 stateful address mode. */
- if (!smap_get_bool(options_map, "dhcpv6_stateless", false)) {
- ipv6_string_mapped(ia_addr, offer_ip);
- ds_put_format(options_action, "ia_addr = %s, ", ia_addr);
- }
-
- /* We're not using SMAP_FOR_EACH because we want a consistent order of the
- * options on different architectures (big or little endian, SSE4.2) */
- const struct smap_node **sorted_opts = smap_sort(options_map);
- for (size_t i = 0; i < smap_count(options_map); i++) {
- const struct smap_node *node = sorted_opts[i];
- if (strcmp(node->key, "dhcpv6_stateless")) {
- ds_put_format(options_action, "%s = %s, ", node->key, node->value);
- }
- }
- free(sorted_opts);
-
- ds_chomp(options_action, ' ');
- ds_chomp(options_action, ',');
- ds_put_cstr(options_action, "); next;");
-
- ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
- "ip6.dst = ip6.src; ip6.src = %s; udp.src = 547; "
- "udp.dst = 546; outport = inport; flags.loopback = 1; "
- "output;",
- server_mac, server_ip);
-
- return true;
-}
-
-struct ovn_port_group_ls {
- struct hmap_node key_node; /* Index on 'key'. */
- struct uuid key; /* nb_ls->header_.uuid. */
- const struct nbrec_logical_switch *nb_ls;
-};
-
-struct ovn_port_group {
- struct hmap_node key_node; /* Index on 'key'. */
- struct uuid key; /* nb_pg->header_.uuid. */
- const struct nbrec_port_group *nb_pg;
- struct hmap nb_lswitches; /* NB lswitches related to the port group */
-};
-
-static void
-ovn_port_group_ls_add(struct ovn_port_group *pg,
- const struct nbrec_logical_switch *nb_ls)
-{
- struct ovn_port_group_ls *pg_ls = xzalloc(sizeof *pg_ls);
- pg_ls->key = nb_ls->header_.uuid;
- pg_ls->nb_ls = nb_ls;
- hmap_insert(&pg->nb_lswitches, &pg_ls->key_node, uuid_hash(&pg_ls->key));
-}
-
-static struct ovn_port_group_ls *
-ovn_port_group_ls_find(struct ovn_port_group *pg, const struct uuid *ls_uuid)
-{
- struct ovn_port_group_ls *pg_ls;
-
- HMAP_FOR_EACH_WITH_HASH (pg_ls, key_node, uuid_hash(ls_uuid),
- &pg->nb_lswitches) {
- if (uuid_equals(ls_uuid, &pg_ls->key)) {
- return pg_ls;
- }
- }
- return NULL;
-}
-
-struct ovn_ls_port_group {
- struct hmap_node key_node; /* Index on 'key'. */
- struct uuid key; /* nb_pg->header_.uuid. */
- const struct nbrec_port_group *nb_pg;
-};
-
-static void
-ovn_ls_port_group_add(struct hmap *nb_pgs,
- const struct nbrec_port_group *nb_pg)
-{
- struct ovn_ls_port_group *ls_pg = xzalloc(sizeof *ls_pg);
- ls_pg->key = nb_pg->header_.uuid;
- ls_pg->nb_pg = nb_pg;
- hmap_insert(nb_pgs, &ls_pg->key_node, uuid_hash(&ls_pg->key));
-}
-
-static void
-ovn_ls_port_group_destroy(struct hmap *nb_pgs)
-{
- struct ovn_ls_port_group *ls_pg;
- HMAP_FOR_EACH_POP (ls_pg, key_node, nb_pgs) {
- free(ls_pg);
- }
- hmap_destroy(nb_pgs);
-}
-
-static bool
-has_stateful_acl(struct ovn_datapath *od)
-{
- for (size_t i = 0; i < od->nbs->n_acls; i++) {
- struct nbrec_acl *acl = od->nbs->acls[i];
- if (!strcmp(acl->action, "allow-related")) {
- return true;
- }
- }
-
- struct ovn_ls_port_group *ls_pg;
- HMAP_FOR_EACH (ls_pg, key_node, &od->nb_pgs) {
- for (size_t i = 0; i < ls_pg->nb_pg->n_acls; i++) {
- struct nbrec_acl *acl = ls_pg->nb_pg->acls[i];
- if (!strcmp(acl->action, "allow-related")) {
- return true;
- }
- }
- }
-
- return false;
-}
-
-static void
-build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
-{
- bool has_stateful = has_stateful_acl(od);
-
- /* Ingress and Egress Pre-ACL Table (Priority 0): Packets are
- * allowed by default. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;");
-
- /* If there are any stateful ACL rules in this datapath, we must
- * send all IP packets through the conntrack action, which handles
- * defragmentation, in order to match L4 headers. */
- if (has_stateful) {
- for (size_t i = 0; i < od->n_router_ports; i++) {
- struct ovn_port *op = od->router_ports[i];
- /* Can't use ct() for router ports. Consider the
- * following configuration: lp1(10.0.0.2) on
- * hostA--ls1--lr0--ls2--lp2(10.0.1.2) on hostB, For a
- * ping from lp1 to lp2, First, the response will go
- * through ct() with a zone for lp2 in the ls2 ingress
- * pipeline on hostB. That ct zone knows about this
- * connection. Next, it goes through ct() with the zone
- * for the router port in the egress pipeline of ls2 on
- * hostB. This zone does not know about the connection,
- * as the icmp request went through the logical router
- * on hostA, not hostB. This would only work with
- * distributed conntrack state across all chassis. */
- struct ds match_in = DS_EMPTY_INITIALIZER;
- struct ds match_out = DS_EMPTY_INITIALIZER;
-
- ds_put_format(&match_in, "ip && inport == %s", op->json_key);
- ds_put_format(&match_out, "ip && outport == %s", op->json_key);
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
- ds_cstr(&match_in), "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
- ds_cstr(&match_out), "next;");
-
- ds_destroy(&match_in);
- ds_destroy(&match_out);
- }
- if (od->localnet_port) {
- struct ds match_in = DS_EMPTY_INITIALIZER;
- struct ds match_out = DS_EMPTY_INITIALIZER;
-
- ds_put_format(&match_in, "ip && inport == %s",
- od->localnet_port->json_key);
- ds_put_format(&match_out, "ip && outport == %s",
- od->localnet_port->json_key);
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
- ds_cstr(&match_in), "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
- ds_cstr(&match_out), "next;");
-
- ds_destroy(&match_in);
- ds_destroy(&match_out);
- }
-
- /* Ingress and Egress Pre-ACL Table (Priority 110).
- *
- * Not to do conntrack on ND and ICMP destination
- * unreachable packets. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110,
- "nd || nd_rs || nd_ra || icmp4.type == 3 || "
- "icmp6.type == 1 || (tcp && tcp.flags == 4)",
- "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110,
- "nd || nd_rs || nd_ra || icmp4.type == 3 || "
- "icmp6.type == 1 || (tcp && tcp.flags == 4)",
- "next;");
-
- /* Ingress and Egress Pre-ACL Table (Priority 100).
- *
- * Regardless of whether the ACL is "from-lport" or "to-lport",
- * we need rules in both the ingress and egress table, because
- * the return traffic needs to be followed.
- *
- * 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
- * it to conntrack for tracking and defragmentation. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 100, "ip",
- REGBIT_CONNTRACK_DEFRAG" = 1; next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 100, "ip",
- REGBIT_CONNTRACK_DEFRAG" = 1; next;");
- }
-}
-
-/* For a 'key' of the form "IP:port" or just "IP", sets 'port' and
- * 'ip_address'. The caller must free() the memory allocated for
- * 'ip_address'. */
-static void
-ip_address_and_port_from_lb_key(const char *key, char **ip_address,
- uint16_t *port, int *addr_family)
-{
- struct sockaddr_storage ss;
- if (!inet_parse_active(key, 0, &ss, false)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address or port for load balancer key %s",
- key);
- return;
- }
-
- struct ds s = DS_EMPTY_INITIALIZER;
- ss_format_address_nobracks(&ss, &s);
- *ip_address = ds_steal_cstr(&s);
-
- *port = ss_get_port(&ss);
-
- *addr_family = ss.ss_family;
-}
-
-/*
- * Returns true if logical switch is configured with DNS records, false
- * otherwise.
- */
-static bool
-ls_has_dns_records(const struct nbrec_logical_switch *nbs)
-{
- for (size_t i = 0; i < nbs->n_dns_records; i++) {
- if (!smap_is_empty(&nbs->dns_records[i]->records)) {
- return true;
- }
- }
-
- return false;
-}
-
-static void
-build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
-{
- /* Do not send ND packets to conntrack */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
- "nd || nd_rs || nd_ra", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
- "nd || nd_rs || nd_ra", "next;");
-
- /* Allow all packets to go to next tables by default. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;");
-
- struct sset all_ips = SSET_INITIALIZER(&all_ips);
- bool vip_configured = false;
- int addr_family = AF_INET;
- for (int i = 0; i < od->nbs->n_load_balancer; i++) {
- struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
- struct smap *vips = &lb->vips;
- struct smap_node *node;
-
- SMAP_FOR_EACH (node, vips) {
- vip_configured = true;
-
- /* node->key contains IP:port or just IP. */
- char *ip_address = NULL;
- uint16_t port;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
- &addr_family);
- if (!ip_address) {
- continue;
- }
-
- if (!sset_contains(&all_ips, ip_address)) {
- sset_add(&all_ips, ip_address);
- }
-
- if (controller_event_en && !node->value[0]) {
- struct ds match = DS_EMPTY_INITIALIZER;
- char *action;
-
- if (addr_family == AF_INET) {
- ds_put_format(&match, "ip4.dst == %s && %s",
- ip_address, lb->protocol);
- } else {
- ds_put_format(&match, "ip6.dst == %s && %s",
- ip_address, lb->protocol);
- }
- if (port) {
- ds_put_format(&match, " && %s.dst == %u", lb->protocol,
- port);
- }
- action = xasprintf("trigger_event(event = \"%s\", "
- "vip = \"%s\", protocol = \"%s\", "
- "load_balancer = \"" UUID_FMT "\");",
- event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
- node->key, lb->protocol,
- UUID_ARGS(&lb->header_.uuid));
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 120,
- ds_cstr(&match), action);
- ds_destroy(&match);
- free(action);
- }
-
- free(ip_address);
-
- /* Ignore L4 port information in the key because fragmented packets
- * may not have L4 information. The pre-stateful table will send
- * the packet through ct() action to de-fragment. In stateful
- * table, we will eventually look at L4 information. */
- }
- }
-
- /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
- * packet to conntrack for defragmentation. */
- const char *ip_address;
- SSET_FOR_EACH(ip_address, &all_ips) {
- char *match;
-
- if (addr_family == AF_INET) {
- match = xasprintf("ip && ip4.dst == %s", ip_address);
- } else {
- match = xasprintf("ip && ip6.dst == %s", ip_address);
- }
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
- 100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;");
- free(match);
- }
-
- sset_destroy(&all_ips);
-
- if (vip_configured) {
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB,
- 100, "ip", REGBIT_CONNTRACK_DEFRAG" = 1; next;");
- }
-}
-
-static void
-build_pre_stateful(struct ovn_datapath *od, struct hmap *lflows)
-{
- /* Ingress and Egress pre-stateful Table (Priority 0): Packets are
- * allowed by default. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 0, "1", "next;");
-
- /* If REGBIT_CONNTRACK_DEFRAG is set as 1, then the packets should be
- * sent to conntrack for tracking and defragmentation. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_STATEFUL, 100,
- REGBIT_CONNTRACK_DEFRAG" == 1", "ct_next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_STATEFUL, 100,
- REGBIT_CONNTRACK_DEFRAG" == 1", "ct_next;");
-}
-
-static void
-build_acl_log(struct ds *actions, const struct nbrec_acl *acl)
-{
- if (!acl->log) {
- return;
- }
-
- ds_put_cstr(actions, "log(");
-
- if (acl->name) {
- ds_put_format(actions, "name=\"%s\", ", acl->name);
- }
-
- /* If a severity level isn't specified, default to "info". */
- if (acl->severity) {
- ds_put_format(actions, "severity=%s, ", acl->severity);
- } else {
- ds_put_format(actions, "severity=info, ");
- }
-
- if (!strcmp(acl->action, "drop")) {
- ds_put_cstr(actions, "verdict=drop, ");
- } else if (!strcmp(acl->action, "reject")) {
- ds_put_cstr(actions, "verdict=reject, ");
- } else if (!strcmp(acl->action, "allow")
- || !strcmp(acl->action, "allow-related")) {
- ds_put_cstr(actions, "verdict=allow, ");
- }
-
- if (acl->meter) {
- ds_put_format(actions, "meter=\"%s\", ", acl->meter);
- }
-
- ds_chomp(actions, ' ');
- ds_chomp(actions, ',');
- ds_put_cstr(actions, "); ");
-}
-
-static void
-build_reject_acl_rules(struct ovn_datapath *od, struct hmap *lflows,
- enum ovn_stage stage, struct nbrec_acl *acl,
- struct ds *extra_match, struct ds *extra_actions)
-{
- struct ds match = DS_EMPTY_INITIALIZER;
- struct ds actions = DS_EMPTY_INITIALIZER;
- bool ingress = (stage == S_SWITCH_IN_ACL);
-
- /* TCP */
- build_acl_log(&actions, acl);
- if (extra_match->length > 0) {
- ds_put_format(&match, "(%s) && ", extra_match->string);
- }
- ds_put_format(&match, "ip4 && tcp && (%s)", acl->match);
- ds_put_format(&actions, "reg0 = 0; "
- "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
- "tcp_reset { outport <-> inport; %s };",
- ingress ? "output;" : "next(pipeline=ingress,table=0);");
- ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET + 10,
- ds_cstr(&match), ds_cstr(&actions));
- ds_clear(&match);
- ds_clear(&actions);
- build_acl_log(&actions, acl);
- if (extra_match->length > 0) {
- ds_put_format(&match, "(%s) && ", extra_match->string);
- }
- ds_put_format(&match, "ip6 && tcp && (%s)", acl->match);
- ds_put_format(&actions, "reg0 = 0; "
- "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
- "tcp_reset { outport <-> inport; %s };",
- ingress ? "output;" : "next(pipeline=ingress,table=0);");
- ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET + 10,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* IP traffic */
- ds_clear(&match);
- ds_clear(&actions);
- build_acl_log(&actions, acl);
- if (extra_match->length > 0) {
- ds_put_format(&match, "(%s) && ", extra_match->string);
- }
- ds_put_format(&match, "ip4 && (%s)", acl->match);
- if (extra_actions->length > 0) {
- ds_put_format(&actions, "%s ", extra_actions->string);
- }
- ds_put_format(&actions, "reg0 = 0; "
- "eth.dst <-> eth.src; ip4.dst <-> ip4.src; "
- "icmp4 { outport <-> inport; %s };",
- ingress ? "output;" : "next(pipeline=ingress,table=0);");
- ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match), ds_cstr(&actions));
- ds_clear(&match);
- ds_clear(&actions);
- build_acl_log(&actions, acl);
- if (extra_match->length > 0) {
- ds_put_format(&match, "(%s) && ", extra_match->string);
- }
- ds_put_format(&match, "ip6 && (%s)", acl->match);
- if (extra_actions->length > 0) {
- ds_put_format(&actions, "%s ", extra_actions->string);
- }
- ds_put_format(&actions, "reg0 = 0; icmp6 { "
- "eth.dst <-> eth.src; ip6.dst <-> ip6.src; "
- "outport <-> inport; %s };",
- ingress ? "output;" : "next(pipeline=ingress,table=0);");
- ovn_lflow_add(lflows, od, stage, acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match), ds_cstr(&actions));
-
- ds_destroy(&match);
- ds_destroy(&actions);
-}
-
-static void
-consider_acl(struct hmap *lflows, struct ovn_datapath *od,
- struct nbrec_acl *acl, bool has_stateful)
-{
- bool ingress = !strcmp(acl->direction, "from-lport") ? true :false;
- enum ovn_stage stage = ingress ? S_SWITCH_IN_ACL : S_SWITCH_OUT_ACL;
-
- char *stage_hint = xasprintf("%08x", acl->header_.uuid.parts[0]);
- if (!strcmp(acl->action, "allow")
- || !strcmp(acl->action, "allow-related")) {
- /* If there are any stateful flows, we must even commit "allow"
- * actions. This is because, while the initiater's
- * direction may not have any stateful rules, the server's
- * may and then its return traffic would not have an
- * associated conntrack entry and would return "+invalid". */
- if (!has_stateful) {
- struct ds actions = DS_EMPTY_INITIALIZER;
- build_acl_log(&actions, acl);
- ds_put_cstr(&actions, "next;");
- ovn_lflow_add_with_hint(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- acl->match, ds_cstr(&actions),
- stage_hint);
- ds_destroy(&actions);
- } else {
- struct ds match = DS_EMPTY_INITIALIZER;
- struct ds actions = DS_EMPTY_INITIALIZER;
-
- /* Commit the connection tracking entry if it's a new
- * connection that matches this ACL. After this commit,
- * the reply traffic is allowed by a flow we create at
- * priority 65535, defined earlier.
- *
- * It's also possible that a known connection was marked for
- * deletion after a policy was deleted, but the policy was
- * re-added while that connection is still known. We catch
- * that case here and un-set ct_label.blocked (which will be done
- * by ct_commit in the "stateful" stage) to indicate that the
- * connection should be allowed to resume.
- */
- ds_put_format(&match, "((ct.new && !ct.est)"
- " || (!ct.new && ct.est && !ct.rpl "
- "&& ct_label.blocked == 1)) "
- "&& (%s)", acl->match);
- ds_put_cstr(&actions, REGBIT_CONNTRACK_COMMIT" = 1; ");
- build_acl_log(&actions, acl);
- ds_put_cstr(&actions, "next;");
- ovn_lflow_add_with_hint(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match),
- ds_cstr(&actions),
- stage_hint);
-
- /* Match on traffic in the request direction for an established
- * connection tracking entry that has not been marked for
- * deletion. There is no need to commit here, so we can just
- * proceed to the next table. We use this to ensure that this
- * connection is still allowed by the currently defined
- * policy. */
- ds_clear(&match);
- ds_clear(&actions);
- ds_put_format(&match,
- "!ct.new && ct.est && !ct.rpl"
- " && ct_label.blocked == 0 && (%s)",
- acl->match);
-
- build_acl_log(&actions, acl);
- ds_put_cstr(&actions, "next;");
- ovn_lflow_add_with_hint(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match), ds_cstr(&actions),
- stage_hint);
-
- ds_destroy(&match);
- ds_destroy(&actions);
- }
- } else if (!strcmp(acl->action, "drop")
- || !strcmp(acl->action, "reject")) {
- struct ds match = DS_EMPTY_INITIALIZER;
- struct ds actions = DS_EMPTY_INITIALIZER;
-
- /* The implementation of "drop" differs if stateful ACLs are in
- * use for this datapath. In that case, the actions differ
- * depending on whether the connection was previously committed
- * to the connection tracker with ct_commit. */
- if (has_stateful) {
- /* If the packet is not part of an established connection, then
- * we can simply reject/drop it. */
- ds_put_cstr(&match,
- "(!ct.est || (ct.est && ct_label.blocked == 1))");
- if (!strcmp(acl->action, "reject")) {
- build_reject_acl_rules(od, lflows, stage, acl, &match,
- &actions);
- } else {
- ds_put_format(&match, " && (%s)", acl->match);
- build_acl_log(&actions, acl);
- ds_put_cstr(&actions, "/* drop */");
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match), ds_cstr(&actions));
- }
- /* For an existing connection without ct_label set, we've
- * encountered a policy change. ACLs previously allowed
- * this connection and we committed the connection tracking
- * entry. Current policy says that we should drop this
- * connection. First, we set bit 0 of ct_label to indicate
- * that this connection is set for deletion. By not
- * specifying "next;", we implicitly drop the packet after
- * updating conntrack state. We would normally defer
- * ct_commit() to the "stateful" stage, but since we're
- * rejecting/dropping the packet, we go ahead and do it here.
- */
- ds_clear(&match);
- ds_clear(&actions);
- ds_put_cstr(&match, "ct.est && ct_label.blocked == 0");
- ds_put_cstr(&actions, "ct_commit(ct_label=1/1); ");
- if (!strcmp(acl->action, "reject")) {
- build_reject_acl_rules(od, lflows, stage, acl, &match,
- &actions);
- } else {
- ds_put_format(&match, " && (%s)", acl->match);
- build_acl_log(&actions, acl);
- ds_put_cstr(&actions, "/* drop */");
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- ds_cstr(&match), ds_cstr(&actions));
- }
- } else {
- /* There are no stateful ACLs in use on this datapath,
- * so a "reject/drop" ACL is simply the "reject/drop"
- * logical flow action in all cases. */
- if (!strcmp(acl->action, "reject")) {
- build_reject_acl_rules(od, lflows, stage, acl, &match,
- &actions);
- } else {
- build_acl_log(&actions, acl);
- ds_put_cstr(&actions, "/* drop */");
- ovn_lflow_add(lflows, od, stage,
- acl->priority + OVN_ACL_PRI_OFFSET,
- acl->match, ds_cstr(&actions));
- }
- }
- ds_destroy(&match);
- ds_destroy(&actions);
- }
- free(stage_hint);
-}
-
-static struct ovn_port_group *
-ovn_port_group_create(struct hmap *pgs,
- const struct nbrec_port_group *nb_pg)
-{
- struct ovn_port_group *pg = xzalloc(sizeof *pg);
- pg->key = nb_pg->header_.uuid;
- pg->nb_pg = nb_pg;
- hmap_init(&pg->nb_lswitches);
- hmap_insert(pgs, &pg->key_node, uuid_hash(&pg->key));
- return pg;
-}
-
-static void
-ovn_port_group_destroy(struct hmap *pgs, struct ovn_port_group *pg)
-{
- if (pg) {
- hmap_remove(pgs, &pg->key_node);
- struct ovn_port_group_ls *ls;
- HMAP_FOR_EACH_POP (ls, key_node, &pg->nb_lswitches) {
- free(ls);
- }
- hmap_destroy(&pg->nb_lswitches);
- free(pg);
- }
-}
-
-static void
-build_port_group_lswitches(struct northd_context *ctx, struct hmap *pgs,
- struct hmap *ports)
-{
- hmap_init(pgs);
-
- const struct nbrec_port_group *nb_pg;
- NBREC_PORT_GROUP_FOR_EACH (nb_pg, ctx->ovnnb_idl) {
- struct ovn_port_group *pg = ovn_port_group_create(pgs, nb_pg);
- for (size_t i = 0; i < nb_pg->n_ports; i++) {
- struct ovn_port *op = ovn_port_find(ports, nb_pg->ports[i]->name);
- if (!op) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_ERR_RL(&rl, "lport %s in port group %s not found.",
- nb_pg->ports[i]->name,
- nb_pg->name);
- continue;
- }
-
- if (!op->od->nbs) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
- VLOG_WARN_RL(&rl, "lport %s in port group %s has no lswitch.",
- nb_pg->ports[i]->name,
- nb_pg->name);
- continue;
- }
-
- struct ovn_port_group_ls *pg_ls =
- ovn_port_group_ls_find(pg, &op->od->nbs->header_.uuid);
- if (!pg_ls) {
- ovn_port_group_ls_add(pg, op->od->nbs);
- ovn_ls_port_group_add(&op->od->nb_pgs, nb_pg);
- }
- }
- }
-}
-
-static void
-build_acls(struct ovn_datapath *od, struct hmap *lflows,
- struct hmap *port_groups)
-{
- bool has_stateful = has_stateful_acl(od);
-
- /* Ingress and Egress ACL Table (Priority 0): Packets are allowed by
- * default. A related rule at priority 1 is added below if there
- * are any stateful ACLs in this datapath. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 0, "1", "next;");
-
- if (has_stateful) {
- /* Ingress and Egress ACL Table (Priority 1).
- *
- * By default, traffic is allowed. This is partially handled by
- * the Priority 0 ACL flows added earlier, but we also need to
- * commit IP flows. This is because, while the initiater's
- * direction may not have any stateful rules, the server's may
- * and then its return traffic would not have an associated
- * conntrack entry and would return "+invalid".
- *
- * We use "ct_commit" for a connection that is not already known
- * by the connection tracker. Once a connection is committed,
- * subsequent packets will hit the flow at priority 0 that just
- * uses "next;"
- *
- * We also check for established connections that have ct_label.blocked
- * set on them. That's a connection that was disallowed, but is
- * now allowed by policy again since it hit this default-allow flow.
- * We need to set ct_label.blocked=0 to let the connection continue,
- * which will be done by ct_commit() in the "stateful" stage.
- * Subsequent packets will hit the flow at priority 0 that just
- * uses "next;". */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 1,
- "ip && (!ct.est || (ct.est && ct_label.blocked == 1))",
- REGBIT_CONNTRACK_COMMIT" = 1; next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 1,
- "ip && (!ct.est || (ct.est && ct_label.blocked == 1))",
- REGBIT_CONNTRACK_COMMIT" = 1; next;");
-
- /* Ingress and Egress ACL Table (Priority 65535).
- *
- * Always drop traffic that's in an invalid state. Also drop
- * reply direction packets for connections that have been marked
- * for deletion (bit 0 of ct_label is set).
- *
- * This is enforced at a higher priority than ACLs can be defined. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
- "ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)",
- "drop;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
- "ct.inv || (ct.est && ct.rpl && ct_label.blocked == 1)",
- "drop;");
-
- /* Ingress and Egress ACL Table (Priority 65535).
- *
- * Allow reply traffic that is part of an established
- * conntrack entry that has not been marked for deletion
- * (bit 0 of ct_label). We only match traffic in the
- * reply direction because we want traffic in the request
- * direction to hit the currently defined policy from ACLs.
- *
- * This is enforced at a higher priority than ACLs can be defined. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
- "ct.est && !ct.rel && !ct.new && !ct.inv "
- "&& ct.rpl && ct_label.blocked == 0",
- "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
- "ct.est && !ct.rel && !ct.new && !ct.inv "
- "&& ct.rpl && ct_label.blocked == 0",
- "next;");
-
- /* Ingress and Egress ACL Table (Priority 65535).
- *
- * Allow traffic that is related to an existing conntrack entry that
- * has not been marked for deletion (bit 0 of ct_label).
- *
- * This is enforced at a higher priority than ACLs can be defined.
- *
- * NOTE: This does not support related data sessions (eg,
- * a dynamically negotiated FTP data channel), but will allow
- * related traffic such as an ICMP Port Unreachable through
- * that's generated from a non-listening UDP port. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
- "!ct.est && ct.rel && !ct.new && !ct.inv "
- "&& ct_label.blocked == 0",
- "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
- "!ct.est && ct.rel && !ct.new && !ct.inv "
- "&& ct_label.blocked == 0",
- "next;");
-
- /* Ingress and Egress ACL Table (Priority 65535).
- *
- * Not to do conntrack on ND packets. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "nd", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "nd", "next;");
- }
-
- /* Ingress or Egress ACL Table (Various priorities). */
- for (size_t i = 0; i < od->nbs->n_acls; i++) {
- struct nbrec_acl *acl = od->nbs->acls[i];
- consider_acl(lflows, od, acl, has_stateful);
- }
- struct ovn_port_group *pg;
- HMAP_FOR_EACH (pg, key_node, port_groups) {
- if (ovn_port_group_ls_find(pg, &od->nbs->header_.uuid)) {
- for (size_t i = 0; i < pg->nb_pg->n_acls; i++) {
- consider_acl(lflows, od, pg->nb_pg->acls[i], has_stateful);
- }
- }
- }
-
- /* Add 34000 priority flow to allow DHCP reply from ovn-controller to all
- * logical ports of the datapath if the CMS has configured DHCPv4 options.
- * */
- for (size_t i = 0; i < od->nbs->n_ports; i++) {
- if (lsp_is_external(od->nbs->ports[i])) {
- continue;
- }
-
- if (od->nbs->ports[i]->dhcpv4_options) {
- const char *server_id = smap_get(
- &od->nbs->ports[i]->dhcpv4_options->options, "server_id");
- const char *server_mac = smap_get(
- &od->nbs->ports[i]->dhcpv4_options->options, "server_mac");
- const char *lease_time = smap_get(
- &od->nbs->ports[i]->dhcpv4_options->options, "lease_time");
- if (server_id && server_mac && lease_time) {
- struct ds match = DS_EMPTY_INITIALIZER;
- const char *actions =
- has_stateful ? "ct_commit; next;" : "next;";
- ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
- "&& ip4.src == %s && udp && udp.src == 67 "
- "&& udp.dst == 68", od->nbs->ports[i]->name,
- server_mac, server_id);
- ovn_lflow_add(
- lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
- actions);
- ds_destroy(&match);
- }
- }
-
- if (od->nbs->ports[i]->dhcpv6_options) {
- const char *server_mac = smap_get(
- &od->nbs->ports[i]->dhcpv6_options->options, "server_id");
- struct eth_addr ea;
- if (server_mac && eth_addr_from_string(server_mac, &ea)) {
- /* Get the link local IP of the DHCPv6 server from the
- * server MAC. */
- struct in6_addr lla;
- in6_generate_lla(ea, &lla);
-
- char server_ip[INET6_ADDRSTRLEN + 1];
- ipv6_string_mapped(server_ip, &lla);
-
- struct ds match = DS_EMPTY_INITIALIZER;
- const char *actions = has_stateful ? "ct_commit; next;" :
- "next;";
- ds_put_format(&match, "outport == \"%s\" && eth.src == %s "
- "&& ip6.src == %s && udp && udp.src == 547 "
- "&& udp.dst == 546", od->nbs->ports[i]->name,
- server_mac, server_ip);
- ovn_lflow_add(
- lflows, od, S_SWITCH_OUT_ACL, 34000, ds_cstr(&match),
- actions);
- ds_destroy(&match);
- }
- }
- }
-
- /* Add a 34000 priority flow to advance the DNS reply from ovn-controller,
- * if the CMS has configured DNS records for the datapath.
- */
- if (ls_has_dns_records(od->nbs)) {
- const char *actions = has_stateful ? "ct_commit; next;" : "next;";
- ovn_lflow_add(
- lflows, od, S_SWITCH_OUT_ACL, 34000, "udp.src == 53",
- actions);
- }
-}
-
-static void
-build_qos(struct ovn_datapath *od, struct hmap *lflows) {
- ovn_lflow_add(lflows, od, S_SWITCH_IN_QOS_MARK, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_QOS_MARK, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_QOS_METER, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_QOS_METER, 0, "1", "next;");
-
- for (size_t i = 0; i < od->nbs->n_qos_rules; i++) {
- struct nbrec_qos *qos = od->nbs->qos_rules[i];
- bool ingress = !strcmp(qos->direction, "from-lport") ? true :false;
- enum ovn_stage stage = ingress ? S_SWITCH_IN_QOS_MARK : S_SWITCH_OUT_QOS_MARK;
- int64_t rate = 0;
- int64_t burst = 0;
-
- for (size_t j = 0; j < qos->n_action; j++) {
- if (!strcmp(qos->key_action[j], "dscp")) {
- struct ds dscp_action = DS_EMPTY_INITIALIZER;
-
- ds_put_format(&dscp_action, "ip.dscp = %"PRId64"; next;",
- qos->value_action[j]);
- ovn_lflow_add(lflows, od, stage,
- qos->priority,
- qos->match, ds_cstr(&dscp_action));
- ds_destroy(&dscp_action);
- }
- }
-
- for (size_t n = 0; n < qos->n_bandwidth; n++) {
- if (!strcmp(qos->key_bandwidth[n], "rate")) {
- rate = qos->value_bandwidth[n];
- } else if (!strcmp(qos->key_bandwidth[n], "burst")) {
- burst = qos->value_bandwidth[n];
- }
- }
- if (rate) {
- struct ds meter_action = DS_EMPTY_INITIALIZER;
- stage = ingress ? S_SWITCH_IN_QOS_METER : S_SWITCH_OUT_QOS_METER;
- if (burst) {
- ds_put_format(&meter_action,
- "set_meter(%"PRId64", %"PRId64"); next;",
- rate, burst);
- } else {
- ds_put_format(&meter_action,
- "set_meter(%"PRId64"); next;",
- rate);
- }
-
- /* Ingress and Egress QoS Meter Table.
- *
- * We limit the bandwidth of this flow by adding a meter table.
- */
- ovn_lflow_add(lflows, od, stage,
- qos->priority,
- qos->match, ds_cstr(&meter_action));
- ds_destroy(&meter_action);
- }
- }
-}
-
-static void
-build_lb(struct ovn_datapath *od, struct hmap *lflows)
-{
- /* Ingress and Egress LB Table (Priority 0): Packets are allowed by
- * default. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, 0, "1", "next;");
-
- if (od->nbs->load_balancer) {
- /* Ingress and Egress LB Table (Priority 65535).
- *
- * Send established traffic through conntrack for just NAT. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_LB, UINT16_MAX,
- "ct.est && !ct.rel && !ct.new && !ct.inv",
- REGBIT_CONNTRACK_NAT" = 1; next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_LB, UINT16_MAX,
- "ct.est && !ct.rel && !ct.new && !ct.inv",
- REGBIT_CONNTRACK_NAT" = 1; next;");
- }
-}
-
-static void
-build_stateful(struct ovn_datapath *od, struct hmap *lflows)
-{
- /* Ingress and Egress stateful Table (Priority 0): Packets are
- * allowed by default. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 0, "1", "next;");
-
- /* If REGBIT_CONNTRACK_COMMIT is set as 1, then the packets should be
- * committed to conntrack. We always set ct_label.blocked to 0 here as
- * any packet that makes it this far is part of a connection we
- * want to allow to continue. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
- REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
- REGBIT_CONNTRACK_COMMIT" == 1", "ct_commit(ct_label=0/1); next;");
-
- /* If REGBIT_CONNTRACK_NAT is set as 1, then packets should just be sent
- * through nat (without committing).
- *
- * REGBIT_CONNTRACK_COMMIT is set for new connections and
- * REGBIT_CONNTRACK_NAT is set for established connections. So they
- * don't overlap.
- */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL, 100,
- REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_STATEFUL, 100,
- REGBIT_CONNTRACK_NAT" == 1", "ct_lb;");
-
- /* Load balancing rules for new connections get committed to conntrack
- * table. So even if REGBIT_CONNTRACK_COMMIT is set in a previous table
- * a higher priority rule for load balancing below also commits the
- * connection, so it is okay if we do not hit the above match on
- * REGBIT_CONNTRACK_COMMIT. */
- for (int i = 0; i < od->nbs->n_load_balancer; i++) {
- struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
- struct smap *vips = &lb->vips;
- struct smap_node *node;
-
- SMAP_FOR_EACH (node, vips) {
- uint16_t port = 0;
- int addr_family;
-
- /* node->key contains IP:port or just IP. */
- char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
- &addr_family);
- if (!ip_address) {
- continue;
- }
-
- /* New connections in Ingress table. */
- char *action = xasprintf("ct_lb(%s);", node->value);
- struct ds match = DS_EMPTY_INITIALIZER;
- if (addr_family == AF_INET) {
- ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address);
- } else {
- ds_put_format(&match, "ct.new && ip6.dst == %s", ip_address);
- }
- if (port) {
- if (lb->protocol && !strcmp(lb->protocol, "udp")) {
- ds_put_format(&match, " && udp.dst == %d", port);
- } else {
- ds_put_format(&match, " && tcp.dst == %d", port);
- }
- ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
- 120, ds_cstr(&match), action);
- } else {
- ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
- 110, ds_cstr(&match), action);
- }
-
- free(ip_address);
- ds_destroy(&match);
- free(action);
- }
- }
-}
-
-static void
-build_lrouter_groups__(struct hmap *ports, struct ovn_datapath *od)
-{
- ovs_assert((od && od->nbr && od->lr_group));
-
- if (od->l3dgw_port && od->l3redirect_port) {
- /* It's a logical router with gateway port. If it
- * has HA_Chassis_Group associated to it in SB DB, then store the
- * ha chassis group name. */
- if (od->l3redirect_port->sb->ha_chassis_group) {
- sset_add(&od->lr_group->ha_chassis_groups,
- od->l3redirect_port->sb->ha_chassis_group->name);
- }
- }
-
- for (size_t i = 0; i < od->nbr->n_ports; i++) {
- struct ovn_port *router_port =
- ovn_port_find(ports, od->nbr->ports[i]->name);
-
- if (!router_port || !router_port->peer) {
- continue;
- }
-
- /* Get the peer logical switch/logical router datapath. */
- struct ovn_datapath *peer_dp = router_port->peer->od;
- if (peer_dp->nbr) {
- if (!peer_dp->lr_group) {
- peer_dp->lr_group = od->lr_group;
- od->lr_group->router_dps[od->lr_group->n_router_dps++]
- = peer_dp;
- build_lrouter_groups__(ports, peer_dp);
- }
- } else {
- for (size_t j = 0; j < peer_dp->n_router_ports; j++) {
- if (!peer_dp->router_ports[j]->peer) {
- /* If there is no peer port connecting to the
- * router port, ignore it. */
- continue;
- }
-
- struct ovn_datapath *router_dp;
- router_dp = peer_dp->router_ports[j]->peer->od;
- if (router_dp == od) {
- continue;
- }
-
- if (router_dp->lr_group == od->lr_group) {
- /* 'router_dp' and 'od' already belong to the same
- * lrouter group. Nothing to be done. */
- continue;
- }
-
- router_dp->lr_group = od->lr_group;
- od->lr_group->router_dps[od->lr_group->n_router_dps++]
- = router_dp;
- build_lrouter_groups__(ports, router_dp);
- }
- }
- }
-}
-
-/* Adds each logical router into a logical router group. All the
- * logical routers which belong to a group are connected to
- * each other either directly or indirectly (via transit logical switches
- * in between).
- *
- * Suppose if 'lr_list' has lr0, lr1, lr2, lr3, lr4, lr5
- * and the topology is like
- * sw0 <-> lr0 <-> sw1 <-> lr1 <->sw2 <-> lr2
- * sw3 <-> lr3 <-> lr4 <-> sw5
- * sw6 <-> lr5 <-> sw7
- * Then 3 groups are created.
- * Group 1 -> lr0, lr1 and lr2
- * lr0, lr1 and lr2's ovn_datapath->lr_group will point to this
- * group. This means sw0's logical ports can send packets to sw2's
- * logical ports if proper static route's are added.
- * Group 2 -> lr3 and lr4
- * lr3 and lr4's ovn_datapath->lr_group will point to this group.
- * Group 3 -> lr5
- *
- * Each logical router can belong to only one group.
- */
-static void
-build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list)
-{
- struct ovn_datapath *od;
- size_t n_router_dps = ovs_list_size(lr_list);
-
- LIST_FOR_EACH (od, lr_list, lr_list) {
- if (!od->lr_group) {
- od->lr_group = xzalloc(sizeof *od->lr_group);
- /* Each logical router group can have max
- * 'n_router_dps'. So allocate enough memory. */
- od->lr_group->router_dps = xcalloc(sizeof *od, n_router_dps);
- od->lr_group->router_dps[0] = od;
- od->lr_group->n_router_dps = 1;
- sset_init(&od->lr_group->ha_chassis_groups);
- build_lrouter_groups__(ports, od);
- }
- }
-}
-
-static void
-build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
- struct hmap *port_groups, struct hmap *lflows,
- struct hmap *mcgroups, struct hmap *igmp_groups)
-{
- /* This flow table structure is documented in ovn-northd(8), so please
- * update ovn-northd.8.xml if you change anything. */
-
- struct ds match = DS_EMPTY_INITIALIZER;
- struct ds actions = DS_EMPTY_INITIALIZER;
-
- /* Build pre-ACL and ACL tables for both ingress and egress.
- * Ingress tables 3 through 10. Egress tables 0 through 7. */
- struct ovn_datapath *od;
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- build_pre_acls(od, lflows);
- build_pre_lb(od, lflows);
- build_pre_stateful(od, lflows);
- build_acls(od, lflows, port_groups);
- build_qos(od, lflows);
- build_lb(od, lflows);
- build_stateful(od, lflows);
- }
-
- /* Logical switch ingress table 0: Admission control framework (priority
- * 100). */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- /* Logical VLANs not supported. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 100, "vlan.present",
- "drop;");
-
- /* Broadcast/multicast source address is invalid. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_L2, 100, "eth.src[40]",
- "drop;");
-
- /* Port security flows have priority 50 (see below) and will continue
- * to the next table if packet source is acceptable. */
- }
-
- /* Logical switch ingress table 0: Ingress port security - L2
- * (priority 50).
- * Ingress table 1: Ingress port security - IP (priority 90 and 80)
- * Ingress table 2: Ingress port security - ND (priority 90 and 80)
- */
- struct ovn_port *op;
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp) {
- continue;
- }
-
- if (!lsp_is_enabled(op->nbsp)) {
- /* Drop packets from disabled logical ports (since logical flow
- * tables are default-drop). */
- continue;
- }
-
- if (lsp_is_external(op->nbsp)) {
- continue;
- }
-
- ds_clear(&match);
- ds_clear(&actions);
- ds_put_format(&match, "inport == %s", op->json_key);
- build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs,
- &match);
-
- const char *queue_id = smap_get(&op->sb->options, "qdisc_queue_id");
- if (queue_id) {
- ds_put_format(&actions, "set_queue(%s); ", queue_id);
- }
- ds_put_cstr(&actions, "next;");
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
- ds_cstr(&match), ds_cstr(&actions));
-
- if (op->nbsp->n_port_security) {
- build_port_security_ip(P_IN, op, lflows);
- build_port_security_nd(op, lflows);
- }
- }
-
- /* Ingress table 1 and 2: Port security - IP and ND, by default goto next.
- * (priority 0)*/
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;");
- }
-
- /* Ingress table 11: ARP/ND responder, skip requests coming from localnet
- * and vtep ports. (priority 100); see ovn-northd.8.xml for the
- * rationale. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp) {
- continue;
- }
-
- if ((!strcmp(op->nbsp->type, "localnet")) ||
- (!strcmp(op->nbsp->type, "vtep"))) {
- ds_clear(&match);
- ds_put_format(&match, "inport == %s", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
- ds_cstr(&match), "next;");
- }
- }
-
- /* Ingress table 11: ARP/ND responder, reply for known IPs.
- * (priority 50). */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp) {
- continue;
- }
-
- /*
- * Add ARP/ND reply flows if either the
- * - port is up or
- * - port type is router or
- * - port type is localport
- */
- if (!lsp_is_up(op->nbsp) && strcmp(op->nbsp->type, "router") &&
- strcmp(op->nbsp->type, "localport")) {
- continue;
- }
-
- if (lsp_is_external(op->nbsp)) {
- continue;
- }
-
- for (size_t i = 0; i < op->n_lsp_addrs; i++) {
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
- ds_clear(&match);
- ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
- op->lsp_addrs[i].ipv4_addrs[j].addr_s);
- ds_clear(&actions);
- ds_put_format(&actions,
- "eth.dst = eth.src; "
- "eth.src = %s; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; "
- "arp.sha = %s; "
- "arp.tpa = arp.spa; "
- "arp.spa = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output;",
- op->lsp_addrs[i].ea_s, op->lsp_addrs[i].ea_s,
- op->lsp_addrs[i].ipv4_addrs[j].addr_s);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* Do not reply to an ARP request from the port that owns the
- * address (otherwise a DHCP client that ARPs to check for a
- * duplicate address will fail). Instead, forward it the usual
- * way.
- *
- * (Another alternative would be to simply drop the packet. If
- * everything is working as it is configured, then this would
- * produce equivalent results, since no one should reply to the
- * request. But ARPing for one's own IP address is intended to
- * detect situations where the network is not working as
- * configured, so dropping the request would frustrate that
- * intent.) */
- ds_put_format(&match, " && inport == %s", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
- ds_cstr(&match), "next;");
- }
-
- /* For ND solicitations, we need to listen for both the
- * unicast IPv6 address and its all-nodes multicast address,
- * but always respond with the unicast IPv6 address. */
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
- ds_clear(&match);
- ds_put_format(&match,
- "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
- op->lsp_addrs[i].ipv6_addrs[j].addr_s,
- op->lsp_addrs[i].ipv6_addrs[j].sn_addr_s,
- op->lsp_addrs[i].ipv6_addrs[j].addr_s);
-
- ds_clear(&actions);
- ds_put_format(&actions,
- "%s { "
- "eth.src = %s; "
- "ip6.src = %s; "
- "nd.target = %s; "
- "nd.tll = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output; "
- "};",
- !strcmp(op->nbsp->type, "router") ?
- "nd_na_router" : "nd_na",
- op->lsp_addrs[i].ea_s,
- op->lsp_addrs[i].ipv6_addrs[j].addr_s,
- op->lsp_addrs[i].ipv6_addrs[j].addr_s,
- op->lsp_addrs[i].ea_s);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 50,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* Do not reply to a solicitation from the port that owns the
- * address (otherwise DAD detection will fail). */
- ds_put_format(&match, " && inport == %s", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_ARP_ND_RSP, 100,
- ds_cstr(&match), "next;");
- }
- }
- }
-
- /* Ingress table 11: ARP/ND responder, by default goto next.
- * (priority 0)*/
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
- }
-
- /* Logical switch ingress table 12 and 13: DHCP options and response
- * priority 100 flows. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp) {
- continue;
- }
-
- if (!lsp_is_enabled(op->nbsp) || !strcmp(op->nbsp->type, "router")) {
- /* Don't add the DHCP flows if the port is not enabled or if the
- * port is a router port. */
- continue;
- }
-
- if (!op->nbsp->dhcpv4_options && !op->nbsp->dhcpv6_options) {
- /* CMS has disabled both native DHCPv4 and DHCPv6 for this lport.
- */
- continue;
- }
-
- bool is_external = lsp_is_external(op->nbsp);
- if (is_external && (!op->od->localnet_port ||
- !op->nbsp->ha_chassis_group)) {
- /* If it's an external port and there is no localnet port
- * and if it doesn't belong to an HA chassis group ignore it. */
- continue;
- }
-
- for (size_t i = 0; i < op->n_lsp_addrs; i++) {
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
- struct ds options_action = DS_EMPTY_INITIALIZER;
- struct ds response_action = DS_EMPTY_INITIALIZER;
- struct ds ipv4_addr_match = DS_EMPTY_INITIALIZER;
- if (build_dhcpv4_action(
- op, op->lsp_addrs[i].ipv4_addrs[j].addr,
- &options_action, &response_action, &ipv4_addr_match)) {
- ds_clear(&match);
- ds_put_format(
- &match, "inport == %s && eth.src == %s && "
- "ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && "
- "udp.src == 68 && udp.dst == 67",
- is_external ? op->od->localnet_port->json_key :
- op->json_key,
- op->lsp_addrs[i].ea_s);
-
- if (is_external) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->json_key);
- }
-
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
- 100, ds_cstr(&match),
- ds_cstr(&options_action));
- ds_clear(&match);
- /* Allow ip4.src = OFFER_IP and
- * ip4.dst = {SERVER_IP, 255.255.255.255} for the below
- * cases
- * - When the client wants to renew the IP by sending
- * the DHCPREQUEST to the server ip.
- * - When the client wants to renew the IP by
- * broadcasting the DHCPREQUEST.
- */
- ds_put_format(
- &match, "inport == %s && eth.src == %s && "
- "%s && udp.src == 68 && udp.dst == 67",
- is_external ? op->od->localnet_port->json_key :
- op->json_key,
- op->lsp_addrs[i].ea_s, ds_cstr(&ipv4_addr_match));
-
- if (is_external) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->json_key);
- }
-
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS,
- 100, ds_cstr(&match),
- ds_cstr(&options_action));
- ds_clear(&match);
-
- /* If REGBIT_DHCP_OPTS_RESULT is set, it means the
- * put_dhcp_opts action is successful. */
- ds_put_format(
- &match, "inport == %s && eth.src == %s && "
- "ip4 && udp.src == 68 && udp.dst == 67"
- " && "REGBIT_DHCP_OPTS_RESULT,
- is_external ? op->od->localnet_port->json_key :
- op->json_key,
- op->lsp_addrs[i].ea_s);
-
- if (is_external) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->json_key);
- }
-
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE,
- 100, ds_cstr(&match),
- ds_cstr(&response_action));
- ds_destroy(&options_action);
- ds_destroy(&response_action);
- ds_destroy(&ipv4_addr_match);
- break;
- }
- }
-
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
- struct ds options_action = DS_EMPTY_INITIALIZER;
- struct ds response_action = DS_EMPTY_INITIALIZER;
- if (build_dhcpv6_action(
- op, &op->lsp_addrs[i].ipv6_addrs[j].addr,
- &options_action, &response_action)) {
- ds_clear(&match);
- ds_put_format(
- &match, "inport == %s && eth.src == %s"
- " && ip6.dst == ff02::1:2 && udp.src == 546 &&"
- " udp.dst == 547",
- is_external ? op->od->localnet_port->json_key :
- op->json_key,
- op->lsp_addrs[i].ea_s);
-
- if (is_external) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->json_key);
- }
-
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_OPTIONS, 100,
- ds_cstr(&match), ds_cstr(&options_action));
-
- /* If REGBIT_DHCP_OPTS_RESULT is set to 1, it means the
- * put_dhcpv6_opts action is successful */
- ds_put_cstr(&match, " && "REGBIT_DHCP_OPTS_RESULT);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_DHCP_RESPONSE, 100,
- ds_cstr(&match), ds_cstr(&response_action));
- ds_destroy(&options_action);
- ds_destroy(&response_action);
- break;
- }
- }
- }
- }
-
- /* Logical switch ingress table 14 and 15: DNS lookup and response
- * priority 100 flows.
- */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs || !ls_has_dns_records(od->nbs)) {
- continue;
- }
-
- struct ds action = DS_EMPTY_INITIALIZER;
-
- ds_clear(&match);
- ds_put_cstr(&match, "udp.dst == 53");
- ds_put_format(&action,
- REGBIT_DNS_LOOKUP_RESULT" = dns_lookup(); next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 100,
- ds_cstr(&match), ds_cstr(&action));
- ds_clear(&action);
- ds_put_cstr(&match, " && "REGBIT_DNS_LOOKUP_RESULT);
- ds_put_format(&action, "eth.dst <-> eth.src; ip4.src <-> ip4.dst; "
- "udp.dst = udp.src; udp.src = 53; outport = inport; "
- "flags.loopback = 1; output;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 100,
- ds_cstr(&match), ds_cstr(&action));
- ds_clear(&action);
- ds_put_format(&action, "eth.dst <-> eth.src; ip6.src <-> ip6.dst; "
- "udp.dst = udp.src; udp.src = 53; outport = inport; "
- "flags.loopback = 1; output;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 100,
- ds_cstr(&match), ds_cstr(&action));
- ds_destroy(&action);
- }
-
- /* Ingress table 12 and 13: DHCP options and response, by default goto
- * next. (priority 0).
- * Ingress table 14 and 15: DNS lookup and response, by default goto next.
- * (priority 0).
- * Ingress table 16 - External port handling, by default goto next.
- * (priority 0). */
-
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_OPTIONS, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DHCP_RESPONSE, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_LOOKUP, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_DNS_RESPONSE, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_IN_EXTERNAL_PORT, 0, "1", "next;");
- }
-
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp || !lsp_is_external(op->nbsp) ||
- !op->od->localnet_port) {
- continue;
- }
-
- /* Table 16: External port. Drop ARP request for router ips from
- * external ports on chassis not binding those ports.
- * This makes the router pipeline to be run only on the chassis
- * binding the external ports. */
-
- for (size_t i = 0; i < op->n_lsp_addrs; i++) {
- for (size_t j = 0; j < op->od->n_router_ports; j++) {
- struct ovn_port *rp = op->od->router_ports[j];
- for (size_t k = 0; k < rp->n_lsp_addrs; k++) {
- for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv4_addrs;
- l++) {
- ds_clear(&match);
- ds_put_format(
- &match, "inport == %s && eth.src == %s"
- " && !is_chassis_resident(%s)"
- " && arp.tpa == %s && arp.op == 1",
- op->od->localnet_port->json_key,
- op->lsp_addrs[i].ea_s, op->json_key,
- rp->lsp_addrs[k].ipv4_addrs[l].addr_s);
- ovn_lflow_add(lflows, op->od,
- S_SWITCH_IN_EXTERNAL_PORT, 100,
- ds_cstr(&match), "drop;");
- }
- for (size_t l = 0; l < rp->lsp_addrs[k].n_ipv6_addrs;
- l++) {
- ds_clear(&match);
- ds_put_format(
- &match, "inport == %s && eth.src == %s"
- " && !is_chassis_resident(%s)"
- " && nd_ns && ip6.dst == {%s, %s} && "
- "nd.target == %s",
- op->od->localnet_port->json_key,
- op->lsp_addrs[i].ea_s, op->json_key,
- rp->lsp_addrs[k].ipv6_addrs[l].addr_s,
- rp->lsp_addrs[k].ipv6_addrs[l].sn_addr_s,
- rp->lsp_addrs[k].ipv6_addrs[l].addr_s);
- ovn_lflow_add(lflows, op->od,
- S_SWITCH_IN_EXTERNAL_PORT, 100,
- ds_cstr(&match), "drop;");
- }
- }
- }
- }
- }
-
- /* Ingress table 17: Destination lookup, broadcast and multicast handling
- * (priority 70 - 100). */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- if (od->mcast_info.enabled) {
- /* Punt IGMP traffic to controller. */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
- "ip4 && ip.proto == 2", "igmp;");
-
- /* Flood all IP multicast traffic destined to 224.0.0.X to all
- * ports - RFC 4541, section 2.1.2, item 2.
- */
- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 85,
- "ip4 && ip4.dst == 224.0.0.0/24",
- "outport = \""MC_FLOOD"\"; output;");
-
- /* Drop unregistered IP multicast if not allowed. */
- if (!od->mcast_info.flood_unregistered) {
- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
- "ip4 && ip4.mcast", "drop;");
- }
- }
-
- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 70, "eth.mcast",
- "outport = \""MC_FLOOD"\"; output;");
- }
-
- /* Ingress table 17: Add IP multicast flows learnt from IGMP
- * (priority 90). */
- struct ovn_igmp_group *igmp_group, *next_igmp_group;
-
- HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, igmp_groups) {
- ds_clear(&match);
- ds_clear(&actions);
-
- if (!igmp_group->datapath) {
- continue;
- }
-
- struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info;
-
- if (mcast_info->active_flows >= mcast_info->table_size) {
- continue;
- }
- mcast_info->active_flows++;
-
- ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ",
- igmp_group->mcgroup.name);
- ds_put_format(&actions, "outport = \"%s\"; output; ",
- igmp_group->mcgroup.name);
-
- ovn_lflow_add(lflows, igmp_group->datapath, S_SWITCH_IN_L2_LKUP, 90,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* Ingress table 17: Destination lookup, unicast handling (priority 50), */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp || lsp_is_external(op->nbsp)) {
- continue;
- }
-
- for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
- /* Addresses are owned by the logical port.
- * Ethernet address followed by zero or more IPv4
- * or IPv6 addresses (or both). */
- struct eth_addr mac;
- if (ovs_scan(op->nbsp->addresses[i],
- ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
- ds_clear(&match);
- ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
- ETH_ADDR_ARGS(mac));
-
- ds_clear(&actions);
- ds_put_format(&actions, "outport = %s; output;", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
- ds_cstr(&match), ds_cstr(&actions));
- } else if (!strcmp(op->nbsp->addresses[i], "unknown")) {
- if (lsp_is_enabled(op->nbsp)) {
- ovn_multicast_add(mcgroups, &mc_unknown, op);
- op->od->has_unknown = true;
- }
- } else if (is_dynamic_lsp_address(op->nbsp->addresses[i])) {
- if (!op->nbsp->dynamic_addresses
- || !ovs_scan(op->nbsp->dynamic_addresses,
- ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
- continue;
- }
- ds_clear(&match);
- ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
- ETH_ADDR_ARGS(mac));
-
- ds_clear(&actions);
- ds_put_format(&actions, "outport = %s; output;", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
- ds_cstr(&match), ds_cstr(&actions));
- } else if (!strcmp(op->nbsp->addresses[i], "router")) {
- if (!op->peer || !op->peer->nbrp
- || !ovs_scan(op->peer->nbrp->mac,
- ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
- continue;
- }
- ds_clear(&match);
- ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT,
- ETH_ADDR_ARGS(mac));
- if (op->peer->od->l3dgw_port
- && op->peer->od->l3redirect_port
- && op->od->localnet_port) {
- bool add_chassis_resident_check = false;
- if (op->peer == op->peer->od->l3dgw_port) {
- /* The peer of this port represents a distributed
- * gateway port. The destination lookup flow for the
- * router's distributed gateway port MAC address should
- * only be programmed on the "redirect-chassis". */
- add_chassis_resident_check = true;
- } else {
- /* Check if the option 'reside-on-redirect-chassis'
- * is set to true on the peer port. If set to true
- * and if the logical switch has a localnet port, it
- * means the router pipeline for the packets from
- * this logical switch should be run on the chassis
- * hosting the gateway port.
- */
- add_chassis_resident_check = smap_get_bool(
- &op->peer->nbrp->options,
- "reside-on-redirect-chassis", false);
- }
-
- if (add_chassis_resident_check) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->peer->od->l3redirect_port->json_key);
- }
- }
-
- ds_clear(&actions);
- ds_put_format(&actions, "outport = %s; output;", op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP, 50,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* Add ethernet addresses specified in NAT rules on
- * distributed logical routers. */
- if (op->peer->od->l3dgw_port
- && op->peer == op->peer->od->l3dgw_port) {
- for (int j = 0; j < op->peer->od->nbr->n_nat; j++) {
- const struct nbrec_nat *nat
- = op->peer->od->nbr->nat[j];
- if (!strcmp(nat->type, "dnat_and_snat")
- && nat->logical_port && nat->external_mac
- && eth_addr_from_string(nat->external_mac, &mac)) {
-
- ds_clear(&match);
- ds_put_format(&match, "eth.dst == "ETH_ADDR_FMT
- " && is_chassis_resident(\"%s\")",
- ETH_ADDR_ARGS(mac),
- nat->logical_port);
-
- ds_clear(&actions);
- ds_put_format(&actions, "outport = %s; output;",
- op->json_key);
- ovn_lflow_add(lflows, op->od, S_SWITCH_IN_L2_LKUP,
- 50, ds_cstr(&match),
- ds_cstr(&actions));
- }
- }
- }
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
-
- VLOG_INFO_RL(&rl,
- "%s: invalid syntax '%s' in addresses column",
- op->nbsp->name, op->nbsp->addresses[i]);
- }
- }
- }
-
- /* Ingress table 17: Destination lookup for unknown MACs (priority 0). */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- if (od->has_unknown) {
- ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 0, "1",
- "outport = \""MC_UNKNOWN"\"; output;");
- }
- }
-
- /* Egress tables 8: Egress port security - IP (priority 0)
- * Egress table 9: Egress port security L2 - multicast/broadcast
- * (priority 100). */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast",
- "output;");
- }
-
- /* Egress table 8: Egress port security - IP (priorities 90 and 80)
- * if port security enabled.
- *
- * Egress table 9: Egress port security - L2 (priorities 50 and 150).
- *
- * Priority 50 rules implement port security for enabled logical port.
- *
- * Priority 150 rules drop packets to disabled logical ports, so that they
- * don't even receive multicast or broadcast packets. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp || lsp_is_external(op->nbsp)) {
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "outport == %s", op->json_key);
- if (lsp_is_enabled(op->nbsp)) {
- build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs,
- &match);
- ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
- ds_cstr(&match), "output;");
- } else {
- ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
- ds_cstr(&match), "drop;");
- }
-
- if (op->nbsp->n_port_security) {
- build_port_security_ip(P_OUT, op, lflows);
- }
- }
-
- ds_destroy(&match);
- ds_destroy(&actions);
-}
-
-static bool
-lrport_is_enabled(const struct nbrec_logical_router_port *lrport)
-{
- return !lrport->enabled || *lrport->enabled;
-}
-
-/* Returns a string of the IP address of the router port 'op' that
- * overlaps with 'ip_s". If one is not found, returns NULL.
- *
- * The caller must not free the returned string. */
-static const char *
-find_lrp_member_ip(const struct ovn_port *op, const char *ip_s)
-{
- bool is_ipv4 = strchr(ip_s, '.') ? true : false;
-
- if (is_ipv4) {
- ovs_be32 ip;
-
- if (!ip_parse(ip_s, &ip)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address %s", ip_s);
- return NULL;
- }
-
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- const struct ipv4_netaddr *na = &op->lrp_networks.ipv4_addrs[i];
-
- if (!((na->network ^ ip) & na->mask)) {
- /* There should be only 1 interface that matches the
- * supplied IP. Otherwise, it's a configuration error,
- * because subnets of a router's interfaces should NOT
- * overlap. */
- return na->addr_s;
- }
- }
- } else {
- struct in6_addr ip6;
-
- if (!ipv6_parse(ip_s, &ip6)) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ipv6 address %s", ip_s);
- return NULL;
- }
-
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- const struct ipv6_netaddr *na = &op->lrp_networks.ipv6_addrs[i];
- struct in6_addr xor_addr = ipv6_addr_bitxor(&na->network, &ip6);
- struct in6_addr and_addr = ipv6_addr_bitand(&xor_addr, &na->mask);
-
- if (ipv6_is_zero(&and_addr)) {
- /* There should be only 1 interface that matches the
- * supplied IP. Otherwise, it's a configuration error,
- * because subnets of a router's interfaces should NOT
- * overlap. */
- return na->addr_s;
- }
- }
- }
-
- return NULL;
-}
-
-static struct ovn_port*
-get_outport_for_routing_policy_nexthop(struct ovn_datapath *od,
- struct hmap *ports,
- int priority, const char *nexthop)
-{
- if (nexthop == NULL) {
- return NULL;
- }
-
- /* Find the router port matching the next hop. */
- for (int i = 0; i < od->nbr->n_ports; i++) {
- struct nbrec_logical_router_port *lrp = od->nbr->ports[i];
-
- struct ovn_port *out_port = ovn_port_find(ports, lrp->name);
- if (out_port && find_lrp_member_ip(out_port, nexthop)) {
- return out_port;
- }
- }
-
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "No path for routing policy priority %d; next hop %s",
- priority, nexthop);
- return NULL;
-}
-
-static void
-build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
- struct hmap *ports,
- const struct nbrec_logical_router_policy *rule)
-{
- struct ds match = DS_EMPTY_INITIALIZER;
- struct ds actions = DS_EMPTY_INITIALIZER;
-
- if (!strcmp(rule->action, "reroute")) {
- struct ovn_port *out_port = get_outport_for_routing_policy_nexthop(
- od, ports, rule->priority, rule->nexthop);
- if (!out_port) {
- return;
- }
-
- const char *lrp_addr_s = find_lrp_member_ip(out_port, rule->nexthop);
- if (!lrp_addr_s) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "lrp_addr not found for routing policy "
- " priority %"PRId64" nexthop %s",
- rule->priority, rule->nexthop);
- return;
- }
- bool is_ipv4 = strchr(rule->nexthop, '.') ? true : false;
- ds_put_format(&actions, "%sreg0 = %s; "
- "%sreg1 = %s; "
- "eth.src = %s; "
- "outport = %s; "
- "flags.loopback = 1; "
- "next;",
- is_ipv4 ? "" : "xx",
- rule->nexthop,
- is_ipv4 ? "" : "xx",
- lrp_addr_s,
- out_port->lrp_networks.ea_s,
- out_port->json_key);
-
- } else if (!strcmp(rule->action, "drop")) {
- ds_put_cstr(&actions, "drop;");
- } else if (!strcmp(rule->action, "allow")) {
- ds_put_cstr(&actions, "next;");
- }
- ds_put_format(&match, "%s", rule->match);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_POLICY, rule->priority,
- ds_cstr(&match), ds_cstr(&actions));
- ds_destroy(&match);
- ds_destroy(&actions);
-}
-
-static void
-add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
-{
- struct ds actions = DS_EMPTY_INITIALIZER;
- struct ds match = DS_EMPTY_INITIALIZER;
-
- if (!op->od->l3dgw_port) {
- return;
- }
-
- if (!op->peer || !op->peer->od->nbs) {
- return;
- }
-
- for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
- const struct nbrec_nat *nat = op->od->nbr->nat[i];
- bool found = false;
-
- if (strcmp(nat->type, "dnat_and_snat") ||
- !nat->external_mac || !nat->external_ip) {
- continue;
- }
-
- const struct ovn_datapath *peer_dp = op->peer->od;
- for (size_t j = 0; j < peer_dp->nbs->n_ports; j++) {
- if (!strcmp(peer_dp->nbs->ports[j]->name, nat->logical_port)) {
- found = true;
- break;
- }
- }
- if (!found) {
- continue;
- }
-
- ds_put_format(&match, "inport == %s && "
- "ip4.src == %s && ip4.dst == %s",
- op->json_key, nat->logical_ip, nat->external_ip);
- ds_put_format(&actions, "outport = %s; eth.dst = %s; "
- REGBIT_DISTRIBUTED_NAT" = 1; "
- REGBIT_NAT_REDIRECT" = 0; next;",
- op->od->l3dgw_port->json_key,
- nat->external_mac);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
- ds_cstr(&match), ds_cstr(&actions));
- ds_clear(&match);
- ds_clear(&actions);
-
- for (size_t j = 0; j < op->od->nbr->n_nat; j++) {
- const struct nbrec_nat *nat2 = op->od->nbr->nat[j];
-
- if (nat == nat2 || strcmp(nat2->type, "dnat_and_snat") ||
- !nat2->external_mac || !nat2->external_ip)
- continue;
-
- ds_put_format(&match, "inport == %s && "
- "ip4.src == %s && ip4.dst == %s",
- op->json_key, nat->logical_ip, nat2->external_ip);
- ds_put_format(&actions, "outport = %s; "
- "eth.src = %s; eth.dst = %s; "
- "reg0 = ip4.dst; reg1 = %s; "
- REGBIT_DISTRIBUTED_NAT" = 1; "
- REGBIT_NAT_REDIRECT" = 0; next;",
- op->od->l3dgw_port->json_key,
- op->od->l3dgw_port->lrp_networks.ea_s,
- nat2->external_mac, nat->external_ip);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
- ds_cstr(&match), ds_cstr(&actions));
- ds_clear(&match);
- ds_clear(&actions);
- }
- }
-}
-
-static void
-add_route(struct hmap *lflows, const struct ovn_port *op,
- const char *lrp_addr_s, const char *network_s, int plen,
- const char *gateway, const char *policy)
-{
- bool is_ipv4 = strchr(network_s, '.') ? true : false;
- struct ds match = DS_EMPTY_INITIALIZER;
- const char *dir;
- uint16_t priority;
-
- if (policy && !strcmp(policy, "src-ip")) {
- dir = "src";
- priority = plen * 2;
- } else {
- dir = "dst";
- priority = (plen * 2) + 1;
- }
-
- /* IPv6 link-local addresses must be scoped to the local router port. */
- if (!is_ipv4) {
- struct in6_addr network;
- ovs_assert(ipv6_parse(network_s, &network));
- if (in6_is_lla(&network)) {
- ds_put_format(&match, "inport == %s && ", op->json_key);
- }
- }
- ds_put_format(&match, "ip%s.%s == %s/%d", is_ipv4 ? "4" : "6", dir,
- network_s, plen);
-
- struct ds actions = DS_EMPTY_INITIALIZER;
- ds_put_format(&actions, "ip.ttl--; %sreg0 = ", is_ipv4 ? "" : "xx");
-
- if (gateway) {
- ds_put_cstr(&actions, gateway);
- } else {
- ds_put_format(&actions, "ip%s.dst", is_ipv4 ? "4" : "6");
- }
- ds_put_format(&actions, "; "
- "%sreg1 = %s; "
- "eth.src = %s; "
- "outport = %s; "
- "flags.loopback = 1; "
- "next;",
- is_ipv4 ? "" : "xx",
- lrp_addr_s,
- op->lrp_networks.ea_s,
- op->json_key);
-
- /* The priority here is calculated to implement longest-prefix-match
- * routing. */
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, priority,
- ds_cstr(&match), ds_cstr(&actions));
- ds_destroy(&match);
- ds_destroy(&actions);
-}
-
-static void
-build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
- struct hmap *ports,
- const struct nbrec_logical_router_static_route *route)
-{
- ovs_be32 nexthop;
- const char *lrp_addr_s = NULL;
- unsigned int plen;
- bool is_ipv4;
-
- /* Verify that the next hop is an IP address with an all-ones mask. */
- char *error = ip_parse_cidr(route->nexthop, &nexthop, &plen);
- if (!error) {
- if (plen != 32) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad next hop mask %s", route->nexthop);
- return;
- }
- is_ipv4 = true;
- } else {
- free(error);
-
- struct in6_addr ip6;
- error = ipv6_parse_cidr(route->nexthop, &ip6, &plen);
- if (!error) {
- if (plen != 128) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad next hop mask %s", route->nexthop);
- return;
- }
- is_ipv4 = false;
- } else {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad next hop ip address %s", route->nexthop);
- free(error);
- return;
- }
- }
-
- char *prefix_s;
- if (is_ipv4) {
- ovs_be32 prefix;
- /* Verify that ip prefix is a valid IPv4 address. */
- error = ip_parse_cidr(route->ip_prefix, &prefix, &plen);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
- route->ip_prefix);
- free(error);
- return;
- }
- prefix_s = xasprintf(IP_FMT, IP_ARGS(prefix & be32_prefix_mask(plen)));
- } else {
- /* Verify that ip prefix is a valid IPv6 address. */
- struct in6_addr prefix;
- error = ipv6_parse_cidr(route->ip_prefix, &prefix, &plen);
- if (error) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
- route->ip_prefix);
- free(error);
- return;
- }
- struct in6_addr mask = ipv6_create_mask(plen);
- struct in6_addr network = ipv6_addr_bitand(&prefix, &mask);
- prefix_s = xmalloc(INET6_ADDRSTRLEN);
- inet_ntop(AF_INET6, &network, prefix_s, INET6_ADDRSTRLEN);
- }
-
- /* Find the outgoing port. */
- struct ovn_port *out_port = NULL;
- if (route->output_port) {
- out_port = ovn_port_find(ports, route->output_port);
- if (!out_port) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "Bad out port %s for static route %s",
- route->output_port, route->ip_prefix);
- goto free_prefix_s;
- }
- lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
- if (!lrp_addr_s) {
- /* There are no IP networks configured on the router's port via
- * which 'route->nexthop' is theoretically reachable. But since
- * 'out_port' has been specified, we honor it by trying to reach
- * 'route->nexthop' via the first IP address of 'out_port'.
- * (There are cases, e.g in GCE, where each VM gets a /32 IP
- * address and the default gateway is still reachable from it.) */
- if (is_ipv4) {
- if (out_port->lrp_networks.n_ipv4_addrs) {
- lrp_addr_s = out_port->lrp_networks.ipv4_addrs[0].addr_s;
- }
- } else {
- if (out_port->lrp_networks.n_ipv6_addrs) {
- lrp_addr_s = out_port->lrp_networks.ipv6_addrs[0].addr_s;
- }
- }
- }
- } else {
- /* output_port is not specified, find the
- * router port matching the next hop. */
- int i;
- for (i = 0; i < od->nbr->n_ports; i++) {
- struct nbrec_logical_router_port *lrp = od->nbr->ports[i];
- out_port = ovn_port_find(ports, lrp->name);
- if (!out_port) {
- /* This should not happen. */
- continue;
- }
-
- lrp_addr_s = find_lrp_member_ip(out_port, route->nexthop);
- if (lrp_addr_s) {
- break;
- }
- }
- }
-
- if (!out_port || !lrp_addr_s) {
- /* There is no matched out port. */
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "No path for static route %s; next hop %s",
- route->ip_prefix, route->nexthop);
- goto free_prefix_s;
- }
-
- char *policy = route->policy ? route->policy : "dst-ip";
- add_route(lflows, out_port, lrp_addr_s, prefix_s, plen, route->nexthop,
- policy);
-
-free_prefix_s:
- free(prefix_s);
-}
-
-static void
-op_put_v4_networks(struct ds *ds, const struct ovn_port *op, bool add_bcast)
-{
- if (!add_bcast && op->lrp_networks.n_ipv4_addrs == 1) {
- ds_put_format(ds, "%s", op->lrp_networks.ipv4_addrs[0].addr_s);
- return;
- }
-
- ds_put_cstr(ds, "{");
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].addr_s);
- if (add_bcast) {
- ds_put_format(ds, "%s, ", op->lrp_networks.ipv4_addrs[i].bcast_s);
- }
- }
- ds_chomp(ds, ' ');
- ds_chomp(ds, ',');
- ds_put_cstr(ds, "}");
-}
-
-static void
-op_put_v6_networks(struct ds *ds, const struct ovn_port *op)
-{
- if (op->lrp_networks.n_ipv6_addrs == 1) {
- ds_put_format(ds, "%s", op->lrp_networks.ipv6_addrs[0].addr_s);
- return;
- }
-
- ds_put_cstr(ds, "{");
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- ds_put_format(ds, "%s, ", op->lrp_networks.ipv6_addrs[i].addr_s);
- }
- ds_chomp(ds, ' ');
- ds_chomp(ds, ',');
- ds_put_cstr(ds, "}");
-}
-
-static const char *
-get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip)
-{
- char *key = xasprintf("%s_force_snat_ip", key_type);
- const char *ip_address = smap_get(&od->nbr->options, key);
- free(key);
-
- if (ip_address) {
- ovs_be32 mask;
- char *error = ip_parse_masked(ip_address, ip, &mask);
- if (error || mask != OVS_BE32_MAX) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
- ip_address, UUID_ARGS(&od->key));
- free(error);
- *ip = 0;
- return NULL;
- }
- return ip_address;
- }
-
- *ip = 0;
- return NULL;
-}
-
-static void
-add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
- struct ds *match, struct ds *actions, int priority,
- const char *lb_force_snat_ip, char *backend_ips,
- bool is_udp, int addr_family)
-{
- /* A match and actions for new connections. */
- char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
- if (lb_force_snat_ip) {
- char *new_actions = xasprintf("flags.force_snat_for_lb = 1; %s",
- ds_cstr(actions));
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
- new_actions);
- free(new_actions);
- } else {
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, new_match,
- ds_cstr(actions));
- }
-
- /* A match and actions for established connections. */
- char *est_match = xasprintf("ct.est && %s", ds_cstr(match));
- if (lb_force_snat_ip) {
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
- "flags.force_snat_for_lb = 1; ct_dnat;");
- } else {
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, priority, est_match,
- "ct_dnat;");
- }
-
- free(new_match);
- free(est_match);
-
- if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) {
- return;
- }
-
- /* Add logical flows to UNDNAT the load balanced reverse traffic in
- * the router egress pipleine stage - S_ROUTER_OUT_UNDNAT if the logical
- * router has a gateway router port associated.
- */
- struct ds undnat_match = DS_EMPTY_INITIALIZER;
- if (addr_family == AF_INET) {
- ds_put_cstr(&undnat_match, "ip4 && (");
- } else {
- ds_put_cstr(&undnat_match, "ip6 && (");
- }
- char *start, *next, *ip_str;
- start = next = xstrdup(backend_ips);
- ip_str = strsep(&next, ",");
- bool backend_ips_found = false;
- while (ip_str && ip_str[0]) {
- char *ip_address = NULL;
- uint16_t port = 0;
- int addr_family_;
- ip_address_and_port_from_lb_key(ip_str, &ip_address, &port,
- &addr_family_);
- if (!ip_address) {
- break;
- }
-
- if (addr_family_ == AF_INET) {
- ds_put_format(&undnat_match, "(ip4.src == %s", ip_address);
- } else {
- ds_put_format(&undnat_match, "(ip6.src == %s", ip_address);
- }
- free(ip_address);
- if (port) {
- ds_put_format(&undnat_match, " && %s.src == %d) || ",
- is_udp ? "udp" : "tcp", port);
- } else {
- ds_put_cstr(&undnat_match, ") || ");
- }
- ip_str = strsep(&next, ",");
- backend_ips_found = true;
- }
-
- free(start);
- if (!backend_ips_found) {
- ds_destroy(&undnat_match);
- return;
- }
- ds_chomp(&undnat_match, ' ');
- ds_chomp(&undnat_match, '|');
- ds_chomp(&undnat_match, '|');
- ds_chomp(&undnat_match, ' ');
- ds_put_format(&undnat_match, ") && outport == %s && "
- "is_chassis_resident(%s)", od->l3dgw_port->json_key,
- od->l3redirect_port->json_key);
- if (lb_force_snat_ip) {
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
- ds_cstr(&undnat_match),
- "flags.force_snat_for_lb = 1; ct_dnat;");
- } else {
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 120,
- ds_cstr(&undnat_match), "ct_dnat;");
- }
-
- ds_destroy(&undnat_match);
-}
-
-#define ND_RA_MAX_INTERVAL_MAX 1800
-#define ND_RA_MAX_INTERVAL_MIN 4
-
-#define ND_RA_MIN_INTERVAL_MAX(max) ((max) * 3 / 4)
-#define ND_RA_MIN_INTERVAL_MIN 3
-
-static void
-copy_ra_to_sb(struct ovn_port *op, const char *address_mode)
-{
- struct smap options;
- smap_clone(&options, &op->sb->options);
-
- smap_add(&options, "ipv6_ra_send_periodic", "true");
- smap_add(&options, "ipv6_ra_address_mode", address_mode);
-
- int max_interval = smap_get_int(&op->nbrp->ipv6_ra_configs,
- "max_interval", ND_RA_MAX_INTERVAL_DEFAULT);
- if (max_interval > ND_RA_MAX_INTERVAL_MAX) {
- max_interval = ND_RA_MAX_INTERVAL_MAX;
- }
- if (max_interval < ND_RA_MAX_INTERVAL_MIN) {
- max_interval = ND_RA_MAX_INTERVAL_MIN;
- }
- smap_add_format(&options, "ipv6_ra_max_interval", "%d", max_interval);
-
- int min_interval = smap_get_int(&op->nbrp->ipv6_ra_configs,
- "min_interval", nd_ra_min_interval_default(max_interval));
- if (min_interval > ND_RA_MIN_INTERVAL_MAX(max_interval)) {
- min_interval = ND_RA_MIN_INTERVAL_MAX(max_interval);
- }
- if (min_interval < ND_RA_MIN_INTERVAL_MIN) {
- min_interval = ND_RA_MIN_INTERVAL_MIN;
- }
- smap_add_format(&options, "ipv6_ra_min_interval", "%d", min_interval);
-
- int mtu = smap_get_int(&op->nbrp->ipv6_ra_configs, "mtu", ND_MTU_DEFAULT);
- /* RFC 2460 requires the MTU for IPv6 to be at least 1280 */
- if (mtu && mtu >= 1280) {
- smap_add_format(&options, "ipv6_ra_mtu", "%d", mtu);
- }
-
- struct ds s = DS_EMPTY_INITIALIZER;
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; ++i) {
- struct ipv6_netaddr *addrs = &op->lrp_networks.ipv6_addrs[i];
- if (in6_is_lla(&addrs->network)) {
- smap_add(&options, "ipv6_ra_src_addr", addrs->addr_s);
- continue;
- }
- ds_put_format(&s, "%s/%u ", addrs->network_s, addrs->plen);
- }
- /* Remove trailing space */
- ds_chomp(&s, ' ');
- smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s));
- ds_destroy(&s);
-
- smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s);
-
- sbrec_port_binding_set_options(op->sb, &options);
- smap_destroy(&options);
-}
-
-static void
-build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
- struct hmap *lflows)
-{
- /* This flow table structure is documented in ovn-northd(8), so please
- * update ovn-northd.8.xml if you change anything. */
-
- struct ds match = DS_EMPTY_INITIALIZER;
- struct ds actions = DS_EMPTY_INITIALIZER;
-
- /* Logical router ingress table 0: Admission control framework. */
- struct ovn_datapath *od;
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- /* Logical VLANs not supported.
- * Broadcast/multicast source address is invalid. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 100,
- "vlan.present || eth.src[40]", "drop;");
- }
-
- /* Logical router ingress table 0: match (priority 50). */
- struct ovn_port *op;
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbrp) {
- continue;
- }
-
- if (!lrport_is_enabled(op->nbrp)) {
- /* Drop packets from disabled logical ports (since logical flow
- * tables are default-drop). */
- continue;
- }
-
- if (op->derived) {
- /* No ingress packets should be received on a chassisredirect
- * port. */
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "eth.mcast && inport == %s", op->json_key);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
- ds_cstr(&match), "next;");
-
- ds_clear(&match);
- ds_put_format(&match, "eth.dst == %s && inport == %s",
- op->lrp_networks.ea_s, op->json_key);
- if (op->od->l3dgw_port && op == op->od->l3dgw_port
- && op->od->l3redirect_port) {
- /* Traffic with eth.dst = l3dgw_port->lrp_networks.ea_s
- * should only be received on the "redirect-chassis". */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ADMISSION, 50,
- ds_cstr(&match), "next;");
- }
-
- /* Logical router ingress table 1: IP Input. */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- /* L3 admission control: drop multicast and broadcast source, localhost
- * source or destination, and zero network source or destination
- * (priority 100). */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 100,
- "ip4.mcast || "
- "ip4.src == 255.255.255.255 || "
- "ip4.src == 127.0.0.0/8 || "
- "ip4.dst == 127.0.0.0/8 || "
- "ip4.src == 0.0.0.0/8 || "
- "ip4.dst == 0.0.0.0/8",
- "drop;");
-
- /* ARP reply handling. Use ARP replies to populate the logical
- * router's ARP table. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2",
- "put_arp(inport, arp.spa, arp.sha);");
-
- /* Drop Ethernet local broadcast. By definition this traffic should
- * not be forwarded.*/
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 50,
- "eth.bcast", "drop;");
-
- /* TTL discard */
- ds_clear(&match);
- ds_put_cstr(&match, "ip4 && ip.ttl == {0, 1}");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
- ds_cstr(&match), "drop;");
-
- /* ND advertisement handling. Use advertisements to populate
- * the logical router's ARP/ND table. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_na",
- "put_nd(inport, nd.target, nd.tll);");
-
- /* Lean from neighbor solicitations that were not directed at
- * us. (A priority-90 flow will respond to requests to us and
- * learn the sender's mac address. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_ns",
- "put_nd(inport, ip6.src, nd.sll);");
-
- /* Pass other traffic not already handled to the next table for
- * routing. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;");
- }
-
- /* Logical router ingress table 1: IP Input for IPv4. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbrp) {
- continue;
- }
-
- if (op->derived) {
- /* No ingress packets are accepted on a chassisredirect
- * port, so no need to program flows for that port. */
- continue;
- }
-
- if (op->lrp_networks.n_ipv4_addrs) {
- /* L3 admission control: drop packets that originate from an
- * IPv4 address owned by the router or a broadcast address
- * known to the router (priority 100). */
- ds_clear(&match);
- ds_put_cstr(&match, "ip4.src == ");
- op_put_v4_networks(&match, op, true);
- ds_put_cstr(&match, " && "REGBIT_EGRESS_LOOPBACK" == 0");
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
- ds_cstr(&match), "drop;");
-
- /* ICMP echo reply. These flows reply to ICMP echo requests
- * received for the router's IP address. Since packets only
- * get here as part of the logical router datapath, the inport
- * (i.e. the incoming locally attached net) does not matter.
- * The ip.ttl also does not matter (RFC1812 section 4.2.2.9) */
- ds_clear(&match);
- ds_put_cstr(&match, "ip4.dst == ");
- op_put_v4_networks(&match, op, false);
- ds_put_cstr(&match, " && icmp4.type == 8 && icmp4.code == 0");
-
- ds_clear(&actions);
- ds_put_format(&actions,
- "ip4.dst <-> ip4.src; "
- "ip.ttl = 255; "
- "icmp4.type = 0; "
- "flags.loopback = 1; "
- "next; ");
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* ICMP time exceeded */
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- ds_clear(&match);
- ds_clear(&actions);
-
- ds_put_format(&match,
- "inport == %s && ip4 && "
- "ip.ttl == {0, 1} && !ip.later_frag", op->json_key);
- ds_put_format(&actions,
- "icmp4 {"
- "eth.dst <-> eth.src; "
- "icmp4.type = 11; /* Time exceeded */ "
- "icmp4.code = 0; /* TTL exceeded in transit */ "
- "ip4.dst = ip4.src; "
- "ip4.src = %s; "
- "ip.ttl = 255; "
- "next; };",
- op->lrp_networks.ipv4_addrs[i].addr_s);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* ARP reply. These flows reply to ARP requests for the router's own
- * IP address. */
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- ds_clear(&match);
- ds_put_format(&match,
- "inport == %s && arp.spa == %s/%u && arp.tpa == %s"
- " && arp.op == 1",
- op->json_key,
- op->lrp_networks.ipv4_addrs[i].network_s,
- op->lrp_networks.ipv4_addrs[i].plen,
- op->lrp_networks.ipv4_addrs[i].addr_s);
-
- if (op->od->l3dgw_port && op->od->l3redirect_port && op->peer
- && op->peer->od->localnet_port) {
- bool add_chassis_resident_check = false;
- if (op == op->od->l3dgw_port) {
- /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
- * should only be sent from the "redirect-chassis", so that
- * upstream MAC learning points to the "redirect-chassis".
- * Also need to avoid generation of multiple ARP responses
- * from different chassis. */
- add_chassis_resident_check = true;
- } else {
- /* Check if the option 'reside-on-redirect-chassis'
- * is set to true on the router port. If set to true
- * and if peer's logical switch has a localnet port, it
- * means the router pipeline for the packets from
- * peer's logical switch is be run on the chassis
- * hosting the gateway port and it should reply to the
- * ARP requests for the router port IPs.
- */
- add_chassis_resident_check = smap_get_bool(
- &op->nbrp->options,
- "reside-on-redirect-chassis", false);
- }
-
- if (add_chassis_resident_check) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
- }
-
- ds_clear(&actions);
- ds_put_format(&actions,
- "put_arp(inport, arp.spa, arp.sha); "
- "eth.dst = eth.src; "
- "eth.src = %s; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; "
- "arp.sha = %s; "
- "arp.tpa = arp.spa; "
- "arp.spa = %s; "
- "outport = %s; "
- "flags.loopback = 1; "
- "output;",
- op->lrp_networks.ea_s,
- op->lrp_networks.ea_s,
- op->lrp_networks.ipv4_addrs[i].addr_s,
- op->json_key);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* Learn from ARP requests that were not directed at us. A typical
- * use case is GARP request handling. (A priority-90 flow will
- * respond to request to us and learn the sender's mac address.) */
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- ds_clear(&match);
- ds_put_format(&match,
- "inport == %s && arp.spa == %s/%u && arp.op == 1",
- op->json_key,
- op->lrp_networks.ipv4_addrs[i].network_s,
- op->lrp_networks.ipv4_addrs[i].plen);
- if (op->od->l3dgw_port && op == op->od->l3dgw_port
- && op->od->l3redirect_port) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
- ds_cstr(&match),
- "put_arp(inport, arp.spa, arp.sha);");
-
- }
-
- /* A set to hold all load-balancer vips that need ARP responses. */
- struct sset all_ips = SSET_INITIALIZER(&all_ips);
- int addr_family;
- get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
-
- const char *ip_address;
- SSET_FOR_EACH(ip_address, &all_ips) {
- ds_clear(&match);
- if (addr_family == AF_INET) {
- ds_put_format(&match,
- "inport == %s && arp.tpa == %s && arp.op == 1",
- op->json_key, ip_address);
- } else {
- ds_put_format(&match,
- "inport == %s && nd_ns && nd.target == %s",
- op->json_key, ip_address);
- }
-
- ds_clear(&actions);
- if (addr_family == AF_INET) {
- ds_put_format(&actions,
- "eth.dst = eth.src; "
- "eth.src = %s; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; "
- "arp.sha = %s; "
- "arp.tpa = arp.spa; "
- "arp.spa = %s; "
- "outport = %s; "
- "flags.loopback = 1; "
- "output;",
- op->lrp_networks.ea_s,
- op->lrp_networks.ea_s,
- ip_address,
- op->json_key);
- } else {
- ds_put_format(&actions,
- "nd_na { "
- "eth.src = %s; "
- "ip6.src = %s; "
- "nd.target = %s; "
- "nd.tll = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output; "
- "};",
- op->lrp_networks.ea_s,
- ip_address,
- ip_address,
- op->lrp_networks.ea_s);
- }
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- sset_destroy(&all_ips);
-
- /* A gateway router can have 2 SNAT IP addresses to force DNATed and
- * LBed traffic respectively to be SNATed. In addition, there can be
- * a number of SNAT rules in the NAT table. */
- ovs_be32 *snat_ips = xmalloc(sizeof *snat_ips *
- (op->od->nbr->n_nat + 2));
- size_t n_snat_ips = 0;
-
- ovs_be32 snat_ip;
- const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat",
- &snat_ip);
- if (dnat_force_snat_ip) {
- snat_ips[n_snat_ips++] = snat_ip;
- }
-
- const char *lb_force_snat_ip = get_force_snat_ip(op->od, "lb",
- &snat_ip);
- if (lb_force_snat_ip) {
- snat_ips[n_snat_ips++] = snat_ip;
- }
-
- for (int i = 0; i < op->od->nbr->n_nat; i++) {
- const struct nbrec_nat *nat;
-
- nat = op->od->nbr->nat[i];
-
- ovs_be32 ip;
- if (!ip_parse(nat->external_ip, &ip) || !ip) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
- "for router %s", nat->external_ip, op->key);
- continue;
- }
-
- if (!strcmp(nat->type, "snat")) {
- snat_ips[n_snat_ips++] = ip;
- continue;
- }
-
- /* ARP handling for external IP addresses.
- *
- * DNAT IP addresses are external IP addresses that need ARP
- * handling. */
- ds_clear(&match);
- ds_put_format(&match,
- "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
- op->json_key, IP_ARGS(ip));
-
- ds_clear(&actions);
- ds_put_format(&actions,
- "eth.dst = eth.src; "
- "arp.op = 2; /* ARP reply */ "
- "arp.tha = arp.sha; ");
-
- if (op->od->l3dgw_port && op == op->od->l3dgw_port) {
- struct eth_addr mac;
- if (nat->external_mac &&
- eth_addr_from_string(nat->external_mac, &mac)
- && nat->logical_port) {
- /* distributed NAT case, use nat->external_mac */
- ds_put_format(&actions,
- "eth.src = "ETH_ADDR_FMT"; "
- "arp.sha = "ETH_ADDR_FMT"; ",
- ETH_ADDR_ARGS(mac),
- ETH_ADDR_ARGS(mac));
- /* Traffic with eth.src = nat->external_mac should only be
- * sent from the chassis where nat->logical_port is
- * resident, so that upstream MAC learning points to the
- * correct chassis. Also need to avoid generation of
- * multiple ARP responses from different chassis. */
- ds_put_format(&match, " && is_chassis_resident(\"%s\")",
- nat->logical_port);
- } else {
- ds_put_format(&actions,
- "eth.src = %s; "
- "arp.sha = %s; ",
- op->lrp_networks.ea_s,
- op->lrp_networks.ea_s);
- /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
- * should only be sent from the "redirect-chassis", so that
- * upstream MAC learning points to the "redirect-chassis".
- * Also need to avoid generation of multiple ARP responses
- * from different chassis. */
- if (op->od->l3redirect_port) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
- }
- } else {
- ds_put_format(&actions,
- "eth.src = %s; "
- "arp.sha = %s; ",
- op->lrp_networks.ea_s,
- op->lrp_networks.ea_s);
- }
- ds_put_format(&actions,
- "arp.tpa = arp.spa; "
- "arp.spa = "IP_FMT"; "
- "outport = %s; "
- "flags.loopback = 1; "
- "output;",
- IP_ARGS(ip),
- op->json_key);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- if (!smap_get(&op->od->nbr->options, "chassis")
- && !op->od->l3dgw_port) {
- /* UDP/TCP port unreachable. */
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- ds_clear(&match);
- ds_put_format(&match,
- "ip4 && ip4.dst == %s && !ip.later_frag && udp",
- op->lrp_networks.ipv4_addrs[i].addr_s);
- const char *action = "icmp4 {"
- "eth.dst <-> eth.src; "
- "ip4.dst <-> ip4.src; "
- "ip.ttl = 255; "
- "icmp4.type = 3; "
- "icmp4.code = 3; "
- "next; };";
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
- ds_cstr(&match), action);
-
- ds_clear(&match);
- ds_put_format(&match,
- "ip4 && ip4.dst == %s && !ip.later_frag && tcp",
- op->lrp_networks.ipv4_addrs[i].addr_s);
- action = "tcp_reset {"
- "eth.dst <-> eth.src; "
- "ip4.dst <-> ip4.src; "
- "next; };";
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
- ds_cstr(&match), action);
-
- ds_clear(&match);
- ds_put_format(&match,
- "ip4 && ip4.dst == %s && !ip.later_frag",
- op->lrp_networks.ipv4_addrs[i].addr_s);
- action = "icmp4 {"
- "eth.dst <-> eth.src; "
- "ip4.dst <-> ip4.src; "
- "ip.ttl = 255; "
- "icmp4.type = 3; "
- "icmp4.code = 2; "
- "next; };";
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
- ds_cstr(&match), action);
- }
- }
-
- ds_clear(&match);
- ds_put_cstr(&match, "ip4.dst == {");
- bool has_drop_ips = false;
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- bool snat_ip_is_router_ip = false;
- for (int j = 0; j < n_snat_ips; j++) {
- /* Packets to SNAT IPs should not be dropped. */
- if (op->lrp_networks.ipv4_addrs[i].addr == snat_ips[j]) {
- snat_ip_is_router_ip = true;
- break;
- }
- }
- if (snat_ip_is_router_ip) {
- continue;
- }
- ds_put_format(&match, "%s, ",
- op->lrp_networks.ipv4_addrs[i].addr_s);
- has_drop_ips = true;
- }
- ds_chomp(&match, ' ');
- ds_chomp(&match, ',');
- ds_put_cstr(&match, "}");
-
- if (has_drop_ips) {
- /* Drop IP traffic to this router. */
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
- ds_cstr(&match), "drop;");
- }
-
- free(snat_ips);
- }
-
- /* Logical router ingress table 1: IP Input for IPv6. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbrp) {
- continue;
- }
-
- if (op->derived) {
- /* No ingress packets are accepted on a chassisredirect
- * port, so no need to program flows for that port. */
- continue;
- }
-
- if (op->lrp_networks.n_ipv6_addrs) {
- /* L3 admission control: drop packets that originate from an
- * IPv6 address owned by the router (priority 100). */
- ds_clear(&match);
- ds_put_cstr(&match, "ip6.src == ");
- op_put_v6_networks(&match, op);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
- ds_cstr(&match), "drop;");
-
- /* ICMPv6 echo reply. These flows reply to echo requests
- * received for the router's IP address. */
- ds_clear(&match);
- ds_put_cstr(&match, "ip6.dst == ");
- op_put_v6_networks(&match, op);
- ds_put_cstr(&match, " && icmp6.type == 128 && icmp6.code == 0");
-
- ds_clear(&actions);
- ds_put_cstr(&actions,
- "ip6.dst <-> ip6.src; "
- "ip.ttl = 255; "
- "icmp6.type = 129; "
- "flags.loopback = 1; "
- "next; ");
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* Drop IPv6 traffic to this router. */
- ds_clear(&match);
- ds_put_cstr(&match, "ip6.dst == ");
- op_put_v6_networks(&match, op);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
- ds_cstr(&match), "drop;");
- }
-
- /* ND reply. These flows reply to ND solicitations for the
- * router's own IP address. */
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- ds_clear(&match);
- ds_put_format(&match,
- "inport == %s && nd_ns && ip6.dst == {%s, %s} "
- "&& nd.target == %s",
- op->json_key,
- op->lrp_networks.ipv6_addrs[i].addr_s,
- op->lrp_networks.ipv6_addrs[i].sn_addr_s,
- op->lrp_networks.ipv6_addrs[i].addr_s);
- if (op->od->l3dgw_port && op == op->od->l3dgw_port
- && op->od->l3redirect_port) {
- /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
- * should only be sent from the "redirect-chassis", so that
- * upstream MAC learning points to the "redirect-chassis".
- * Also need to avoid generation of multiple ND replies
- * from different chassis. */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- op->od->l3redirect_port->json_key);
- }
-
- ds_clear(&actions);
- ds_put_format(&actions,
- "put_nd(inport, ip6.src, nd.sll); "
- "nd_na_router { "
- "eth.src = %s; "
- "ip6.src = %s; "
- "nd.target = %s; "
- "nd.tll = %s; "
- "outport = inport; "
- "flags.loopback = 1; "
- "output; "
- "};",
- op->lrp_networks.ea_s,
- op->lrp_networks.ipv6_addrs[i].addr_s,
- op->lrp_networks.ipv6_addrs[i].addr_s,
- op->lrp_networks.ea_s);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* UDP/TCP port unreachable */
- if (!smap_get(&op->od->nbr->options, "chassis")
- && !op->od->l3dgw_port) {
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- ds_clear(&match);
- ds_put_format(&match,
- "ip6 && ip6.dst == %s && !ip.later_frag && tcp",
- op->lrp_networks.ipv6_addrs[i].addr_s);
- const char *action = "tcp_reset {"
- "eth.dst <-> eth.src; "
- "ip6.dst <-> ip6.src; "
- "next; };";
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
- ds_cstr(&match), action);
-
- ds_clear(&match);
- ds_put_format(&match,
- "ip6 && ip6.dst == %s && !ip.later_frag && udp",
- op->lrp_networks.ipv6_addrs[i].addr_s);
- action = "icmp6 {"
- "eth.dst <-> eth.src; "
- "ip6.dst <-> ip6.src; "
- "ip.ttl = 255; "
- "icmp6.type = 1; "
- "icmp6.code = 4; "
- "next; };";
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
- ds_cstr(&match), action);
-
- ds_clear(&match);
- ds_put_format(&match,
- "ip6 && ip6.dst == %s && !ip.later_frag",
- op->lrp_networks.ipv6_addrs[i].addr_s);
- action = "icmp6 {"
- "eth.dst <-> eth.src; "
- "ip6.dst <-> ip6.src; "
- "ip.ttl = 255; "
- "icmp6.type = 1; "
- "icmp6.code = 3; "
- "next; };";
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 70,
- ds_cstr(&match), action);
- }
- }
-
- /* ICMPv6 time exceeded */
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- /* skip link-local address */
- if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].network)) {
- continue;
- }
-
- ds_clear(&match);
- ds_clear(&actions);
-
- ds_put_format(&match,
- "inport == %s && ip6 && "
- "ip6.src == %s/%d && "
- "ip.ttl == {0, 1} && !ip.later_frag",
- op->json_key,
- op->lrp_networks.ipv6_addrs[i].network_s,
- op->lrp_networks.ipv6_addrs[i].plen);
- ds_put_format(&actions,
- "icmp6 {"
- "eth.dst <-> eth.src; "
- "ip6.dst = ip6.src; "
- "ip6.src = %s; "
- "ip.ttl = 255; "
- "icmp6.type = 3; /* Time exceeded */ "
- "icmp6.code = 0; /* TTL exceeded in transit */ "
- "next; };",
- op->lrp_networks.ipv6_addrs[i].addr_s);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 40,
- ds_cstr(&match), ds_cstr(&actions));
- }
- }
-
- /* NAT, Defrag and load balancing. */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- /* Packets are allowed by default. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;");
-
- /* NAT rules are only valid on Gateway routers and routers with
- * l3dgw_port (router has a port with "redirect-chassis"
- * specified). */
- if (!smap_get(&od->nbr->options, "chassis") && !od->l3dgw_port) {
- continue;
- }
-
- ovs_be32 snat_ip;
- const char *dnat_force_snat_ip = get_force_snat_ip(od, "dnat",
- &snat_ip);
- const char *lb_force_snat_ip = get_force_snat_ip(od, "lb",
- &snat_ip);
-
- for (int i = 0; i < od->nbr->n_nat; i++) {
- const struct nbrec_nat *nat;
-
- nat = od->nbr->nat[i];
-
- ovs_be32 ip, mask;
-
- char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
- if (error || mask != OVS_BE32_MAX) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad external ip %s for nat",
- nat->external_ip);
- free(error);
- continue;
- }
-
- /* Check the validity of nat->logical_ip. 'logical_ip' can
- * be a subnet when the type is "snat". */
- error = ip_parse_masked(nat->logical_ip, &ip, &mask);
- if (!strcmp(nat->type, "snat")) {
- if (error) {
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
- "in router "UUID_FMT"",
- nat->logical_ip, UUID_ARGS(&od->key));
- free(error);
- continue;
- }
- } else {
- if (error || mask != OVS_BE32_MAX) {
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
- ""UUID_FMT"", nat->logical_ip, UUID_ARGS(&od->key));
- free(error);
- continue;
- }
- }
-
- /* For distributed router NAT, determine whether this NAT rule
- * satisfies the conditions for distributed NAT processing. */
- bool distributed = false;
- struct eth_addr mac;
- if (od->l3dgw_port && !strcmp(nat->type, "dnat_and_snat") &&
- nat->logical_port && nat->external_mac) {
- if (eth_addr_from_string(nat->external_mac, &mac)) {
- distributed = true;
- } else {
- static struct vlog_rate_limit rl =
- VLOG_RATE_LIMIT_INIT(5, 1);
- VLOG_WARN_RL(&rl, "bad mac %s for dnat in router "
- ""UUID_FMT"", nat->external_mac, UUID_ARGS(&od->key));
- continue;
- }
- }
-
- /* Ingress UNSNAT table: It is for already established connections'
- * reverse traffic. i.e., SNAT has already been done in egress
- * pipeline and now the packet has entered the ingress pipeline as
- * part of a reply. We undo the SNAT here.
- *
- * Undoing SNAT has to happen before DNAT processing. This is
- * because when the packet was DNATed in ingress pipeline, it did
- * not know about the possibility of eventual additional SNAT in
- * egress pipeline. */
- if (!strcmp(nat->type, "snat")
- || !strcmp(nat->type, "dnat_and_snat")) {
- if (!od->l3dgw_port) {
- /* Gateway router. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s",
- nat->external_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
- ds_cstr(&match), "ct_snat;");
- } else {
- /* Distributed router. */
-
- /* Traffic received on l3dgw_port is subject to NAT. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s"
- " && inport == %s",
- nat->external_ip,
- od->l3dgw_port->json_key);
- if (!distributed && od->l3redirect_port) {
- /* Flows for NAT rules that are centralized are only
- * programmed on the "redirect-chassis". */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- od->l3redirect_port->json_key);
- }
- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
- ds_cstr(&match), "ct_snat;");
-
- /* Traffic received on other router ports must be
- * redirected to the central instance of the l3dgw_port
- * for NAT processing. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s",
- nat->external_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
- ds_cstr(&match),
- REGBIT_NAT_REDIRECT" = 1; next;");
- }
- }
-
- /* Ingress DNAT table: Packets enter the pipeline with destination
- * IP address that needs to be DNATted from a external IP address
- * to a logical IP address. */
- if (!strcmp(nat->type, "dnat")
- || !strcmp(nat->type, "dnat_and_snat")) {
- if (!od->l3dgw_port) {
- /* Gateway router. */
- /* Packet when it goes from the initiator to destination.
- * We need to set flags.loopback because the router can
- * send the packet back through the same interface. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s",
- nat->external_ip);
- ds_clear(&actions);
- if (dnat_force_snat_ip) {
- /* Indicate to the future tables that a DNAT has taken
- * place and a force SNAT needs to be done in the
- * Egress SNAT table. */
- ds_put_format(&actions,
- "flags.force_snat_for_dnat = 1; ");
- }
- ds_put_format(&actions, "flags.loopback = 1; ct_dnat(%s);",
- nat->logical_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
- ds_cstr(&match), ds_cstr(&actions));
- } else {
- /* Distributed router. */
-
- /* Traffic received on l3dgw_port is subject to NAT. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s"
- " && inport == %s",
- nat->external_ip,
- od->l3dgw_port->json_key);
- if (!distributed && od->l3redirect_port) {
- /* Flows for NAT rules that are centralized are only
- * programmed on the "redirect-chassis". */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- od->l3redirect_port->json_key);
- }
- ds_clear(&actions);
- ds_put_format(&actions, "ct_dnat(%s);",
- nat->logical_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* Traffic received on other router ports must be
- * redirected to the central instance of the l3dgw_port
- * for NAT processing. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s",
- nat->external_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
- ds_cstr(&match),
- REGBIT_NAT_REDIRECT" = 1; next;");
- }
- }
-
- /* Egress UNDNAT table: It is for already established connections'
- * reverse traffic. i.e., DNAT has already been done in ingress
- * pipeline and now the packet has entered the egress pipeline as
- * part of a reply. We undo the DNAT here.
- *
- * Note that this only applies for NAT on a distributed router.
- * Undo DNAT on a gateway router is done in the ingress DNAT
- * pipeline stage. */
- if (od->l3dgw_port && (!strcmp(nat->type, "dnat")
- || !strcmp(nat->type, "dnat_and_snat"))) {
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.src == %s"
- " && outport == %s",
- nat->logical_ip,
- od->l3dgw_port->json_key);
- if (!distributed && od->l3redirect_port) {
- /* Flows for NAT rules that are centralized are only
- * programmed on the "redirect-chassis". */
- ds_put_format(&match, " && is_chassis_resident(%s)",
- od->l3redirect_port->json_key);
- }
- ds_clear(&actions);
- if (distributed) {
- ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
- ETH_ADDR_ARGS(mac));
- }
- ds_put_format(&actions, "ct_dnat;");
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* Egress SNAT table: Packets enter the egress pipeline with
- * source ip address that needs to be SNATted to a external ip
- * address. */
- if (!strcmp(nat->type, "snat")
- || !strcmp(nat->type, "dnat_and_snat")) {
- if (!od->l3dgw_port) {
- /* Gateway router. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.src == %s",
- nat->logical_ip);
- ds_clear(&actions);
- ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
-
- /* The priority here is calculated such that the
- * nat->logical_ip with the longest mask gets a higher
- * priority. */
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
- count_1bits(ntohl(mask)) + 1,
- ds_cstr(&match), ds_cstr(&actions));
- } else {
- uint16_t priority = count_1bits(ntohl(mask)) + 1;
-
- /* Distributed router. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.src == %s"
- " && outport == %s",
- nat->logical_ip,
- od->l3dgw_port->json_key);
- if (!distributed && od->l3redirect_port) {
- /* Flows for NAT rules that are centralized are only
- * programmed on the "redirect-chassis". */
- priority += 128;
- ds_put_format(&match, " && is_chassis_resident(%s)",
- od->l3redirect_port->json_key);
- }
- ds_clear(&actions);
- if (distributed) {
- ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
- ETH_ADDR_ARGS(mac));
- }
- ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
-
- /* The priority here is calculated such that the
- * nat->logical_ip with the longest mask gets a higher
- * priority. */
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT,
- priority, ds_cstr(&match),
- ds_cstr(&actions));
- }
- }
-
- /* Logical router ingress table 0:
- * For NAT on a distributed router, add rules allowing
- * ingress traffic with eth.dst matching nat->external_mac
- * on the l3dgw_port instance where nat->logical_port is
- * resident. */
- if (distributed) {
- ds_clear(&match);
- ds_put_format(&match,
- "eth.dst == "ETH_ADDR_FMT" && inport == %s"
- " && is_chassis_resident(\"%s\")",
- ETH_ADDR_ARGS(mac),
- od->l3dgw_port->json_key,
- nat->logical_port);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ADMISSION, 50,
- ds_cstr(&match), "next;");
- }
-
- /* Ingress Gateway Redirect Table: For NAT on a distributed
- * router, add flows that are specific to a NAT rule. These
- * flows indicate the presence of an applicable NAT rule that
- * can be applied in a distributed manner. */
- if (distributed) {
- ds_clear(&match);
- ds_put_format(&match, "ip4.src == %s && outport == %s",
- nat->logical_ip,
- od->l3dgw_port->json_key);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
- ds_cstr(&match), "next;");
- }
-
- /* Egress Loopback table: For NAT on a distributed router.
- * If packets in the egress pipeline on the distributed
- * gateway port have ip.dst matching a NAT external IP, then
- * loop a clone of the packet back to the beginning of the
- * ingress pipeline with inport = outport. */
- if (od->l3dgw_port) {
- /* Distributed router. */
- if (!strcmp(nat->type, "dnat_and_snat") &&
- nat->external_mac && nat->external_ip) {
- for (int j = 0; j < od->nbr->n_nat; j++) {
- const struct nbrec_nat *nat2 = od->nbr->nat[j];
-
- if (nat2 == nat ||
- strcmp(nat2->type, "dnat_and_snat") ||
- !nat2->external_mac || !nat2->external_ip) {
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "is_chassis_resident(\"%s\") && "
- "ip4.src == %s && ip4.dst == %s",
- nat->logical_port, nat2->external_ip,
- nat->external_ip);
- ds_clear(&actions);
- ds_put_format(&actions,
- "inport = outport; outport = \"\"; "
- "flags = 0; flags.loopback = 1; "
- REGBIT_EGRESS_LOOPBACK" = 1; "
- "next(pipeline=ingress, table=0); ");
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 300,
- ds_cstr(&match), ds_cstr(&actions));
-
- ds_clear(&match);
- ds_put_format(&match,
- "ip4.src == %s && ip4.dst == %s",
- nat2->external_ip, nat->external_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
- ds_cstr(&match), "next;");
- ds_clear(&match);
- }
- }
-
- ds_clear(&match);
- ds_put_format(&match, "ip4.dst == %s && outport == %s",
- nat->external_ip,
- od->l3dgw_port->json_key);
- ds_clear(&actions);
- ds_put_format(&actions,
- "clone { ct_clear; "
- "inport = outport; outport = \"\"; "
- "flags = 0; flags.loopback = 1; ");
- for (int j = 0; j < MFF_N_LOG_REGS; j++) {
- ds_put_format(&actions, "reg%d = 0; ", j);
- }
- ds_put_format(&actions, REGBIT_EGRESS_LOOPBACK" = 1; "
- "next(pipeline=ingress, table=0); };");
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 100,
- ds_cstr(&match), ds_cstr(&actions));
- }
- }
-
- /* Handle force SNAT options set in the gateway router. */
- if (dnat_force_snat_ip && !od->l3dgw_port) {
- /* If a packet with destination IP address as that of the
- * gateway router (as set in options:dnat_force_snat_ip) is seen,
- * UNSNAT it. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s", dnat_force_snat_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110,
- ds_cstr(&match), "ct_snat;");
-
- /* Higher priority rules to force SNAT with the IP addresses
- * configured in the Gateway router. This only takes effect
- * when the packet has already been DNATed once. */
- ds_clear(&match);
- ds_put_format(&match, "flags.force_snat_for_dnat == 1 && ip");
- ds_clear(&actions);
- ds_put_format(&actions, "ct_snat(%s);", dnat_force_snat_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100,
- ds_cstr(&match), ds_cstr(&actions));
- }
- if (lb_force_snat_ip && !od->l3dgw_port) {
- /* If a packet with destination IP address as that of the
- * gateway router (as set in options:lb_force_snat_ip) is seen,
- * UNSNAT it. */
- ds_clear(&match);
- ds_put_format(&match, "ip && ip4.dst == %s", lb_force_snat_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
- ds_cstr(&match), "ct_snat;");
-
- /* Load balanced traffic will have flags.force_snat_for_lb set.
- * Force SNAT it. */
- ds_clear(&match);
- ds_put_format(&match, "flags.force_snat_for_lb == 1 && ip");
- ds_clear(&actions);
- ds_put_format(&actions, "ct_snat(%s);", lb_force_snat_ip);
- ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 100,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- if (!od->l3dgw_port) {
- /* For gateway router, re-circulate every packet through
- * the DNAT zone. This helps with the following.
- *
- * Any packet that needs to be unDNATed in the reverse
- * direction gets unDNATed. Ideally this could be done in
- * the egress pipeline. But since the gateway router
- * does not have any feature that depends on the source
- * ip address being external IP address for IP routing,
- * we can do it here, saving a future re-circulation. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
- "ip", "flags.loopback = 1; ct_dnat;");
- } else {
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 400,
- REGBIT_DISTRIBUTED_NAT" == 1", "next;");
-
- /* For NAT on a distributed router, add flows to Ingress
- * IP Routing table, Ingress ARP Resolution table, and
- * Ingress Gateway Redirect Table that are not specific to a
- * NAT rule. */
-
- /* The highest priority IN_IP_ROUTING rule matches packets
- * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages),
- * with action "ip.ttl--; next;". The IN_GW_REDIRECT table
- * will take care of setting the outport. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 300,
- REGBIT_NAT_REDIRECT" == 1", "ip.ttl--; next;");
-
- /* The highest priority IN_ARP_RESOLVE rule matches packets
- * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages),
- * then sets eth.dst to the distributed gateway port's
- * ethernet address. */
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;",
- od->l3dgw_port->lrp_networks.ea_s);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 200,
- REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions));
-
- /* The highest priority IN_GW_REDIRECT rule redirects packets
- * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages) to
- * the central instance of the l3dgw_port for NAT processing. */
- ds_clear(&actions);
- ds_put_format(&actions, "outport = %s; next;",
- od->l3redirect_port->json_key);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200,
- REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions));
- }
-
- /* Load balancing and packet defrag are only valid on
- * Gateway routers or router with gateway port. */
- if (!smap_get(&od->nbr->options, "chassis") && !od->l3dgw_port) {
- continue;
- }
-
- /* A set to hold all ips that need defragmentation and tracking. */
- struct sset all_ips = SSET_INITIALIZER(&all_ips);
-
- for (int i = 0; i < od->nbr->n_load_balancer; i++) {
- struct nbrec_load_balancer *lb = od->nbr->load_balancer[i];
- struct smap *vips = &lb->vips;
- struct smap_node *node;
-
- SMAP_FOR_EACH (node, vips) {
- uint16_t port = 0;
- int addr_family;
-
- /* node->key contains IP:port or just IP. */
- char *ip_address = NULL;
- ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
- &addr_family);
- if (!ip_address) {
- continue;
- }
-
- if (!sset_contains(&all_ips, ip_address)) {
- sset_add(&all_ips, ip_address);
- /* If there are any load balancing rules, we should send
- * the packet to conntrack for defragmentation and
- * tracking. This helps with two things.
- *
- * 1. With tracking, we can send only new connections to
- * pick a DNAT ip address from a group.
- * 2. If there are L4 ports in load balancing rules, we
- * need the defragmentation to match on L4 ports. */
- ds_clear(&match);
- if (addr_family == AF_INET) {
- ds_put_format(&match, "ip && ip4.dst == %s",
- ip_address);
- } else {
- ds_put_format(&match, "ip && ip6.dst == %s",
- ip_address);
- }
- ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
- 100, ds_cstr(&match), "ct_next;");
- }
-
- /* Higher priority rules are added for load-balancing in DNAT
- * table. For every match (on a VIP[:port]), we add two flows
- * via add_router_lb_flow(). One flow is for specific matching
- * on ct.new with an action of "ct_lb($targets);". The other
- * flow is for ct.est with an action of "ct_dnat;". */
- ds_clear(&actions);
- ds_put_format(&actions, "ct_lb(%s);", node->value);
-
- ds_clear(&match);
- if (addr_family == AF_INET) {
- ds_put_format(&match, "ip && ip4.dst == %s",
- ip_address);
- } else {
- ds_put_format(&match, "ip && ip6.dst == %s",
- ip_address);
- }
- free(ip_address);
-
- int prio = 110;
- bool is_udp = lb->protocol && !strcmp(lb->protocol, "udp") ?
- true : false;
- if (port) {
- if (is_udp) {
- ds_put_format(&match, " && udp && udp.dst == %d",
- port);
- } else {
- ds_put_format(&match, " && tcp && tcp.dst == %d",
- port);
- }
- prio = 120;
- }
-
- if (od->l3redirect_port) {
- ds_put_format(&match, " && is_chassis_resident(%s)",
- od->l3redirect_port->json_key);
- }
- add_router_lb_flow(lflows, od, &match, &actions, prio,
- lb_force_snat_ip, node->value, is_udp,
- addr_family);
- }
- }
- sset_destroy(&all_ips);
- }
-
- /* Logical router ingress table 5 and 6: IPv6 Router Adv (RA) options and
- * response. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbrp || op->nbrp->peer || !op->peer) {
- continue;
- }
-
- if (!op->lrp_networks.n_ipv6_addrs) {
- continue;
- }
-
- const char *address_mode = smap_get(
- &op->nbrp->ipv6_ra_configs, "address_mode");
-
- if (!address_mode) {
- continue;
- }
- if (strcmp(address_mode, "slaac") &&
- strcmp(address_mode, "dhcpv6_stateful") &&
- strcmp(address_mode, "dhcpv6_stateless")) {
- static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
- VLOG_WARN_RL(&rl, "Invalid address mode [%s] defined",
- address_mode);
- continue;
- }
-
- if (smap_get_bool(&op->nbrp->ipv6_ra_configs, "send_periodic",
- false)) {
- copy_ra_to_sb(op, address_mode);
- }
-
- ds_clear(&match);
- ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && nd_rs",
- op->json_key);
- ds_clear(&actions);
-
- const char *mtu_s = smap_get(
- &op->nbrp->ipv6_ra_configs, "mtu");
-
- /* As per RFC 2460, 1280 is minimum IPv6 MTU. */
- uint32_t mtu = (mtu_s && atoi(mtu_s) >= 1280) ? atoi(mtu_s) : 0;
-
- ds_put_format(&actions, REGBIT_ND_RA_OPTS_RESULT" = put_nd_ra_opts("
- "addr_mode = \"%s\", slla = %s",
- address_mode, op->lrp_networks.ea_s);
- if (mtu > 0) {
- ds_put_format(&actions, ", mtu = %u", mtu);
- }
-
- bool add_rs_response_flow = false;
-
- for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- if (in6_is_lla(&op->lrp_networks.ipv6_addrs[i].network)) {
- continue;
- }
-
- ds_put_format(&actions, ", prefix = %s/%u",
- op->lrp_networks.ipv6_addrs[i].network_s,
- op->lrp_networks.ipv6_addrs[i].plen);
-
- add_rs_response_flow = true;
- }
-
- if (add_rs_response_flow) {
- ds_put_cstr(&actions, "); next;");
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_OPTIONS, 50,
- ds_cstr(&match), ds_cstr(&actions));
- ds_clear(&actions);
- ds_clear(&match);
- ds_put_format(&match, "inport == %s && ip6.dst == ff02::2 && "
- "nd_ra && "REGBIT_ND_RA_OPTS_RESULT, op->json_key);
-
- char ip6_str[INET6_ADDRSTRLEN + 1];
- struct in6_addr lla;
- in6_generate_lla(op->lrp_networks.ea, &lla);
- memset(ip6_str, 0, sizeof(ip6_str));
- ipv6_string_mapped(ip6_str, &lla);
- ds_put_format(&actions, "eth.dst = eth.src; eth.src = %s; "
- "ip6.dst = ip6.src; ip6.src = %s; "
- "outport = inport; flags.loopback = 1; "
- "output;",
- op->lrp_networks.ea_s, ip6_str);
- ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ND_RA_RESPONSE, 50,
- ds_cstr(&match), ds_cstr(&actions));
- }
- }
-
- /* Logical router ingress table 5, 6: RS responder, by default goto next.
- * (priority 0)*/
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_OPTIONS, 0, "1", "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1", "next;");
- }
-
- /* Logical router ingress table 7: IP Routing.
- *
- * A packet that arrives at this table is an IP packet that should be
- * routed to the address in 'ip[46].dst'. This table sets outport to
- * the correct output port, eth.src to the output port's MAC
- * address, and '[xx]reg0' to the next-hop IP address (leaving
- * 'ip[46].dst', the packet’s final destination, unchanged), and
- * advances to the next table for ARP/ND resolution. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbrp) {
- continue;
- }
-
- /* create logical flows for DVR floating IPs */
- add_distributed_nat_routes(lflows, op);
-
- for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
- add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
- op->lrp_networks.ipv4_addrs[i].network_s,
- op->lrp_networks.ipv4_addrs[i].plen, NULL, NULL);
- }
-
- for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
- add_route(lflows, op, op->lrp_networks.ipv6_addrs[i].addr_s,
- op->lrp_networks.ipv6_addrs[i].network_s,
- op->lrp_networks.ipv6_addrs[i].plen, NULL, NULL);
- }
- }
-
- /* Convert the static routes to flows. */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- for (int i = 0; i < od->nbr->n_static_routes; i++) {
- const struct nbrec_logical_router_static_route *route;
-
- route = od->nbr->static_routes[i];
- build_static_route_flow(lflows, od, ports, route);
- }
- }
-
- /* Logical router ingress table 8: Policy.
- *
- * A packet that arrives at this table is an IP packet that should be
- * permitted/denied/rerouted to the address in the rule's nexthop.
- * This table sets outport to the correct out_port,
- * eth.src to the output port's MAC address,
- * and '[xx]reg0' to the next-hop IP address (leaving
- * 'ip[46].dst', the packet’s final destination, unchanged), and
- * advances to the next table for ARP/ND resolution. */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
- /* This is a catch-all rule. It has the lowest priority (0)
- * does a match-all("1") and pass-through (next) */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_POLICY, 0, "1", "next;");
-
- /* Convert routing policies to flows. */
- for (int i = 0; i < od->nbr->n_policies; i++) {
- const struct nbrec_logical_router_policy *rule
- = od->nbr->policies[i];
- build_routing_policy_flow(lflows, od, ports, rule);
- }
- }
-
-
- /* XXX destination unreachable */
-
- /* Local router ingress table 9: ARP Resolution.
- *
- * Any packet that reaches this table is an IP packet whose next-hop IP
- * address is in reg0. (ip4.dst is the final destination.) This table
- * resolves the IP address in reg0 into an output port in outport and an
- * Ethernet address in eth.dst. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (op->nbsp && !lsp_is_enabled(op->nbsp)) {
- continue;
- }
-
- if (op->nbrp) {
- /* This is a logical router port. If next-hop IP address in
- * '[xx]reg0' matches IP address of this router port, then
- * the packet is intended to eventually be sent to this
- * logical port. Set the destination mac address using this
- * port's mac address.
- *
- * The packet is still in peer's logical pipeline. So the match
- * should be on peer's outport. */
- if (op->peer && op->nbrp->peer) {
- if (op->lrp_networks.n_ipv4_addrs) {
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && reg0 == ",
- op->peer->json_key);
- op_put_v4_networks(&match, op, false);
-
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;",
- op->lrp_networks.ea_s);
- ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
- 100, ds_cstr(&match), ds_cstr(&actions));
- }
-
- if (op->lrp_networks.n_ipv6_addrs) {
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && xxreg0 == ",
- op->peer->json_key);
- op_put_v6_networks(&match, op);
-
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;",
- op->lrp_networks.ea_s);
- ovn_lflow_add(lflows, op->peer->od, S_ROUTER_IN_ARP_RESOLVE,
- 100, ds_cstr(&match), ds_cstr(&actions));
- }
- }
- } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")) {
- /* This is a logical switch port that backs a VM or a container.
- * Extract its addresses. For each of the address, go through all
- * the router ports attached to the switch (to which this port
- * connects) and if the address in question is reachable from the
- * router port, add an ARP/ND entry in that router's pipeline. */
-
- for (size_t i = 0; i < op->n_lsp_addrs; i++) {
- const char *ea_s = op->lsp_addrs[i].ea_s;
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv4_addrs; j++) {
- const char *ip_s = op->lsp_addrs[i].ipv4_addrs[j].addr_s;
- for (size_t k = 0; k < op->od->n_router_ports; k++) {
- /* Get the Logical_Router_Port that the
- * Logical_Switch_Port is connected to, as
- * 'peer'. */
- const char *peer_name = smap_get(
- &op->od->router_ports[k]->nbsp->options,
- "router-port");
- if (!peer_name) {
- continue;
- }
-
- struct ovn_port *peer = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbrp) {
- continue;
- }
-
- if (!find_lrp_member_ip(peer, ip_s)) {
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && reg0 == %s",
- peer->json_key, ip_s);
-
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
- ovn_lflow_add(lflows, peer->od,
- S_ROUTER_IN_ARP_RESOLVE, 100,
- ds_cstr(&match), ds_cstr(&actions));
- }
- }
-
- for (size_t j = 0; j < op->lsp_addrs[i].n_ipv6_addrs; j++) {
- const char *ip_s = op->lsp_addrs[i].ipv6_addrs[j].addr_s;
- for (size_t k = 0; k < op->od->n_router_ports; k++) {
- /* Get the Logical_Router_Port that the
- * Logical_Switch_Port is connected to, as
- * 'peer'. */
- const char *peer_name = smap_get(
- &op->od->router_ports[k]->nbsp->options,
- "router-port");
- if (!peer_name) {
- continue;
- }
-
- struct ovn_port *peer = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbrp) {
- continue;
- }
-
- if (!find_lrp_member_ip(peer, ip_s)) {
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && xxreg0 == %s",
- peer->json_key, ip_s);
-
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;", ea_s);
- ovn_lflow_add(lflows, peer->od,
- S_ROUTER_IN_ARP_RESOLVE, 100,
- ds_cstr(&match), ds_cstr(&actions));
- }
- }
- }
- } else if (!strcmp(op->nbsp->type, "router")) {
- /* This is a logical switch port that connects to a router. */
-
- /* The peer of this switch port is the router port for which
- * we need to add logical flows such that it can resolve
- * ARP entries for all the other router ports connected to
- * the switch in question. */
-
- const char *peer_name = smap_get(&op->nbsp->options,
- "router-port");
- if (!peer_name) {
- continue;
- }
-
- struct ovn_port *peer = ovn_port_find(ports, peer_name);
- if (!peer || !peer->nbrp) {
- continue;
- }
-
- for (size_t i = 0; i < op->od->n_router_ports; i++) {
- const char *router_port_name = smap_get(
- &op->od->router_ports[i]->nbsp->options,
- "router-port");
- struct ovn_port *router_port = ovn_port_find(ports,
- router_port_name);
- if (!router_port || !router_port->nbrp) {
- continue;
- }
-
- /* Skip the router port under consideration. */
- if (router_port == peer) {
- continue;
- }
-
- if (router_port->lrp_networks.n_ipv4_addrs) {
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && reg0 == ",
- peer->json_key);
- op_put_v4_networks(&match, router_port, false);
-
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;",
- router_port->lrp_networks.ea_s);
- ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
- 100, ds_cstr(&match), ds_cstr(&actions));
- }
-
- if (router_port->lrp_networks.n_ipv6_addrs) {
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && xxreg0 == ",
- peer->json_key);
- op_put_v6_networks(&match, router_port);
-
- ds_clear(&actions);
- ds_put_format(&actions, "eth.dst = %s; next;",
- router_port->lrp_networks.ea_s);
- ovn_lflow_add(lflows, peer->od, S_ROUTER_IN_ARP_RESOLVE,
- 100, ds_cstr(&match), ds_cstr(&actions));
- }
- }
- }
- }
-
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip4",
- "get_arp(outport, reg0); next;");
-
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 0, "ip6",
- "get_nd(outport, xxreg0); next;");
- }
-
- /* Local router ingress table 10: Check packet length.
- *
- * Any IPv4 packet with outport set to the distributed gateway
- * router port, check the packet length and store the result in the
- * 'REGBIT_PKT_LARGER' register bit.
- *
- * Local router ingress table 11: Handle larger packets.
- *
- * Any IPv4 packet with outport set to the distributed gateway
- * router port and the 'REGBIT_PKT_LARGER' register bit is set,
- * generate ICMPv4 packet with type 3 (Destination Unreachable) and
- * code 4 (Fragmentation needed).
- * */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- /* Packets are allowed by default. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 0, "1",
- "next;");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 0, "1",
- "next;");
-
- if (od->l3dgw_port && od->l3redirect_port) {
- int gw_mtu = 0;
- if (od->l3dgw_port->nbrp) {
- gw_mtu = smap_get_int(&od->l3dgw_port->nbrp->options,
- "gateway_mtu", 0);
- }
- /* Add the flows only if gateway_mtu is configured. */
- if (gw_mtu <= 0) {
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "outport == %s && ip4",
- od->l3dgw_port->json_key);
-
- ds_clear(&actions);
- ds_put_format(&actions,
- REGBIT_PKT_LARGER" = check_pkt_larger(%d);"
- " next;", gw_mtu);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_CHK_PKT_LEN, 50,
- ds_cstr(&match), ds_cstr(&actions));
-
- for (size_t i = 0; i < od->nbr->n_ports; i++) {
- struct ovn_port *rp = ovn_port_find(ports,
- od->nbr->ports[i]->name);
- if (!rp || rp == od->l3dgw_port) {
- continue;
- }
- ds_clear(&match);
- ds_put_format(&match, "inport == %s && outport == %s && ip4 "
- "&& "REGBIT_PKT_LARGER,
- rp->json_key, od->l3dgw_port->json_key);
-
- ds_clear(&actions);
- /* Set icmp4.frag_mtu to gw_mtu - 58. 58 is the Geneve tunnel
- * overhead. */
- ds_put_format(&actions,
- "icmp4_error {"
- REGBIT_EGRESS_LOOPBACK" = 1; "
- "eth.dst = %s; "
- "ip4.dst = ip4.src; "
- "ip4.src = %s; "
- "ip.ttl = 255; "
- "icmp4.type = 3; /* Destination Unreachable. */ "
- "icmp4.code = 4; /* Frag Needed and DF was Set. */ "
- "icmp4.frag_mtu = %d; "
- "next(pipeline=ingress, table=0); };",
- rp->lrp_networks.ea_s,
- rp->lrp_networks.ipv4_addrs[0].addr_s,
- gw_mtu - 18);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_LARGER_PKTS, 50,
- ds_cstr(&match), ds_cstr(&actions));
- }
- }
- }
-
- /* Logical router ingress table 12: Gateway redirect.
- *
- * For traffic with outport equal to the l3dgw_port
- * on a distributed router, this table redirects a subset
- * of the traffic to the l3redirect_port which represents
- * the central instance of the l3dgw_port.
- */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
- if (od->l3dgw_port && od->l3redirect_port) {
- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
- REGBIT_DISTRIBUTED_NAT" == 1", "next;");
-
- /* For traffic with outport == l3dgw_port, if the
- * packet did not match any higher priority redirect
- * rule, then the traffic is redirected to the central
- * instance of the l3dgw_port. */
- ds_clear(&match);
- ds_put_format(&match, "outport == %s",
- od->l3dgw_port->json_key);
- ds_clear(&actions);
- ds_put_format(&actions, "outport = %s; next;",
- od->l3redirect_port->json_key);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
- ds_cstr(&match), ds_cstr(&actions));
-
- /* If the Ethernet destination has not been resolved,
- * redirect to the central instance of the l3dgw_port.
- * Such traffic will be replaced by an ARP request or ND
- * Neighbor Solicitation in the ARP request ingress
- * table, before being redirected to the central instance.
- */
- ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- /* Packets are allowed by default. */
- ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 0, "1", "next;");
- }
-
- /* Local router ingress table 13: ARP request.
- *
- * In the common case where the Ethernet destination has been resolved,
- * this table outputs the packet (priority 0). Otherwise, it composes
- * and sends an ARP/IPv6 NA request (priority 100). */
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbr) {
- continue;
- }
-
- for (int i = 0; i < od->nbr->n_static_routes; i++) {
- const struct nbrec_logical_router_static_route *route;
-
- route = od->nbr->static_routes[i];
- struct in6_addr gw_ip6;
- unsigned int plen;
- char *error = ipv6_parse_cidr(route->nexthop, &gw_ip6, &plen);
- if (error || plen != 128) {
- free(error);
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "eth.dst == 00:00:00:00:00:00 && "
- "ip6 && xxreg0 == %s", route->nexthop);
- struct in6_addr sn_addr;
- struct eth_addr eth_dst;
- in6_addr_solicited_node(&sn_addr, &gw_ip6);
- ipv6_multicast_to_ethernet(&eth_dst, &sn_addr);
-
- char sn_addr_s[INET6_ADDRSTRLEN + 1];
- ipv6_string_mapped(sn_addr_s, &sn_addr);
-
- ds_clear(&actions);
- ds_put_format(&actions,
- "nd_ns { "
- "eth.dst = "ETH_ADDR_FMT"; "
- "ip6.dst = %s; "
- "nd.target = %s; "
- "output; "
- "};", ETH_ADDR_ARGS(eth_dst), sn_addr_s,
- route->nexthop);
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 200,
- ds_cstr(&match), ds_cstr(&actions));
- }
-
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
- "eth.dst == 00:00:00:00:00:00",
- "arp { "
- "eth.dst = ff:ff:ff:ff:ff:ff; "
- "arp.spa = reg1; "
- "arp.tpa = reg0; "
- "arp.op = 1; " /* ARP request */
- "output; "
- "};");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
- "eth.dst == 00:00:00:00:00:00",
- "nd_ns { "
- "nd.target = xxreg0; "
- "output; "
- "};");
- ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;");
- }
-
- /* Logical router egress table 1: Delivery (priority 100).
- *
- * Priority 100 rules deliver packets to enabled logical ports. */
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbrp) {
- continue;
- }
-
- if (!lrport_is_enabled(op->nbrp)) {
- /* Drop packets to disabled logical ports (since logical flow
- * tables are default-drop). */
- continue;
- }
-
- if (op->derived) {
- /* No egress packets should be processed in the context of
- * a chassisredirect port. The chassisredirect port should
- * be replaced by the l3dgw port in the local output
- * pipeline stage before egress processing. */
- continue;
- }
-
- ds_clear(&match);
- ds_put_format(&match, "outport == %s", op->json_key);
- ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 100,
- ds_cstr(&match), "output;");
- }
-
- ds_destroy(&match);
- ds_destroy(&actions);
-}
-
-/* Updates the Logical_Flow and Multicast_Group tables in the OVN_SB database,
- * constructing their contents based on the OVN_NB database. */
-static void
-build_lflows(struct northd_context *ctx, struct hmap *datapaths,
- struct hmap *ports, struct hmap *port_groups,
- struct hmap *mcgroups, struct hmap *igmp_groups)
-{
- struct hmap lflows = HMAP_INITIALIZER(&lflows);
-
- build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups,
- igmp_groups);
- build_lrouter_flows(datapaths, ports, &lflows);
-
- /* Push changes to the Logical_Flow table to database. */
- const struct sbrec_logical_flow *sbflow, *next_sbflow;
- SBREC_LOGICAL_FLOW_FOR_EACH_SAFE (sbflow, next_sbflow, ctx->ovnsb_idl) {
- struct ovn_datapath *od
- = ovn_datapath_from_sbrec(datapaths, sbflow->logical_datapath);
- if (!od) {
- sbrec_logical_flow_delete(sbflow);
- continue;
- }
-
- enum ovn_datapath_type dp_type = od->nbs ? DP_SWITCH : DP_ROUTER;
- enum ovn_pipeline pipeline
- = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
- struct ovn_lflow *lflow = ovn_lflow_find(
- &lflows, od, ovn_stage_build(dp_type, pipeline, sbflow->table_id),
- sbflow->priority, sbflow->match, sbflow->actions, sbflow->hash);
- if (lflow) {
- ovn_lflow_destroy(&lflows, lflow);
- } else {
- sbrec_logical_flow_delete(sbflow);
- }
- }
- struct ovn_lflow *lflow, *next_lflow;
- HMAP_FOR_EACH_SAFE (lflow, next_lflow, hmap_node, &lflows) {
- const char *pipeline = ovn_stage_get_pipeline_name(lflow->stage);
- uint8_t table = ovn_stage_get_table(lflow->stage);
-
- sbflow = sbrec_logical_flow_insert(ctx->ovnsb_txn);
- sbrec_logical_flow_set_logical_datapath(sbflow, lflow->od->sb);
- sbrec_logical_flow_set_pipeline(sbflow, pipeline);
- sbrec_logical_flow_set_table_id(sbflow, table);
- sbrec_logical_flow_set_priority(sbflow, lflow->priority);
- sbrec_logical_flow_set_match(sbflow, lflow->match);
- sbrec_logical_flow_set_actions(sbflow, lflow->actions);
-
- /* Trim the source locator lflow->where, which looks something like
- * "ovn/northd/ovn-northd.c:1234", down to just the part following the
- * last slash, e.g. "ovn-northd.c:1234". */
- const char *slash = strrchr(lflow->where, '/');
-#if _WIN32
- const char *backslash = strrchr(lflow->where, '\\');
- if (!slash || backslash > slash) {
- slash = backslash;
- }
-#endif
- const char *where = slash ? slash + 1 : lflow->where;
-
- struct smap ids = SMAP_INITIALIZER(&ids);
- smap_add(&ids, "stage-name", ovn_stage_to_str(lflow->stage));
- smap_add(&ids, "source", where);
- if (lflow->stage_hint) {
- smap_add(&ids, "stage-hint", lflow->stage_hint);
- }
- sbrec_logical_flow_set_external_ids(sbflow, &ids);
- smap_destroy(&ids);
-
- ovn_lflow_destroy(&lflows, lflow);
- }
- hmap_destroy(&lflows);
-
- /* Push changes to the Multicast_Group table to database. */
- const struct sbrec_multicast_group *sbmc, *next_sbmc;
- SBREC_MULTICAST_GROUP_FOR_EACH_SAFE (sbmc, next_sbmc, ctx->ovnsb_idl) {
- struct ovn_datapath *od = ovn_datapath_from_sbrec(datapaths,
- sbmc->datapath);
- if (!od) {
- sbrec_multicast_group_delete(sbmc);
- continue;
- }
-
- struct multicast_group group = { .name = sbmc->name,
- .key = sbmc->tunnel_key };
- struct ovn_multicast *mc = ovn_multicast_find(mcgroups, od, &group);
- if (mc) {
- ovn_multicast_update_sbrec(mc, sbmc);
- ovn_multicast_destroy(mcgroups, mc);
- } else {
- sbrec_multicast_group_delete(sbmc);
- }
- }
- struct ovn_multicast *mc, *next_mc;
- HMAP_FOR_EACH_SAFE (mc, next_mc, hmap_node, mcgroups) {
- if (!mc->datapath) {
- ovn_multicast_destroy(mcgroups, mc);
- continue;
- }
- sbmc = sbrec_multicast_group_insert(ctx->ovnsb_txn);
- sbrec_multicast_group_set_datapath(sbmc, mc->datapath->sb);
- sbrec_multicast_group_set_name(sbmc, mc->group->name);
- sbrec_multicast_group_set_tunnel_key(sbmc, mc->group->key);
- ovn_multicast_update_sbrec(mc, sbmc);
- ovn_multicast_destroy(mcgroups, mc);
- }
-}
-
-static void
-sync_address_set(struct northd_context *ctx, const char *name,
- const char **addrs, size_t n_addrs,
- struct shash *sb_address_sets)
-{
- const struct sbrec_address_set *sb_address_set;
- sb_address_set = shash_find_and_delete(sb_address_sets,
- name);
- if (!sb_address_set) {
- sb_address_set = sbrec_address_set_insert(ctx->ovnsb_txn);
- sbrec_address_set_set_name(sb_address_set, name);
- }
-
- sbrec_address_set_set_addresses(sb_address_set,
- addrs, n_addrs);
-}
-
-/* Go through 'addresses' and add found IPv4 addresses to 'ipv4_addrs' and IPv6
- * addresses to 'ipv6_addrs'.
- */
-static void
-split_addresses(const char *addresses, struct svec *ipv4_addrs,
- struct svec *ipv6_addrs)
-{
- struct lport_addresses laddrs;
- extract_lsp_addresses(addresses, &laddrs);
- for (size_t k = 0; k < laddrs.n_ipv4_addrs; k++) {
- svec_add(ipv4_addrs, laddrs.ipv4_addrs[k].addr_s);
- }
- for (size_t k = 0; k < laddrs.n_ipv6_addrs; k++) {
- svec_add(ipv6_addrs, laddrs.ipv6_addrs[k].addr_s);
- }
- destroy_lport_addresses(&laddrs);
-}
-
-/* OVN_Southbound Address_Set table contains same records as in north
- * bound, plus the records generated from Port_Group table in north bound.
- *
- * There are 2 records generated from each port group, one for IPv4, and
- * one for IPv6, named in the format: <port group name>_ip4 and
- * <port group name>_ip6 respectively. MAC addresses are ignored.
- *
- * We always update OVN_Southbound to match the Address_Set and Port_Group
- * in OVN_Northbound, so that the address sets used in Logical_Flows in
- * OVN_Southbound is checked against the proper set.*/
-static void
-sync_address_sets(struct northd_context *ctx)
-{
- struct shash sb_address_sets = SHASH_INITIALIZER(&sb_address_sets);
-
- const struct sbrec_address_set *sb_address_set;
- SBREC_ADDRESS_SET_FOR_EACH (sb_address_set, ctx->ovnsb_idl) {
- shash_add(&sb_address_sets, sb_address_set->name, sb_address_set);
- }
-
- /* sync port group generated address sets first */
- const struct nbrec_port_group *nb_port_group;
- NBREC_PORT_GROUP_FOR_EACH (nb_port_group, ctx->ovnnb_idl) {
- struct svec ipv4_addrs = SVEC_EMPTY_INITIALIZER;
- struct svec ipv6_addrs = SVEC_EMPTY_INITIALIZER;
- for (size_t i = 0; i < nb_port_group->n_ports; i++) {
- for (size_t j = 0; j < nb_port_group->ports[i]->n_addresses; j++) {
- const char *addrs = nb_port_group->ports[i]->addresses[j];
- if (!is_dynamic_lsp_address(addrs)) {
- split_addresses(addrs, &ipv4_addrs, &ipv6_addrs);
- }
- }
- if (nb_port_group->ports[i]->dynamic_addresses) {
- split_addresses(nb_port_group->ports[i]->dynamic_addresses,
- &ipv4_addrs, &ipv6_addrs);
- }
- }
- char *ipv4_addrs_name = xasprintf("%s_ip4", nb_port_group->name);
- char *ipv6_addrs_name = xasprintf("%s_ip6", nb_port_group->name);
- sync_address_set(ctx, ipv4_addrs_name,
- /* "char **" is not compatible with "const char **" */
- (const char **)ipv4_addrs.names,
- ipv4_addrs.n, &sb_address_sets);
- sync_address_set(ctx, ipv6_addrs_name,
- /* "char **" is not compatible with "const char **" */
- (const char **)ipv6_addrs.names,
- ipv6_addrs.n, &sb_address_sets);
- free(ipv4_addrs_name);
- free(ipv6_addrs_name);
- svec_destroy(&ipv4_addrs);
- svec_destroy(&ipv6_addrs);
- }
-
- /* sync user defined address sets, which may overwrite port group
- * generated address sets if same name is used */
- const struct nbrec_address_set *nb_address_set;
- NBREC_ADDRESS_SET_FOR_EACH (nb_address_set, ctx->ovnnb_idl) {
- sync_address_set(ctx, nb_address_set->name,
- /* "char **" is not compatible with "const char **" */
- (const char **)nb_address_set->addresses,
- nb_address_set->n_addresses, &sb_address_sets);
- }
-
- struct shash_node *node, *next;
- SHASH_FOR_EACH_SAFE (node, next, &sb_address_sets) {
- sbrec_address_set_delete(node->data);
- shash_delete(&sb_address_sets, node);
- }
- shash_destroy(&sb_address_sets);
-}
-
-/* Each port group in Port_Group table in OVN_Northbound has a corresponding
- * entry in Port_Group table in OVN_Southbound. In OVN_Northbound the entries
- * contains lport uuids, while in OVN_Southbound we store the lport names.
- */
-static void
-sync_port_groups(struct northd_context *ctx)
-{
- struct shash sb_port_groups = SHASH_INITIALIZER(&sb_port_groups);
-
- const struct sbrec_port_group *sb_port_group;
- SBREC_PORT_GROUP_FOR_EACH (sb_port_group, ctx->ovnsb_idl) {
- shash_add(&sb_port_groups, sb_port_group->name, sb_port_group);
- }
-
- const struct nbrec_port_group *nb_port_group;
- NBREC_PORT_GROUP_FOR_EACH (nb_port_group, ctx->ovnnb_idl) {
- sb_port_group = shash_find_and_delete(&sb_port_groups,
- nb_port_group->name);
- if (!sb_port_group) {
- sb_port_group = sbrec_port_group_insert(ctx->ovnsb_txn);
- sbrec_port_group_set_name(sb_port_group, nb_port_group->name);
- }
-
- const char **nb_port_names = xcalloc(nb_port_group->n_ports,
- sizeof *nb_port_names);
- int i;
- for (i = 0; i < nb_port_group->n_ports; i++) {
- nb_port_names[i] = nb_port_group->ports[i]->name;
- }
- sbrec_port_group_set_ports(sb_port_group,
- nb_port_names,
- nb_port_group->n_ports);
- free(nb_port_names);
- }
-
- struct shash_node *node, *next;
- SHASH_FOR_EACH_SAFE (node, next, &sb_port_groups) {
- sbrec_port_group_delete(node->data);
- shash_delete(&sb_port_groups, node);
- }
- shash_destroy(&sb_port_groups);
-}
-
-struct band_entry {
- int64_t rate;
- int64_t burst_size;
- const char *action;
-};
-
-static int
-band_cmp(const void *band1_, const void *band2_)
-{
- const struct band_entry *band1p = band1_;
- const struct band_entry *band2p = band2_;
-
- if (band1p->rate != band2p->rate) {
- return band1p->rate > band2p->rate ? -1 : 1;
- } else if (band1p->burst_size != band2p->burst_size) {
- return band1p->burst_size > band2p->burst_size ? -1 : 1;
- } else {
- return strcmp(band1p->action, band2p->action);
- }
-}
-
-static bool
-bands_need_update(const struct nbrec_meter *nb_meter,
- const struct sbrec_meter *sb_meter)
-{
- if (nb_meter->n_bands != sb_meter->n_bands) {
- return true;
- }
-
- /* A single band is the most common scenario, so speed up that
- * check. */
- if (nb_meter->n_bands == 1) {
- struct nbrec_meter_band *nb_band = nb_meter->bands[0];
- struct sbrec_meter_band *sb_band = sb_meter->bands[0];
-
- return !(nb_band->rate == sb_band->rate
- && nb_band->burst_size == sb_band->burst_size
- && !strcmp(sb_band->action, nb_band->action));
- }
-
- /* Place the Northbound entries in sorted order. */
- struct band_entry *nb_bands;
- nb_bands = xmalloc(sizeof *nb_bands * nb_meter->n_bands);
- for (size_t i = 0; i < nb_meter->n_bands; i++) {
- struct nbrec_meter_band *nb_band = nb_meter->bands[i];
-
- nb_bands[i].rate = nb_band->rate;
- nb_bands[i].burst_size = nb_band->burst_size;
- nb_bands[i].action = nb_band->action;
- }
- qsort(nb_bands, nb_meter->n_bands, sizeof *nb_bands, band_cmp);
-
- /* Place the Southbound entries in sorted order. */
- struct band_entry *sb_bands;
- sb_bands = xmalloc(sizeof *sb_bands * sb_meter->n_bands);
- for (size_t i = 0; i < sb_meter->n_bands; i++) {
- struct sbrec_meter_band *sb_band = sb_meter->bands[i];
-
- sb_bands[i].rate = sb_band->rate;
- sb_bands[i].burst_size = sb_band->burst_size;
- sb_bands[i].action = sb_band->action;
- }
- qsort(sb_bands, sb_meter->n_bands, sizeof *sb_bands, band_cmp);
-
- bool need_update = false;
- for (size_t i = 0; i < nb_meter->n_bands; i++) {
- if (nb_bands[i].rate != sb_bands[i].rate
- || nb_bands[i].burst_size != sb_bands[i].burst_size
- || strcmp(nb_bands[i].action, sb_bands[i].action)) {
- need_update = true;
- goto done;
- }
- }
-
-done:
- free(nb_bands);
- free(sb_bands);
-
- return need_update;
-}
-
-/* Each entry in the Meter and Meter_Band tables in OVN_Northbound have
- * a corresponding entries in the Meter and Meter_Band tables in
- * OVN_Southbound.
- */
-static void
-sync_meters(struct northd_context *ctx)
-{
- struct shash sb_meters = SHASH_INITIALIZER(&sb_meters);
-
- const struct sbrec_meter *sb_meter;
- SBREC_METER_FOR_EACH (sb_meter, ctx->ovnsb_idl) {
- shash_add(&sb_meters, sb_meter->name, sb_meter);
- }
-
- const struct nbrec_meter *nb_meter;
- NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
- bool new_sb_meter = false;
-
- sb_meter = shash_find_and_delete(&sb_meters, nb_meter->name);
- if (!sb_meter) {
- sb_meter = sbrec_meter_insert(ctx->ovnsb_txn);
- sbrec_meter_set_name(sb_meter, nb_meter->name);
- new_sb_meter = true;
- }
-
- if (new_sb_meter || bands_need_update(nb_meter, sb_meter)) {
- struct sbrec_meter_band **sb_bands;
- sb_bands = xcalloc(nb_meter->n_bands, sizeof *sb_bands);
- for (size_t i = 0; i < nb_meter->n_bands; i++) {
- const struct nbrec_meter_band *nb_band = nb_meter->bands[i];
-
- sb_bands[i] = sbrec_meter_band_insert(ctx->ovnsb_txn);
-
- sbrec_meter_band_set_action(sb_bands[i], nb_band->action);
- sbrec_meter_band_set_rate(sb_bands[i], nb_band->rate);
- sbrec_meter_band_set_burst_size(sb_bands[i],
- nb_band->burst_size);
- }
- sbrec_meter_set_bands(sb_meter, sb_bands, nb_meter->n_bands);
- free(sb_bands);
- }
-
- sbrec_meter_set_unit(sb_meter, nb_meter->unit);
- }
-
- struct shash_node *node, *next;
- SHASH_FOR_EACH_SAFE (node, next, &sb_meters) {
- sbrec_meter_delete(node->data);
- shash_delete(&sb_meters, node);
- }
- shash_destroy(&sb_meters);
-}
-
-/*
- * struct 'dns_info' is used to sync the DNS records between OVN Northbound db
- * and Southbound db.
- */
-struct dns_info {
- struct hmap_node hmap_node;
- const struct nbrec_dns *nb_dns; /* DNS record in the Northbound db. */
- const struct sbrec_dns *sb_dns; /* DNS record in the Soutbound db. */
-
- /* Datapaths to which the DNS entry is associated with it. */
- const struct sbrec_datapath_binding **sbs;
- size_t n_sbs;
-};
-
-static inline struct dns_info *
-get_dns_info_from_hmap(struct hmap *dns_map, struct uuid *uuid)
-{
- struct dns_info *dns_info;
- size_t hash = uuid_hash(uuid);
- HMAP_FOR_EACH_WITH_HASH (dns_info, hmap_node, hash, dns_map) {
- if (uuid_equals(&dns_info->nb_dns->header_.uuid, uuid)) {
- return dns_info;
- }
- }
-
- return NULL;
-}
-
-static void
-sync_dns_entries(struct northd_context *ctx, struct hmap *datapaths)
-{
- struct hmap dns_map = HMAP_INITIALIZER(&dns_map);
- struct ovn_datapath *od;
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs || !od->nbs->n_dns_records) {
- continue;
- }
-
- for (size_t i = 0; i < od->nbs->n_dns_records; i++) {
- struct dns_info *dns_info = get_dns_info_from_hmap(
- &dns_map, &od->nbs->dns_records[i]->header_.uuid);
- if (!dns_info) {
- size_t hash = uuid_hash(
- &od->nbs->dns_records[i]->header_.uuid);
- dns_info = xzalloc(sizeof *dns_info);;
- dns_info->nb_dns = od->nbs->dns_records[i];
- hmap_insert(&dns_map, &dns_info->hmap_node, hash);
- }
-
- dns_info->n_sbs++;
- dns_info->sbs = xrealloc(dns_info->sbs,
- dns_info->n_sbs * sizeof *dns_info->sbs);
- dns_info->sbs[dns_info->n_sbs - 1] = od->sb;
- }
- }
-
- const struct sbrec_dns *sbrec_dns, *next;
- SBREC_DNS_FOR_EACH_SAFE (sbrec_dns, next, ctx->ovnsb_idl) {
- const char *nb_dns_uuid = smap_get(&sbrec_dns->external_ids, "dns_id");
- struct uuid dns_uuid;
- if (!nb_dns_uuid || !uuid_from_string(&dns_uuid, nb_dns_uuid)) {
- sbrec_dns_delete(sbrec_dns);
- continue;
- }
-
- struct dns_info *dns_info =
- get_dns_info_from_hmap(&dns_map, &dns_uuid);
- if (dns_info) {
- dns_info->sb_dns = sbrec_dns;
- } else {
- sbrec_dns_delete(sbrec_dns);
- }
- }
-
- struct dns_info *dns_info;
- HMAP_FOR_EACH_POP (dns_info, hmap_node, &dns_map) {
- if (!dns_info->sb_dns) {
- sbrec_dns = sbrec_dns_insert(ctx->ovnsb_txn);
- dns_info->sb_dns = sbrec_dns;
- char *dns_id = xasprintf(
- UUID_FMT, UUID_ARGS(&dns_info->nb_dns->header_.uuid));
- const struct smap external_ids =
- SMAP_CONST1(&external_ids, "dns_id", dns_id);
- sbrec_dns_set_external_ids(sbrec_dns, &external_ids);
- free(dns_id);
- }
-
- /* Set the datapaths and records. If nothing has changed, then
- * this will be a no-op.
- */
- sbrec_dns_set_datapaths(
- dns_info->sb_dns,
- (struct sbrec_datapath_binding **)dns_info->sbs,
- dns_info->n_sbs);
- sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records);
- free(dns_info->sbs);
- free(dns_info);
- }
- hmap_destroy(&dns_map);
-}
-
-static void
-destroy_datapaths_and_ports(struct hmap *datapaths, struct hmap *ports,
- struct ovs_list *lr_list)
-{
- struct ovn_datapath *router_dp;
- LIST_FOR_EACH_POP (router_dp, lr_list, lr_list) {
- if (router_dp->lr_group) {
- struct lrouter_group *lr_group = router_dp->lr_group;
-
- for (size_t i = 0; i < lr_group->n_router_dps; i++) {
- lr_group->router_dps[i]->lr_group = NULL;
- }
-
- free(lr_group->router_dps);
- sset_destroy(&lr_group->ha_chassis_groups);
- free(lr_group);
- }
- }
-
- struct ovn_datapath *dp, *next_dp;
- HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, datapaths) {
- ovn_datapath_destroy(datapaths, dp);
- }
- hmap_destroy(datapaths);
-
- struct ovn_port *port, *next_port;
- HMAP_FOR_EACH_SAFE (port, next_port, key_node, ports) {
- ovn_port_destroy(ports, port);
- }
- hmap_destroy(ports);
-}
-
-static void
-build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
-{
- struct ovn_datapath *od;
-
- HMAP_FOR_EACH (od, key_node, datapaths) {
- if (!od->nbs) {
- continue;
- }
-
- const struct sbrec_ip_multicast *ip_mcast =
- ip_mcast_lookup(ctx->sbrec_ip_mcast_by_dp, od->sb);
-
- if (!ip_mcast) {
- ip_mcast = sbrec_ip_multicast_insert(ctx->ovnsb_txn);
- }
- store_mcast_info_for_datapath(ip_mcast, od);
- }
-
- /* Delete southbound records without northbound matches. */
- const struct sbrec_ip_multicast *sb, *sb_next;
-
- SBREC_IP_MULTICAST_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
- if (!sb->datapath ||
- !ovn_datapath_from_sbrec(datapaths, sb->datapath)) {
- sbrec_ip_multicast_delete(sb);
- }
- }
-}
-
-static void
-build_mcast_groups(struct northd_context *ctx,
- struct hmap *datapaths, struct hmap *ports,
- struct hmap *mcast_groups,
- struct hmap *igmp_groups)
-{
- struct ovn_port *op;
-
- hmap_init(mcast_groups);
- hmap_init(igmp_groups);
-
- HMAP_FOR_EACH (op, key_node, ports) {
- if (!op->nbsp) {
- continue;
- }
-
- if (lsp_is_enabled(op->nbsp)) {
- ovn_multicast_add(mcast_groups, &mc_flood, op);
- }
- }
-
- const struct sbrec_igmp_group *sb_igmp, *sb_igmp_next;
-
- SBREC_IGMP_GROUP_FOR_EACH_SAFE (sb_igmp, sb_igmp_next, ctx->ovnsb_idl) {
- /* If this is a stale group (e.g., controller had crashed,
- * purge it).
- */
- if (!sb_igmp->chassis || !sb_igmp->datapath) {
- sbrec_igmp_group_delete(sb_igmp);
- continue;
- }
-
- /* If the datapath value is stale, purge the group. */
- struct ovn_datapath *od =
- ovn_datapath_from_sbrec(datapaths, sb_igmp->datapath);
- if (!od) {
- sbrec_igmp_group_delete(sb_igmp);
- continue;
- }
-
- /* Add the IGMP group entry. Will also try to allocate an ID for it
- * if the multicast group already exists.
- */
- ovn_igmp_group_add(ctx, igmp_groups, od, sb_igmp);
- }
-
- /* Walk the aggregated IGMP groups and allocate IDs for new entries.
- * Then store the ports in the associated multicast group.
- */
- struct ovn_igmp_group *igmp_group, *igmp_group_next;
- HMAP_FOR_EACH_SAFE (igmp_group, igmp_group_next, hmap_node, igmp_groups) {
- if (igmp_group->mcgroup.key == 0) {
- struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info;
- igmp_group->mcgroup.key = ovn_mcast_group_allocate_key(mcast_info);
- }
-
- /* If we ran out of keys just destroy the entry. */
- if (igmp_group->mcgroup.key == 0) {
- ovn_igmp_group_destroy(igmp_groups, igmp_group);
- continue;
- }
-
- /* Aggregate the ports from all SB entries corresponding to this
- * group.
- */
- ovn_igmp_group_aggregate_ports(igmp_group, ports, mcast_groups);
- }
-}
-
-static void
-ovnnb_db_run(struct northd_context *ctx,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- struct ovsdb_idl_loop *sb_loop,
- struct hmap *datapaths, struct hmap *ports,
- struct ovs_list *lr_list)
-{
- if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
- return;
- }
- struct hmap port_groups;
- struct hmap mcast_groups;
- struct hmap igmp_groups;
-
- build_datapaths(ctx, datapaths, lr_list);
- build_ports(ctx, sbrec_chassis_by_name, datapaths, ports);
- build_ipam(datapaths, ports);
- build_port_group_lswitches(ctx, &port_groups, ports);
- build_lrouter_groups(ports, lr_list);
- build_ip_mcast(ctx, datapaths);
- build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups);
- build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
- &igmp_groups);
-
- sync_address_sets(ctx);
- sync_port_groups(ctx);
- sync_meters(ctx);
- sync_dns_entries(ctx, datapaths);
-
- struct ovn_igmp_group *igmp_group, *next_igmp_group;
-
- HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, &igmp_groups) {
- ovn_igmp_group_destroy(&igmp_groups, igmp_group);
- }
-
- struct ovn_port_group *pg, *next_pg;
- HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) {
- ovn_port_group_destroy(&port_groups, pg);
- }
- hmap_destroy(&igmp_groups);
- hmap_destroy(&mcast_groups);
- hmap_destroy(&port_groups);
-
- /* Sync ipsec configuration.
- * Copy nb_cfg from northbound to southbound database.
- * Also set up to update sb_cfg once our southbound transaction commits. */
- const struct nbrec_nb_global *nb = nbrec_nb_global_first(ctx->ovnnb_idl);
- if (!nb) {
- nb = nbrec_nb_global_insert(ctx->ovnnb_txn);
- }
- const struct sbrec_sb_global *sb = sbrec_sb_global_first(ctx->ovnsb_idl);
- if (!sb) {
- sb = sbrec_sb_global_insert(ctx->ovnsb_txn);
- }
- if (nb->ipsec != sb->ipsec) {
- sbrec_sb_global_set_ipsec(sb, nb->ipsec);
- }
- sbrec_sb_global_set_nb_cfg(sb, nb->nb_cfg);
- sbrec_sb_global_set_options(sb, &nb->options);
- sb_loop->next_cfg = nb->nb_cfg;
-
- const char *mac_addr_prefix = smap_get(&nb->options, "mac_prefix");
- if (mac_addr_prefix) {
- struct eth_addr addr;
-
- memset(&addr, 0, sizeof addr);
- if (ovs_scan(mac_addr_prefix, "%"SCNx8":%"SCNx8":%"SCNx8,
- &addr.ea[0], &addr.ea[1], &addr.ea[2])) {
- mac_prefix = addr;
- }
- } else {
- struct smap options;
-
- smap_clone(&options, &nb->options);
- eth_addr_random(&mac_prefix);
- memset(&mac_prefix.ea[3], 0, 3);
-
- smap_add_format(&options, "mac_prefix",
- "%02"PRIx8":%02"PRIx8":%02"PRIx8,
- mac_prefix.ea[0], mac_prefix.ea[1], mac_prefix.ea[2]);
- nbrec_nb_global_verify_options(nb);
- nbrec_nb_global_set_options(nb, &options);
-
- smap_destroy(&options);
- }
-
- controller_event_en = smap_get_bool(&nb->options,
- "controller_event", false);
-
- cleanup_macam(&macam);
-}
-
-/* Stores the list of chassis which references an ha_chassis_group.
- */
-struct ha_ref_chassis_info {
- const struct sbrec_ha_chassis_group *ha_chassis_group;
- struct sbrec_chassis **ref_chassis;
- size_t n_ref_chassis;
- size_t free_slots;
-};
-
-static void
-add_to_ha_ref_chassis_info(struct ha_ref_chassis_info *ref_ch_info,
- const struct sbrec_chassis *chassis)
-{
- for (size_t j = 0; j < ref_ch_info->n_ref_chassis; j++) {
- if (ref_ch_info->ref_chassis[j] == chassis) {
- return;
- }
- }
-
- /* Allocate space for 3 chassis at a time. */
- if (!ref_ch_info->free_slots) {
- ref_ch_info->ref_chassis =
- xrealloc(ref_ch_info->ref_chassis,
- sizeof *ref_ch_info->ref_chassis *
- (ref_ch_info->n_ref_chassis + 3));
- ref_ch_info->free_slots = 3;
- }
-
- ref_ch_info->ref_chassis[ref_ch_info->n_ref_chassis] =
- CONST_CAST(struct sbrec_chassis *, chassis);
- ref_ch_info->n_ref_chassis++;
- ref_ch_info->free_slots--;
-}
-
-static void
-update_sb_ha_group_ref_chassis(struct shash *ha_ref_chassis_map)
-{
- struct shash_node *node, *next;
- SHASH_FOR_EACH_SAFE (node, next, ha_ref_chassis_map) {
- struct ha_ref_chassis_info *ha_ref_info = node->data;
- sbrec_ha_chassis_group_set_ref_chassis(ha_ref_info->ha_chassis_group,
- ha_ref_info->ref_chassis,
- ha_ref_info->n_ref_chassis);
- free(ha_ref_info->ref_chassis);
- free(ha_ref_info);
- shash_delete(ha_ref_chassis_map, node);
- }
-}
-
-/* This function checks if the port binding 'sb' references
- * a HA chassis group.
- * Eg. Suppose a distributed logical router port - lr0-public
- * uses an HA chassis group - hagrp1 and if hagrp1 has 3 ha
- * chassis - gw1, gw2 and gw3.
- * Or
- * If the distributed logical router port - lr0-public has
- * 3 gateway chassis - gw1, gw2 and gw3.
- * ovn-northd creates ha chassis group - hagrp1 in SB DB
- * and adds gw1, gw2 and gw3 to its ha_chassis list.
- *
- * If port binding 'sb' represents a logical switch port 'p1'
- * and its logical switch is connected to the logical router
- * 'lr0' directly or indirectly (i.e p1's logical switch is
- * connected to a router 'lr1' and 'lr1' has a path to lr0 via
- * transit logical switches) and 'sb' is claimed by chassis - 'c1' then
- * this function adds c1 to the list of the reference chassis
- * - 'ref_chassis' of hagrp1.
- */
-static void
-build_ha_chassis_group_ref_chassis(struct northd_context *ctx,
- const struct sbrec_port_binding *sb,
- struct ovn_port *op,
- struct shash *ha_ref_chassis_map)
-{
- struct lrouter_group *lr_group = NULL;
- for (size_t i = 0; i < op->od->n_router_ports; i++) {
- if (!op->od->router_ports[i]->peer) {
- continue;
- }
-
- lr_group = op->od->router_ports[i]->peer->od->lr_group;
- /* If a logical switch has multiple router ports, then
- * all the logical routers belong to the same logical
- * router group. */
- break;
- }
-
- if (!lr_group) {
- return;
- }
-
- const char *ha_group_name;
- SSET_FOR_EACH (ha_group_name, &lr_group->ha_chassis_groups) {
- const struct sbrec_ha_chassis_group *sb_ha_chassis_grp;
- sb_ha_chassis_grp = ha_chassis_group_lookup_by_name(
- ctx->sbrec_ha_chassis_grp_by_name, ha_group_name);
-
- if (sb_ha_chassis_grp) {
- struct ha_ref_chassis_info *ref_ch_info =
- shash_find_data(ha_ref_chassis_map, sb_ha_chassis_grp->name);
- ovs_assert(ref_ch_info);
- add_to_ha_ref_chassis_info(ref_ch_info, sb->chassis);
- }
- }
-}
-
-/* Handle changes to the 'chassis' column of the 'Port_Binding' table. When
- * this column is not empty, it means we need to set the corresponding logical
- * port as 'up' in the northbound DB. */
-static void
-handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
- struct shash *ha_ref_chassis_map)
-{
- const struct sbrec_port_binding *sb;
- bool build_ha_chassis_ref = false;
- if (ctx->ovnsb_txn) {
- const struct sbrec_ha_chassis_group *ha_ch_grp;
- SBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->ovnsb_idl) {
- struct ha_ref_chassis_info *ref_ch_info =
- xzalloc(sizeof *ref_ch_info);
- ref_ch_info->ha_chassis_group = ha_ch_grp;
- build_ha_chassis_ref = true;
- shash_add(ha_ref_chassis_map, ha_ch_grp->name, ref_ch_info);
- }
- }
-
- SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
- struct ovn_port *op = ovn_port_find(ports, sb->logical_port);
-
- if (!op || !op->nbsp) {
- /* The logical port doesn't exist for this port binding. This can
- * happen under normal circumstances when ovn-northd hasn't gotten
- * around to pruning the Port_Binding yet. */
- continue;
- }
-
- bool up = (sb->chassis || !strcmp(op->nbsp->type, "router"));
- if (!op->nbsp->up || *op->nbsp->up != up) {
- nbrec_logical_switch_port_set_up(op->nbsp, &up, 1);
- }
-
- if (build_ha_chassis_ref && ctx->ovnsb_txn && sb->chassis) {
- /* Check and add the chassis which has claimed this 'sb'
- * to the ha chassis group's ref_chassis if required. */
- build_ha_chassis_group_ref_chassis(ctx, sb, op,
- ha_ref_chassis_map);
- }
- }
-}
-
-static struct gen_opts_map supported_dhcp_opts[] = {
- OFFERIP,
- DHCP_OPT_NETMASK,
- DHCP_OPT_ROUTER,
- DHCP_OPT_DNS_SERVER,
- DHCP_OPT_LOG_SERVER,
- DHCP_OPT_LPR_SERVER,
- DHCP_OPT_SWAP_SERVER,
- DHCP_OPT_POLICY_FILTER,
- DHCP_OPT_ROUTER_SOLICITATION,
- DHCP_OPT_NIS_SERVER,
- DHCP_OPT_NTP_SERVER,
- DHCP_OPT_SERVER_ID,
- DHCP_OPT_TFTP_SERVER,
- DHCP_OPT_CLASSLESS_STATIC_ROUTE,
- DHCP_OPT_MS_CLASSLESS_STATIC_ROUTE,
- DHCP_OPT_IP_FORWARD_ENABLE,
- DHCP_OPT_ROUTER_DISCOVERY,
- DHCP_OPT_ETHERNET_ENCAP,
- DHCP_OPT_DEFAULT_TTL,
- DHCP_OPT_TCP_TTL,
- DHCP_OPT_MTU,
- DHCP_OPT_LEASE_TIME,
- DHCP_OPT_T1,
- DHCP_OPT_T2,
- DHCP_OPT_WPAD,
- DHCP_OPT_BOOTFILE,
- DHCP_OPT_PATH_PREFIX,
- DHCP_OPT_TFTP_SERVER_ADDRESS,
- DHCP_OPT_DOMAIN_NAME,
-};
-
-static struct gen_opts_map supported_dhcpv6_opts[] = {
- DHCPV6_OPT_IA_ADDR,
- DHCPV6_OPT_SERVER_ID,
- DHCPV6_OPT_DOMAIN_SEARCH,
- DHCPV6_OPT_DNS_SERVER
-};
-
-static void
-check_and_add_supported_dhcp_opts_to_sb_db(struct northd_context *ctx)
-{
- struct hmap dhcp_opts_to_add = HMAP_INITIALIZER(&dhcp_opts_to_add);
- for (size_t i = 0; (i < sizeof(supported_dhcp_opts) /
- sizeof(supported_dhcp_opts[0])); i++) {
- hmap_insert(&dhcp_opts_to_add, &supported_dhcp_opts[i].hmap_node,
- dhcp_opt_hash(supported_dhcp_opts[i].name));
- }
-
- const struct sbrec_dhcp_options *opt_row, *opt_row_next;
- SBREC_DHCP_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
- struct gen_opts_map *dhcp_opt =
- dhcp_opts_find(&dhcp_opts_to_add, opt_row->name);
- if (dhcp_opt) {
- hmap_remove(&dhcp_opts_to_add, &dhcp_opt->hmap_node);
- } else {
- sbrec_dhcp_options_delete(opt_row);
- }
- }
-
- struct gen_opts_map *opt;
- HMAP_FOR_EACH (opt, hmap_node, &dhcp_opts_to_add) {
- struct sbrec_dhcp_options *sbrec_dhcp_option =
- sbrec_dhcp_options_insert(ctx->ovnsb_txn);
- sbrec_dhcp_options_set_name(sbrec_dhcp_option, opt->name);
- sbrec_dhcp_options_set_code(sbrec_dhcp_option, opt->code);
- sbrec_dhcp_options_set_type(sbrec_dhcp_option, opt->type);
- }
-
- hmap_destroy(&dhcp_opts_to_add);
-}
-
-static void
-check_and_add_supported_dhcpv6_opts_to_sb_db(struct northd_context *ctx)
-{
- struct hmap dhcpv6_opts_to_add = HMAP_INITIALIZER(&dhcpv6_opts_to_add);
- for (size_t i = 0; (i < sizeof(supported_dhcpv6_opts) /
- sizeof(supported_dhcpv6_opts[0])); i++) {
- hmap_insert(&dhcpv6_opts_to_add, &supported_dhcpv6_opts[i].hmap_node,
- dhcp_opt_hash(supported_dhcpv6_opts[i].name));
- }
-
- const struct sbrec_dhcpv6_options *opt_row, *opt_row_next;
- SBREC_DHCPV6_OPTIONS_FOR_EACH_SAFE(opt_row, opt_row_next, ctx->ovnsb_idl) {
- struct gen_opts_map *dhcp_opt =
- dhcp_opts_find(&dhcpv6_opts_to_add, opt_row->name);
- if (dhcp_opt) {
- hmap_remove(&dhcpv6_opts_to_add, &dhcp_opt->hmap_node);
- } else {
- sbrec_dhcpv6_options_delete(opt_row);
- }
- }
-
- struct gen_opts_map *opt;
- HMAP_FOR_EACH(opt, hmap_node, &dhcpv6_opts_to_add) {
- struct sbrec_dhcpv6_options *sbrec_dhcpv6_option =
- sbrec_dhcpv6_options_insert(ctx->ovnsb_txn);
- sbrec_dhcpv6_options_set_name(sbrec_dhcpv6_option, opt->name);
- sbrec_dhcpv6_options_set_code(sbrec_dhcpv6_option, opt->code);
- sbrec_dhcpv6_options_set_type(sbrec_dhcpv6_option, opt->type);
- }
-
- hmap_destroy(&dhcpv6_opts_to_add);
-}
-
-static const char *rbac_chassis_auth[] =
- {"name"};
-static const char *rbac_chassis_update[] =
- {"nb_cfg", "external_ids", "encaps", "vtep_logical_switches"};
-
-static const char *rbac_encap_auth[] =
- {"chassis_name"};
-static const char *rbac_encap_update[] =
- {"type", "options", "ip"};
-
-static const char *rbac_port_binding_auth[] =
- {""};
-static const char *rbac_port_binding_update[] =
- {"chassis"};
-
-static const char *rbac_mac_binding_auth[] =
- {""};
-static const char *rbac_mac_binding_update[] =
- {"logical_port", "ip", "mac", "datapath"};
-
-static struct rbac_perm_cfg {
- const char *table;
- const char **auth;
- int n_auth;
- bool insdel;
- const char **update;
- int n_update;
- const struct sbrec_rbac_permission *row;
-} rbac_perm_cfg[] = {
- {
- .table = "Chassis",
- .auth = rbac_chassis_auth,
- .n_auth = ARRAY_SIZE(rbac_chassis_auth),
- .insdel = true,
- .update = rbac_chassis_update,
- .n_update = ARRAY_SIZE(rbac_chassis_update),
- .row = NULL
- },{
- .table = "Encap",
- .auth = rbac_encap_auth,
- .n_auth = ARRAY_SIZE(rbac_encap_auth),
- .insdel = true,
- .update = rbac_encap_update,
- .n_update = ARRAY_SIZE(rbac_encap_update),
- .row = NULL
- },{
- .table = "Port_Binding",
- .auth = rbac_port_binding_auth,
- .n_auth = ARRAY_SIZE(rbac_port_binding_auth),
- .insdel = false,
- .update = rbac_port_binding_update,
- .n_update = ARRAY_SIZE(rbac_port_binding_update),
- .row = NULL
- },{
- .table = "MAC_Binding",
- .auth = rbac_mac_binding_auth,
- .n_auth = ARRAY_SIZE(rbac_mac_binding_auth),
- .insdel = true,
- .update = rbac_mac_binding_update,
- .n_update = ARRAY_SIZE(rbac_mac_binding_update),
- .row = NULL
- },{
- .table = NULL,
- .auth = NULL,
- .n_auth = 0,
- .insdel = false,
- .update = NULL,
- .n_update = 0,
- .row = NULL
- }
-};
-
-static bool
-ovn_rbac_validate_perm(const struct sbrec_rbac_permission *perm)
-{
- struct rbac_perm_cfg *pcfg;
- int i, j, n_found;
-
- for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
- if (!strcmp(perm->table, pcfg->table)) {
- break;
- }
- }
- if (!pcfg->table) {
- return false;
- }
- if (perm->n_authorization != pcfg->n_auth ||
- perm->n_update != pcfg->n_update) {
- return false;
- }
- if (perm->insert_delete != pcfg->insdel) {
- return false;
- }
- /* verify perm->authorization vs. pcfg->auth */
- n_found = 0;
- for (i = 0; i < pcfg->n_auth; i++) {
- for (j = 0; j < perm->n_authorization; j++) {
- if (!strcmp(pcfg->auth[i], perm->authorization[j])) {
- n_found++;
- break;
- }
- }
- }
- if (n_found != pcfg->n_auth) {
- return false;
- }
-
- /* verify perm->update vs. pcfg->update */
- n_found = 0;
- for (i = 0; i < pcfg->n_update; i++) {
- for (j = 0; j < perm->n_update; j++) {
- if (!strcmp(pcfg->update[i], perm->update[j])) {
- n_found++;
- break;
- }
- }
- }
- if (n_found != pcfg->n_update) {
- return false;
- }
-
- /* Success, db state matches expected state */
- pcfg->row = perm;
- return true;
-}
-
-static void
-ovn_rbac_create_perm(struct rbac_perm_cfg *pcfg,
- struct northd_context *ctx,
- const struct sbrec_rbac_role *rbac_role)
-{
- struct sbrec_rbac_permission *rbac_perm;
-
- rbac_perm = sbrec_rbac_permission_insert(ctx->ovnsb_txn);
- sbrec_rbac_permission_set_table(rbac_perm, pcfg->table);
- sbrec_rbac_permission_set_authorization(rbac_perm,
- pcfg->auth,
- pcfg->n_auth);
- sbrec_rbac_permission_set_insert_delete(rbac_perm, pcfg->insdel);
- sbrec_rbac_permission_set_update(rbac_perm,
- pcfg->update,
- pcfg->n_update);
- sbrec_rbac_role_update_permissions_setkey(rbac_role, pcfg->table,
- rbac_perm);
-}
-
-static void
-check_and_update_rbac(struct northd_context *ctx)
-{
- const struct sbrec_rbac_role *rbac_role = NULL;
- const struct sbrec_rbac_permission *perm_row, *perm_next;
- const struct sbrec_rbac_role *role_row, *role_row_next;
- struct rbac_perm_cfg *pcfg;
-
- for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
- pcfg->row = NULL;
- }
-
- SBREC_RBAC_PERMISSION_FOR_EACH_SAFE (perm_row, perm_next, ctx->ovnsb_idl) {
- if (!ovn_rbac_validate_perm(perm_row)) {
- sbrec_rbac_permission_delete(perm_row);
- }
- }
- SBREC_RBAC_ROLE_FOR_EACH_SAFE (role_row, role_row_next, ctx->ovnsb_idl) {
- if (strcmp(role_row->name, "ovn-controller")) {
- sbrec_rbac_role_delete(role_row);
- } else {
- rbac_role = role_row;
- }
- }
-
- if (!rbac_role) {
- rbac_role = sbrec_rbac_role_insert(ctx->ovnsb_txn);
- sbrec_rbac_role_set_name(rbac_role, "ovn-controller");
- }
-
- for (pcfg = rbac_perm_cfg; pcfg->table; pcfg++) {
- if (!pcfg->row) {
- ovn_rbac_create_perm(pcfg, ctx, rbac_role);
- }
- }
-}
-
-/* Updates the sb_cfg and hv_cfg columns in the northbound NB_Global table. */
-static void
-update_northbound_cfg(struct northd_context *ctx,
- struct ovsdb_idl_loop *sb_loop)
-{
- /* Update northbound sb_cfg if appropriate. */
- const struct nbrec_nb_global *nbg = nbrec_nb_global_first(ctx->ovnnb_idl);
- int64_t sb_cfg = sb_loop->cur_cfg;
- if (nbg && sb_cfg && nbg->sb_cfg != sb_cfg) {
- nbrec_nb_global_set_sb_cfg(nbg, sb_cfg);
- }
-
- /* Update northbound hv_cfg if appropriate. */
- if (nbg) {
- /* Find minimum nb_cfg among all chassis. */
- const struct sbrec_chassis *chassis;
- int64_t hv_cfg = nbg->nb_cfg;
- SBREC_CHASSIS_FOR_EACH (chassis, ctx->ovnsb_idl) {
- if (chassis->nb_cfg < hv_cfg) {
- hv_cfg = chassis->nb_cfg;
- }
- }
-
- /* Update hv_cfg. */
- if (nbg->hv_cfg != hv_cfg) {
- nbrec_nb_global_set_hv_cfg(nbg, hv_cfg);
- }
- }
-}
-
-/* Handle a fairly small set of changes in the southbound database. */
-static void
-ovnsb_db_run(struct northd_context *ctx, struct ovsdb_idl_loop *sb_loop,
- struct hmap *ports)
-{
- if (!ctx->ovnnb_txn || !ovsdb_idl_has_ever_connected(ctx->ovnsb_idl)) {
- return;
- }
-
- struct shash ha_ref_chassis_map = SHASH_INITIALIZER(&ha_ref_chassis_map);
- handle_port_binding_changes(ctx, ports, &ha_ref_chassis_map);
- update_northbound_cfg(ctx, sb_loop);
- if (ctx->ovnsb_txn) {
- update_sb_ha_group_ref_chassis(&ha_ref_chassis_map);
- }
- shash_destroy(&ha_ref_chassis_map);
-}
-
-static void
-ovn_db_run(struct northd_context *ctx,
- struct ovsdb_idl_index *sbrec_chassis_by_name,
- struct ovsdb_idl_loop *ovnsb_idl_loop)
-{
- struct hmap datapaths, ports;
- struct ovs_list lr_list;
- ovs_list_init(&lr_list);
- hmap_init(&datapaths);
- hmap_init(&ports);
- ovnnb_db_run(ctx, sbrec_chassis_by_name, ovnsb_idl_loop,
- &datapaths, &ports, &lr_list);
- ovnsb_db_run(ctx, ovnsb_idl_loop, &ports);
- destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);
-}
-
-static void
-parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
-{
- enum {
- DAEMON_OPTION_ENUMS,
- VLOG_OPTION_ENUMS,
- SSL_OPTION_ENUMS,
- };
- static const struct option long_options[] = {
- {"ovnsb-db", required_argument, NULL, 'd'},
- {"ovnnb-db", required_argument, NULL, 'D'},
- {"unixctl", required_argument, NULL, 'u'},
- {"help", no_argument, NULL, 'h'},
- {"options", no_argument, NULL, 'o'},
- {"version", no_argument, NULL, 'V'},
- DAEMON_LONG_OPTIONS,
- VLOG_LONG_OPTIONS,
- STREAM_SSL_LONG_OPTIONS,
- {NULL, 0, NULL, 0},
- };
- char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
-
- for (;;) {
- int c;
-
- c = getopt_long(argc, argv, short_options, long_options, NULL);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- DAEMON_OPTION_HANDLERS;
- VLOG_OPTION_HANDLERS;
- STREAM_SSL_OPTION_HANDLERS;
-
- case 'd':
- ovnsb_db = optarg;
- break;
-
- case 'D':
- ovnnb_db = optarg;
- break;
-
- case 'u':
- unixctl_path = optarg;
- break;
-
- case 'h':
- usage();
- exit(EXIT_SUCCESS);
-
- case 'o':
- ovs_cmdl_print_options(long_options);
- exit(EXIT_SUCCESS);
-
- case 'V':
- ovs_print_version(0, 0);
- exit(EXIT_SUCCESS);
-
- default:
- break;
- }
- }
-
- if (!ovnsb_db) {
- ovnsb_db = default_sb_db();
- }
-
- if (!ovnnb_db) {
- ovnnb_db = default_nb_db();
- }
-
- free(short_options);
-}
-
-static void
-add_column_noalert(struct ovsdb_idl *idl,
- const struct ovsdb_idl_column *column)
-{
- ovsdb_idl_add_column(idl, column);
- ovsdb_idl_omit_alert(idl, column);
-}
-
-int
-main(int argc, char *argv[])
-{
- int res = EXIT_SUCCESS;
- struct unixctl_server *unixctl;
- int retval;
- bool exiting;
-
- fatal_ignore_sigpipe();
- ovs_cmdl_proctitle_init(argc, argv);
- set_program_name(argv[0]);
- service_start(&argc, &argv);
- parse_options(argc, argv);
-
- daemonize_start(false);
-
- retval = unixctl_server_create(unixctl_path, &unixctl);
- if (retval) {
- exit(EXIT_FAILURE);
- }
- unixctl_command_register("exit", "", 0, 0, ovn_northd_exit, &exiting);
-
- daemonize_complete();
-
- /* We want to detect (almost) all changes to the ovn-nb db. */
- struct ovsdb_idl_loop ovnnb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
- ovsdb_idl_create(ovnnb_db, &nbrec_idl_class, true, true));
- ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_sb_cfg);
- ovsdb_idl_omit_alert(ovnnb_idl_loop.idl, &nbrec_nb_global_col_hv_cfg);
-
- /* We want to detect only selected changes to the ovn-sb db. */
- struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
- ovsdb_idl_create(ovnsb_db, &sbrec_idl_class, false, true));
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_sb_global);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_nb_cfg);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_options);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_sb_global_col_ipsec);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_logical_flow);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_logical_flow_col_logical_datapath);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_pipeline);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_table_id);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_priority);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_match);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_actions);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_multicast_group);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_multicast_group_col_datapath);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_multicast_group_col_tunnel_key);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_name);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_multicast_group_col_ports);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_datapath_binding);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_datapath_binding_col_tunnel_key);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_datapath_binding_col_external_ids);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_binding);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_datapath);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_logical_port);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_tunnel_key);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_parent_port);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_tag);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_type);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_options);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_binding_col_mac);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_nat_addresses);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_gateway_chassis);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_ha_chassis_group);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl,
- &sbrec_gateway_chassis_col_chassis);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_gateway_chassis_col_name);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl,
- &sbrec_gateway_chassis_col_priority);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl,
- &sbrec_gateway_chassis_col_external_ids);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl,
- &sbrec_gateway_chassis_col_options);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_port_binding_col_external_ids);
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_mac_binding);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_datapath);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_ip);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_mac_binding_col_mac);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_mac_binding_col_logical_port);
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcp_options);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_code);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_type);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcp_options_col_name);
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dhcpv6_options);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_code);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_type);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dhcpv6_options_col_name);
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_address_set);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_name);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_address_set_col_addresses);
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_port_group);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_name);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_port_group_col_ports);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_dns);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_datapaths);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_records);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_dns_col_external_ids);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_role);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_name);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_role_col_permissions);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_rbac_permission);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_rbac_permission_col_table);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_rbac_permission_col_authorization);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_rbac_permission_col_insert_delete);
- add_column_noalert(ovnsb_idl_loop.idl, &sbrec_rbac_permission_col_update);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_name);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_unit);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_col_bands);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_meter_band);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_action);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_rate);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_meter_band_col_burst_size);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_chassis);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_col_chassis);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_col_priority);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_col_external_ids);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis_group);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_group_col_name);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_group_col_ha_chassis);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_group_col_external_ids);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ha_chassis_group_col_ref_chassis);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_igmp_group);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_address);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_datapath);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_chassis);
- ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_igmp_group_col_ports);
-
- ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ip_multicast);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_datapath);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_enabled);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_querier);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_eth_src);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_ip4_src);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_table_size);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_idle_timeout);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_query_interval);
- add_column_noalert(ovnsb_idl_loop.idl,
- &sbrec_ip_multicast_col_query_max_resp);
-
- struct ovsdb_idl_index *sbrec_chassis_by_name
- = chassis_index_create(ovnsb_idl_loop.idl);
-
- struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name
- = ha_chassis_group_index_create(ovnsb_idl_loop.idl);
-
- struct ovsdb_idl_index *sbrec_mcast_group_by_name_dp
- = mcast_group_index_create(ovnsb_idl_loop.idl);
-
- struct ovsdb_idl_index *sbrec_ip_mcast_by_dp
- = ip_mcast_index_create(ovnsb_idl_loop.idl);
-
- /* Ensure that only a single ovn-northd is active in the deployment by
- * acquiring a lock called "ovn_northd" on the southbound database
- * and then only performing DB transactions if the lock is held. */
- ovsdb_idl_set_lock(ovnsb_idl_loop.idl, "ovn_northd");
- bool had_lock = false;
-
- /* Main loop. */
- exiting = false;
- while (!exiting) {
- struct northd_context ctx = {
- .ovnnb_idl = ovnnb_idl_loop.idl,
- .ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
- .ovnsb_idl = ovnsb_idl_loop.idl,
- .ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
- .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name,
- .sbrec_mcast_group_by_name_dp = sbrec_mcast_group_by_name_dp,
- .sbrec_ip_mcast_by_dp = sbrec_ip_mcast_by_dp,
- };
-
- if (!had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
- VLOG_INFO("ovn-northd lock acquired. "
- "This ovn-northd instance is now active.");
- had_lock = true;
- } else if (had_lock && !ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
- VLOG_INFO("ovn-northd lock lost. "
- "This ovn-northd instance is now on standby.");
- had_lock = false;
- }
-
- if (ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
- ovn_db_run(&ctx, sbrec_chassis_by_name, &ovnsb_idl_loop);
- if (ctx.ovnsb_txn) {
- check_and_add_supported_dhcp_opts_to_sb_db(&ctx);
- check_and_add_supported_dhcpv6_opts_to_sb_db(&ctx);
- check_and_update_rbac(&ctx);
- }
- }
-
- unixctl_server_run(unixctl);
- unixctl_server_wait(unixctl);
- if (exiting) {
- poll_immediate_wake();
- }
- ovsdb_idl_loop_commit_and_wait(&ovnnb_idl_loop);
- ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
-
- poll_block();
- if (should_service_stop()) {
- exiting = true;
- }
- }
-
- unixctl_server_destroy(unixctl);
- ovsdb_idl_loop_destroy(&ovnnb_idl_loop);
- ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
- service_stop();
-
- exit(res);
-}
-
-static void
-ovn_northd_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *exiting_)
-{
- bool *exiting = exiting_;
- *exiting = true;
-
- unixctl_command_reply(conn, NULL);
-}
diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
deleted file mode 100644
index c4099f25a..000000000
--- a/ovn/ovn-architecture.7.xml
+++ /dev/null
@@ -1,2074 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manpage program="ovn-architecture" section="7" title="OVN Architecture">
- <h1>Name</h1>
- <p>ovn-architecture -- Open Virtual Network architecture</p>
-
- <h1>Description</h1>
-
- <p>
- OVN, the Open Virtual Network, is a system to support virtual network
- abstraction. OVN complements the existing capabilities of OVS to add
- native support for virtual network abstractions, such as virtual L2 and L3
- overlays and security groups. Services such as DHCP are also desirable
- features. Just like OVS, OVN's design goal is to have a production-quality
- implementation that can operate at significant scale.
- </p>
-
- <p>
- An OVN deployment consists of several components:
- </p>
-
- <ul>
- <li>
- <p>
- A <dfn>Cloud Management System</dfn> (<dfn>CMS</dfn>), which is
- OVN's ultimate client (via its users and administrators). OVN
- integration requires installing a CMS-specific plugin and
- related software (see below). OVN initially targets OpenStack
- as CMS.
- </p>
-
- <p>
- We generally speak of ``the'' CMS, but one can imagine scenarios in
- which multiple CMSes manage different parts of an OVN deployment.
- </p>
- </li>
-
- <li>
- An OVN Database physical or virtual node (or, eventually, cluster)
- installed in a central location.
- </li>
-
- <li>
- One or more (usually many) <dfn>hypervisors</dfn>. Hypervisors must run
- Open vSwitch and implement the interface described in
- <code>IntegrationGuide.rst</code> in the OVS source tree. Any hypervisor
- platform supported by Open vSwitch is acceptable.
- </li>
-
- <li>
- <p>
- Zero or more <dfn>gateways</dfn>. A gateway extends a tunnel-based
- logical network into a physical network by bidirectionally forwarding
- packets between tunnels and a physical Ethernet port. This allows
- non-virtualized machines to participate in logical networks. A gateway
- may be a physical host, a virtual machine, or an ASIC-based hardware
- switch that supports the <code>vtep</code>(5) schema.
- </p>
-
- <p>
- Hypervisors and gateways are together called <dfn>transport node</dfn>
- or <dfn>chassis</dfn>.
- </p>
- </li>
- </ul>
-
- <p>
- The diagram below shows how the major components of OVN and related
- software interact. Starting at the top of the diagram, we have:
- </p>
-
- <ul>
- <li>
- The Cloud Management System, as defined above.
- </li>
-
- <li>
- <p>
- The <dfn>OVN/CMS Plugin</dfn> is the component of the CMS that
- interfaces to OVN. In OpenStack, this is a Neutron plugin.
- The plugin's main purpose is to translate the CMS's notion of logical
- network configuration, stored in the CMS's configuration database in a
- CMS-specific format, into an intermediate representation understood by
- OVN.
- </p>
-
- <p>
- This component is necessarily CMS-specific, so a new plugin needs to be
- developed for each CMS that is integrated with OVN. All of the
- components below this one in the diagram are CMS-independent.
- </p>
- </li>
-
- <li>
- <p>
- The <dfn>OVN Northbound Database</dfn> receives the intermediate
- representation of logical network configuration passed down by the
- OVN/CMS Plugin. The database schema is meant to be ``impedance
- matched'' with the concepts used in a CMS, so that it directly supports
- notions of logical switches, routers, ACLs, and so on. See
- <code>ovn-nb</code>(5) for details.
- </p>
-
- <p>
- The OVN Northbound Database has only two clients: the OVN/CMS Plugin
- above it and <code>ovn-northd</code> below it.
- </p>
- </li>
-
- <li>
- <code>ovn-northd</code>(8) connects to the OVN Northbound Database
- above it and the OVN Southbound Database below it. It translates the
- logical network configuration in terms of conventional network
- concepts, taken from the OVN Northbound Database, into logical
- datapath flows in the OVN Southbound Database below it.
- </li>
-
- <li>
- <p>
- The <dfn>OVN Southbound Database</dfn> is the center of the system.
- Its clients are <code>ovn-northd</code>(8) above it and
- <code>ovn-controller</code>(8) on every transport node below it.
- </p>
-
- <p>
- The OVN Southbound Database contains three kinds of data: <dfn>Physical
- Network</dfn> (PN) tables that specify how to reach hypervisor and
- other nodes, <dfn>Logical Network</dfn> (LN) tables that describe the
- logical network in terms of ``logical datapath flows,'' and
- <dfn>Binding</dfn> tables that link logical network components'
- locations to the physical network. The hypervisors populate the PN and
- Port_Binding tables, whereas <code>ovn-northd</code>(8) populates the
- LN tables.
- </p>
-
- <p>
- OVN Southbound Database performance must scale with the number of
- transport nodes. This will likely require some work on
- <code>ovsdb-server</code>(1) as we encounter bottlenecks.
- Clustering for availability may be needed.
- </p>
- </li>
- </ul>
-
- <p>
- The remaining components are replicated onto each hypervisor:
- </p>
-
- <ul>
- <li>
- <code>ovn-controller</code>(8) is OVN's agent on each hypervisor and
- software gateway. Northbound, it connects to the OVN Southbound
- Database to learn about OVN configuration and status and to
- populate the PN table and the <code>Chassis</code> column in
- <code>Binding</code> table with the hypervisor's status.
- Southbound, it connects to <code>ovs-vswitchd</code>(8) as an
- OpenFlow controller, for control over network traffic, and to the
- local <code>ovsdb-server</code>(1) to allow it to monitor and
- control Open vSwitch configuration.
- </li>
-
- <li>
- <code>ovs-vswitchd</code>(8) and <code>ovsdb-server</code>(1) are
- conventional components of Open vSwitch.
- </li>
- </ul>
-
- <pre fixed="yes">
- CMS
- |
- |
- +-----------|-----------+
- | | |
- | OVN/CMS Plugin |
- | | |
- | | |
- | OVN Northbound DB |
- | | |
- | | |
- | ovn-northd |
- | | |
- +-----------|-----------+
- |
- |
- +-------------------+
- | OVN Southbound DB |
- +-------------------+
- |
- |
- +------------------+------------------+
- | | |
- HV 1 | | HV n |
-+---------------|---------------+ . +---------------|---------------+
-| | | . | | |
-| ovn-controller | . | ovn-controller |
-| | | | . | | | |
-| | | | | | | |
-| ovs-vswitchd ovsdb-server | | ovs-vswitchd ovsdb-server |
-| | | |
-+-------------------------------+ +-------------------------------+
- </pre>
-
- <h2>Information Flow in OVN</h2>
-
- <p>
- Configuration data in OVN flows from north to south. The CMS, through its
- OVN/CMS plugin, passes the logical network configuration to
- <code>ovn-northd</code> via the northbound database. In turn,
- <code>ovn-northd</code> compiles the configuration into a lower-level form
- and passes it to all of the chassis via the southbound database.
- </p>
-
- <p>
- Status information in OVN flows from south to north. OVN currently
- provides only a few forms of status information. First,
- <code>ovn-northd</code> populates the <code>up</code> column in the
- northbound <code>Logical_Switch_Port</code> table: if a logical port's
- <code>chassis</code> column in the southbound <code>Port_Binding</code>
- table is nonempty, it sets <code>up</code> to <code>true</code>, otherwise
- to <code>false</code>. This allows the CMS to detect when a VM's
- networking has come up.
- </p>
-
- <p>
- Second, OVN provides feedback to the CMS on the realization of its
- configuration, that is, whether the configuration provided by the CMS has
- taken effect. This feature requires the CMS to participate in a sequence
- number protocol, which works the following way:
- </p>
-
- <ol>
- <li>
- When the CMS updates the configuration in the northbound database, as
- part of the same transaction, it increments the value of the
- <code>nb_cfg</code> column in the <code>NB_Global</code> table. (This is
- only necessary if the CMS wants to know when the configuration has been
- realized.)
- </li>
-
- <li>
- When <code>ovn-northd</code> updates the southbound database based on a
- given snapshot of the northbound database, it copies <code>nb_cfg</code>
- from northbound <code>NB_Global</code> into the southbound database
- <code>SB_Global</code> table, as part of the same transaction. (Thus, an
- observer monitoring both databases can determine when the southbound
- database is caught up with the northbound.)
- </li>
-
- <li>
- After <code>ovn-northd</code> receives confirmation from the southbound
- database server that its changes have committed, it updates
- <code>sb_cfg</code> in the northbound <code>NB_Global</code> table to the
- <code>nb_cfg</code> version that was pushed down. (Thus, the CMS or
- another observer can determine when the southbound database is caught up
- without a connection to the southbound database.)
- </li>
-
- <li>
- The <code>ovn-controller</code> process on each chassis receives the
- updated southbound database, with the updated <code>nb_cfg</code>. This
- process in turn updates the physical flows installed in the chassis's
- Open vSwitch instances. When it receives confirmation from Open vSwitch
- that the physical flows have been updated, it updates <code>nb_cfg</code>
- in its own <code>Chassis</code> record in the southbound database.
- </li>
-
- <li>
- <code>ovn-northd</code> monitors the <code>nb_cfg</code> column in all of
- the <code>Chassis</code> records in the southbound database. It keeps
- track of the minimum value among all the records and copies it into the
- <code>hv_cfg</code> column in the northbound <code>NB_Global</code>
- table. (Thus, the CMS or another observer can determine when all of the
- hypervisors have caught up to the northbound configuration.)
- </li>
- </ol>
-
- <h2>Chassis Setup</h2>
-
- <p>
- Each chassis in an OVN deployment must be configured with an Open vSwitch
- bridge dedicated for OVN's use, called the <dfn>integration bridge</dfn>.
- System startup scripts may create this bridge prior to starting
- <code>ovn-controller</code> if desired. If this bridge does not exist when
- ovn-controller starts, it will be created automatically with the default
- configuration suggested below. The ports on the integration bridge include:
- </p>
-
- <ul>
- <li>
- On any chassis, tunnel ports that OVN uses to maintain logical network
- connectivity. <code>ovn-controller</code> adds, updates, and removes
- these tunnel ports.
- </li>
-
- <li>
- On a hypervisor, any VIFs that are to be attached to logical networks.
- The hypervisor itself, or the integration between Open vSwitch and the
- hypervisor (described in <code>IntegrationGuide.rst</code>) takes care of
- this. (This is not part of OVN or new to OVN; this is pre-existing
- integration work that has already been done on hypervisors that support
- OVS.)
- </li>
-
- <li>
- On a gateway, the physical port used for logical network connectivity.
- System startup scripts add this port to the bridge prior to starting
- <code>ovn-controller</code>. This can be a patch port to another bridge,
- instead of a physical port, in more sophisticated setups.
- </li>
- </ul>
-
- <p>
- Other ports should not be attached to the integration bridge. In
- particular, physical ports attached to the underlay network (as opposed to
- gateway ports, which are physical ports attached to logical networks) must
- not be attached to the integration bridge. Underlay physical ports should
- instead be attached to a separate Open vSwitch bridge (they need not be
- attached to any bridge at all, in fact).
- </p>
-
- <p>
- The integration bridge should be configured as described below.
- The effect of each of these settings is documented in
- <code>ovs-vswitchd.conf.db</code>(5):
- </p>
-
- <!-- Keep the following in sync with create_br_int() in
- ovn/controller/ovn-controller.c. -->
- <dl>
- <dt><code>fail-mode=secure</code></dt>
- <dd>
- Avoids switching packets between isolated logical networks before
- <code>ovn-controller</code> starts up. See <code>Controller Failure
- Settings</code> in <code>ovs-vsctl</code>(8) for more information.
- </dd>
-
- <dt><code>other-config:disable-in-band=true</code></dt>
- <dd>
- Suppresses in-band control flows for the integration bridge. It would be
- unusual for such flows to show up anyway, because OVN uses a local
- controller (over a Unix domain socket) instead of a remote controller.
- It's possible, however, for some other bridge in the same system to have
- an in-band remote controller, and in that case this suppresses the flows
- that in-band control would ordinarily set up. Refer to the documentation
- for more information.
- </dd>
- </dl>
-
- <p>
- The customary name for the integration bridge is <code>br-int</code>, but
- another name may be used.
- </p>
-
- <h2>Logical Networks</h2>
-
- <p>
- A <dfn>logical network</dfn> implements the same concepts as physical
- networks, but they are insulated from the physical network with tunnels or
- other encapsulations. This allows logical networks to have separate IP and
- other address spaces that overlap, without conflicting, with those used for
- physical networks. Logical network topologies can be arranged without
- regard for the topologies of the physical networks on which they run.
- </p>
-
- <p>
- Logical network concepts in OVN include:
- </p>
-
- <ul>
- <li>
- <dfn>Logical switches</dfn>, the logical version of Ethernet switches.
- </li>
-
- <li>
- <dfn>Logical routers</dfn>, the logical version of IP routers. Logical
- switches and routers can be connected into sophisticated topologies.
- </li>
-
- <li>
- <dfn>Logical datapaths</dfn> are the logical version of an OpenFlow
- switch. Logical switches and routers are both implemented as logical
- datapaths.
- </li>
-
- <li>
- <p>
- <dfn>Logical ports</dfn> represent the points of connectivity in and
- out of logical switches and logical routers. Some common types of
- logical ports are:
- </p>
-
- <ul>
- <li>
- Logical ports representing VIFs.
- </li>
-
- <li>
- <dfn>Localnet ports</dfn> represent the points of connectivity
- between logical switches and the physical network. They are
- implemented as OVS patch ports between the integration bridge
- and the separate Open vSwitch bridge that underlay physical
- ports attach to.
- </li>
-
- <li>
- <dfn>Logical patch ports</dfn> represent the points of
- connectivity between logical switches and logical routers, and
- in some cases between peer logical routers. There is a pair of
- logical patch ports at each such point of connectivity, one on
- each side.
- </li>
- <li>
- <dfn>Localport ports</dfn> represent the points of local
- connectivity between logical switches and VIFs. These ports are
- present in every chassis (not bound to any particular one) and
- traffic from them will never go through a tunnel. A
- <code>localport</code> is expected to only generate traffic destined
- for a local destination, typically in response to a request it
- received.
- One use case is how OpenStack Neutron uses a <code>localport</code>
- port for serving metadata to VM's residing on every hypervisor. A
- metadata proxy process is attached to this port on every host and all
- VM's within the same network will reach it at the same IP/MAC address
- without any traffic being sent over a tunnel. Further details can be
- seen at https://docs.openstack.org/developer/networking-ovn/design/metadata_api.html.
- </li>
- </ul>
- </li>
- </ul>
-
- <h2>Life Cycle of a VIF</h2>
-
- <p>
- Tables and their schemas presented in isolation are difficult to
- understand. Here's an example.
- </p>
-
- <p>
- A VIF on a hypervisor is a virtual network interface attached either
- to a VM or a container running directly on that hypervisor (This is
- different from the interface of a container running inside a VM).
- </p>
-
- <p>
- The steps in this example refer often to details of the OVN and OVN
- Northbound database schemas. Please see <code>ovn-sb</code>(5) and
- <code>ovn-nb</code>(5), respectively, for the full story on these
- databases.
- </p>
-
- <ol>
- <li>
- A VIF's life cycle begins when a CMS administrator creates a new VIF
- using the CMS user interface or API and adds it to a switch (one
- implemented by OVN as a logical switch). The CMS updates its own
- configuration. This includes associating unique, persistent identifier
- <var>vif-id</var> and Ethernet address <var>mac</var> with the VIF.
- </li>
-
- <li>
- The CMS plugin updates the OVN Northbound database to include the new
- VIF, by adding a row to the <code>Logical_Switch_Port</code>
- table. In the new row, <code>name</code> is <var>vif-id</var>,
- <code>mac</code> is <var>mac</var>, <code>switch</code> points to
- the OVN logical switch's Logical_Switch record, and other columns
- are initialized appropriately.
- </li>
-
- <li>
- <code>ovn-northd</code> receives the OVN Northbound database update. In
- turn, it makes the corresponding updates to the OVN Southbound database,
- by adding rows to the OVN Southbound database <code>Logical_Flow</code>
- table to reflect the new port, e.g. add a flow to recognize that packets
- destined to the new port's MAC address should be delivered to it, and
- update the flow that delivers broadcast and multicast packets to include
- the new port. It also creates a record in the <code>Binding</code> table
- and populates all its columns except the column that identifies the
- <code>chassis</code>.
- </li>
-
- <li>
- On every hypervisor, <code>ovn-controller</code> receives the
- <code>Logical_Flow</code> table updates that <code>ovn-northd</code> made
- in the previous step. As long as the VM that owns the VIF is powered
- off, <code>ovn-controller</code> cannot do much; it cannot, for example,
- arrange to send packets to or receive packets from the VIF, because the
- VIF does not actually exist anywhere.
- </li>
-
- <li>
- Eventually, a user powers on the VM that owns the VIF. On the hypervisor
- where the VM is powered on, the integration between the hypervisor and
- Open vSwitch (described in <code>IntegrationGuide.rst</code>) adds the VIF
- to the OVN integration bridge and stores <var>vif-id</var> in
- <code>external_ids</code>:<code>iface-id</code> to indicate that the
- interface is an instantiation of the new VIF. (None of this code is new
- in OVN; this is pre-existing integration work that has already been done
- on hypervisors that support OVS.)
- </li>
-
- <li>
- On the hypervisor where the VM is powered on, <code>ovn-controller</code>
- notices <code>external_ids</code>:<code>iface-id</code> in the new
- Interface. In response, in the OVN Southbound DB, it updates the
- <code>Binding</code> table's <code>chassis</code> column for the
- row that links the logical port from <code>external_ids</code>:<code>
- iface-id</code> to the hypervisor. Afterward, <code>ovn-controller</code>
- updates the local hypervisor's OpenFlow tables so that packets to and from
- the VIF are properly handled.
- </li>
-
- <li>
- Some CMS systems, including OpenStack, fully start a VM only when its
- networking is ready. To support this, <code>ovn-northd</code> notices
- the <code>chassis</code> column updated for the row in
- <code>Binding</code> table and pushes this upward by updating the
- <ref column="up" table="Logical_Switch_Port" db="OVN_NB"/> column
- in the OVN Northbound database's <ref table="Logical_Switch_Port"
- db="OVN_NB"/> table to indicate that the VIF is now up. The CMS,
- if it uses this feature, can then react by allowing the VM's
- execution to proceed.
- </li>
-
- <li>
- On every hypervisor but the one where the VIF resides,
- <code>ovn-controller</code> notices the completely populated row in the
- <code>Binding</code> table. This provides <code>ovn-controller</code>
- the physical location of the logical port, so each instance updates the
- OpenFlow tables of its switch (based on logical datapath flows in the OVN
- DB <code>Logical_Flow</code> table) so that packets to and from the VIF
- can be properly handled via tunnels.
- </li>
-
- <li>
- Eventually, a user powers off the VM that owns the VIF. On the
- hypervisor where the VM was powered off, the VIF is deleted from the OVN
- integration bridge.
- </li>
-
- <li>
- On the hypervisor where the VM was powered off,
- <code>ovn-controller</code> notices that the VIF was deleted. In
- response, it removes the <code>Chassis</code> column content in the
- <code>Binding</code> table for the logical port.
- </li>
-
- <li>
- On every hypervisor, <code>ovn-controller</code> notices the empty
- <code>Chassis</code> column in the <code>Binding</code> table's row
- for the logical port. This means that <code>ovn-controller</code> no
- longer knows the physical location of the logical port, so each instance
- updates its OpenFlow table to reflect that.
- </li>
-
- <li>
- Eventually, when the VIF (or its entire VM) is no longer needed by
- anyone, an administrator deletes the VIF using the CMS user interface or
- API. The CMS updates its own configuration.
- </li>
-
- <li>
- The CMS plugin removes the VIF from the OVN Northbound database,
- by deleting its row in the <code>Logical_Switch_Port</code> table.
- </li>
-
- <li>
- <code>ovn-northd</code> receives the OVN Northbound update and in turn
- updates the OVN Southbound database accordingly, by removing or updating
- the rows from the OVN Southbound database <code>Logical_Flow</code> table
- and <code>Binding</code> table that were related to the now-destroyed
- VIF.
- </li>
-
- <li>
- On every hypervisor, <code>ovn-controller</code> receives the
- <code>Logical_Flow</code> table updates that <code>ovn-northd</code> made
- in the previous step. <code>ovn-controller</code> updates OpenFlow
- tables to reflect the update, although there may not be much to do, since
- the VIF had already become unreachable when it was removed from the
- <code>Binding</code> table in a previous step.
- </li>
- </ol>
-
- <h2>Life Cycle of a Container Interface Inside a VM</h2>
-
- <p>
- OVN provides virtual network abstractions by converting information
- written in OVN_NB database to OpenFlow flows in each hypervisor. Secure
- virtual networking for multi-tenants can only be provided if OVN controller
- is the only entity that can modify flows in Open vSwitch. When the
- Open vSwitch integration bridge resides in the hypervisor, it is a
- fair assumption to make that tenant workloads running inside VMs cannot
- make any changes to Open vSwitch flows.
- </p>
-
- <p>
- If the infrastructure provider trusts the applications inside the
- containers not to break out and modify the Open vSwitch flows, then
- containers can be run in hypervisors. This is also the case when
- containers are run inside the VMs and Open vSwitch integration bridge
- with flows added by OVN controller resides in the same VM. For both
- the above cases, the workflow is the same as explained with an example
- in the previous section ("Life Cycle of a VIF").
- </p>
-
- <p>
- This section talks about the life cycle of a container interface (CIF)
- when containers are created in the VMs and the Open vSwitch integration
- bridge resides inside the hypervisor. In this case, even if a container
- application breaks out, other tenants are not affected because the
- containers running inside the VMs cannot modify the flows in the
- Open vSwitch integration bridge.
- </p>
-
- <p>
- When multiple containers are created inside a VM, there are multiple
- CIFs associated with them. The network traffic associated with these
- CIFs need to reach the Open vSwitch integration bridge running in the
- hypervisor for OVN to support virtual network abstractions. OVN should
- also be able to distinguish network traffic coming from different CIFs.
- There are two ways to distinguish network traffic of CIFs.
- </p>
-
- <p>
- One way is to provide one VIF for every CIF (1:1 model). This means that
- there could be a lot of network devices in the hypervisor. This would slow
- down OVS because of all the additional CPU cycles needed for the management
- of all the VIFs. It would also mean that the entity creating the
- containers in a VM should also be able to create the corresponding VIFs in
- the hypervisor.
- </p>
-
- <p>
- The second way is to provide a single VIF for all the CIFs (1:many model).
- OVN could then distinguish network traffic coming from different CIFs via
- a tag written in every packet. OVN uses this mechanism and uses VLAN as
- the tagging mechanism.
- </p>
-
- <ol>
- <li>
- A CIF's life cycle begins when a container is spawned inside a VM by
- the either the same CMS that created the VM or a tenant that owns that VM
- or even a container Orchestration System that is different than the CMS
- that initially created the VM. Whoever the entity is, it will need to
- know the <var>vif-id</var> that is associated with the network interface
- of the VM through which the container interface's network traffic is
- expected to go through. The entity that creates the container interface
- will also need to choose an unused VLAN inside that VM.
- </li>
-
- <li>
- The container spawning entity (either directly or through the CMS that
- manages the underlying infrastructure) updates the OVN Northbound
- database to include the new CIF, by adding a row to the
- <code>Logical_Switch_Port</code> table. In the new row,
- <code>name</code> is any unique identifier,
- <code>parent_name</code> is the <var>vif-id</var> of the VM
- through which the CIF's network traffic is expected to go through
- and the <code>tag</code> is the VLAN tag that identifies the
- network traffic of that CIF.
- </li>
-
- <li>
- <code>ovn-northd</code> receives the OVN Northbound database update. In
- turn, it makes the corresponding updates to the OVN Southbound database,
- by adding rows to the OVN Southbound database's <code>Logical_Flow</code>
- table to reflect the new port and also by creating a new row in the
- <code>Binding</code> table and populating all its columns except the
- column that identifies the <code>chassis</code>.
- </li>
-
- <li>
- On every hypervisor, <code>ovn-controller</code> subscribes to the
- changes in the <code>Binding</code> table. When a new row is created
- by <code>ovn-northd</code> that includes a value in
- <code>parent_port</code> column of <code>Binding</code> table, the
- <code>ovn-controller</code> in the hypervisor whose OVN integration bridge
- has that same value in <var>vif-id</var> in
- <code>external_ids</code>:<code>iface-id</code>
- updates the local hypervisor's OpenFlow tables so that packets to and
- from the VIF with the particular VLAN <code>tag</code> are properly
- handled. Afterward it updates the <code>chassis</code> column of
- the <code>Binding</code> to reflect the physical location.
- </li>
-
- <li>
- One can only start the application inside the container after the
- underlying network is ready. To support this, <code>ovn-northd</code>
- notices the updated <code>chassis</code> column in <code>Binding</code>
- table and updates the <ref column="up" table="Logical_Switch_Port"
- db="OVN_NB"/> column in the OVN Northbound database's
- <ref table="Logical_Switch_Port" db="OVN_NB"/> table to indicate that the
- CIF is now up. The entity responsible to start the container application
- queries this value and starts the application.
- </li>
-
- <li>
- Eventually the entity that created and started the container, stops it.
- The entity, through the CMS (or directly) deletes its row in the
- <code>Logical_Switch_Port</code> table.
- </li>
-
- <li>
- <code>ovn-northd</code> receives the OVN Northbound update and in turn
- updates the OVN Southbound database accordingly, by removing or updating
- the rows from the OVN Southbound database <code>Logical_Flow</code> table
- that were related to the now-destroyed CIF. It also deletes the row in
- the <code>Binding</code> table for that CIF.
- </li>
-
- <li>
- On every hypervisor, <code>ovn-controller</code> receives the
- <code>Logical_Flow</code> table updates that <code>ovn-northd</code> made
- in the previous step. <code>ovn-controller</code> updates OpenFlow
- tables to reflect the update.
- </li>
- </ol>
-
- <h2>Architectural Physical Life Cycle of a Packet</h2>
-
- <p>
- This section describes how a packet travels from one virtual machine or
- container to another through OVN. This description focuses on the physical
- treatment of a packet; for a description of the logical life cycle of a
- packet, please refer to the <code>Logical_Flow</code> table in
- <code>ovn-sb</code>(5).
- </p>
-
- <p>
- This section mentions several data and metadata fields, for clarity
- summarized here:
- </p>
-
- <dl>
- <dt>tunnel key</dt>
- <dd>
- When OVN encapsulates a packet in Geneve or another tunnel, it attaches
- extra data to it to allow the receiving OVN instance to process it
- correctly. This takes different forms depending on the particular
- encapsulation, but in each case we refer to it here as the ``tunnel
- key.'' See <code>Tunnel Encapsulations</code>, below, for details.
- </dd>
-
- <dt>logical datapath field</dt>
- <dd>
- A field that denotes the logical datapath through which a packet is being
- processed.
- <!-- Keep the following in sync with MFF_LOG_DATAPATH in
- ovn/lib/logical-fields.h. -->
- OVN uses the field that OpenFlow 1.1+ simply (and confusingly) calls
- ``metadata'' to store the logical datapath. (This field is passed across
- tunnels as part of the tunnel key.)
- </dd>
-
- <dt>logical input port field</dt>
- <dd>
- <p>
- A field that denotes the logical port from which the packet
- entered the logical datapath.
- <!-- Keep the following in sync with MFF_LOG_INPORT in
- ovn/lib/logical-fields.h. -->
- OVN stores this in Open vSwitch extension register number 14.
- </p>
-
- <p>
- Geneve and STT tunnels pass this field as part of the tunnel key.
- Although VXLAN tunnels do not explicitly carry a logical input port,
- OVN only uses VXLAN to communicate with gateways that from OVN's
- perspective consist of only a single logical port, so that OVN can set
- the logical input port field to this one on ingress to the OVN logical
- pipeline.
- </p>
- </dd>
-
- <dt>logical output port field</dt>
- <dd>
- <p>
- A field that denotes the logical port from which the packet will
- leave the logical datapath. This is initialized to 0 at the
- beginning of the logical ingress pipeline.
- <!-- Keep the following in sync with MFF_LOG_OUTPORT in
- ovn/lib/logical-fields.h. -->
- OVN stores this in Open vSwitch extension register number 15.
- </p>
-
- <p>
- Geneve and STT tunnels pass this field as part of the tunnel key.
- VXLAN tunnels do not transmit the logical output port field.
- Since VXLAN tunnels do not carry a logical output port field in
- the tunnel key, when a packet is received from VXLAN tunnel by
- an OVN hypervisor, the packet is resubmitted to table 8 to
- determine the output port(s); when the packet reaches table 32,
- these packets are resubmitted to table 33 for local delivery by
- checking a MLF_RCV_FROM_VXLAN flag, which is set when the packet
- arrives from a VXLAN tunnel.
- </p>
- </dd>
-
- <dt>conntrack zone field for logical ports</dt>
- <dd>
- A field that denotes the connection tracking zone for logical ports.
- The value only has local significance and is not meaningful between
- chassis. This is initialized to 0 at the beginning of the logical
- <!-- Keep the following in sync with MFF_LOG_CT_ZONE in
- ovn/lib/logical-fields.h. -->
- ingress pipeline. OVN stores this in Open vSwitch extension register
- number 13.
- </dd>
-
- <dt>conntrack zone fields for routers</dt>
- <dd>
- Fields that denote the connection tracking zones for routers. These
- values only have local significance and are not meaningful between
- chassis. OVN stores the zone information for DNATting in Open vSwitch
- <!-- Keep the following in sync with MFF_LOG_DNAT_ZONE and
- MFF_LOG_SNAT_ZONE in ovn/lib/logical-fields.h. -->
- extension register number 11 and zone information for SNATing in
- Open vSwitch extension register number 12.
- </dd>
-
- <dt>logical flow flags</dt>
- <dd>
- The logical flags are intended to handle keeping context between
- tables in order to decide which rules in subsequent tables are
- matched. These values only have local significance and are not
- meaningful between chassis. OVN stores the logical flags in
- <!-- Keep the following in sync with MFF_LOG_FLAGS in
- ovn/lib/logical-fields.h. -->
- Open vSwitch extension register number 10.
- </dd>
-
- <dt>VLAN ID</dt>
- <dd>
- The VLAN ID is used as an interface between OVN and containers nested
- inside a VM (see <code>Life Cycle of a container interface inside a
- VM</code>, above, for more information).
- </dd>
- </dl>
-
- <p>
- Initially, a VM or container on the ingress hypervisor sends a packet on a
- port attached to the OVN integration bridge. Then:
- </p>
-
- <ol>
- <li>
- <p>
- OpenFlow table 0 performs physical-to-logical translation. It matches
- the packet's ingress port. Its actions annotate the packet with
- logical metadata, by setting the logical datapath field to identify the
- logical datapath that the packet is traversing and the logical input
- port field to identify the ingress port. Then it resubmits to table 8
- to enter the logical ingress pipeline.
- </p>
-
- <p>
- Packets that originate from a container nested within a VM are treated
- in a slightly different way. The originating container can be
- distinguished based on the VIF-specific VLAN ID, so the
- physical-to-logical translation flows additionally match on VLAN ID and
- the actions strip the VLAN header. Following this step, OVN treats
- packets from containers just like any other packets.
- </p>
-
- <p>
- Table 0 also processes packets that arrive from other chassis. It
- distinguishes them from other packets by ingress port, which is a
- tunnel. As with packets just entering the OVN pipeline, the actions
- annotate these packets with logical datapath and logical ingress port
- metadata. In addition, the actions set the logical output port field,
- which is available because in OVN tunneling occurs after the logical
- output port is known. These three pieces of information are obtained
- from the tunnel encapsulation metadata (see <code>Tunnel
- Encapsulations</code> for encoding details). Then the actions resubmit
- to table 33 to enter the logical egress pipeline.
- </p>
- </li>
-
- <li>
- <p>
- OpenFlow tables 8 through 31 execute the logical ingress pipeline from
- the <code>Logical_Flow</code> table in the OVN Southbound database.
- These tables are expressed entirely in terms of logical concepts like
- logical ports and logical datapaths. A big part of
- <code>ovn-controller</code>'s job is to translate them into equivalent
- OpenFlow (in particular it translates the table numbers:
- <code>Logical_Flow</code> tables 0 through 23 become OpenFlow tables 8
- through 31).
- </p>
-
- <p>
- Each logical flow maps to one or more OpenFlow flows. An actual packet
- ordinarily matches only one of these, although in some cases it can
- match more than one of these flows (which is not a problem because all
- of them have the same actions). <code>ovn-controller</code> uses the
- first 32 bits of the logical flow's UUID as the cookie for its OpenFlow
- flow or flows. (This is not necessarily unique, since the first 32
- bits of a logical flow's UUID is not necessarily unique.)
- </p>
-
- <p>
- Some logical flows can map to the Open vSwitch ``conjunctive match''
- extension (see <code>ovs-fields</code>(7)). Flows with a
- <code>conjunction</code> action use an OpenFlow cookie of 0, because
- they can correspond to multiple logical flows. The OpenFlow flow for a
- conjunctive match includes a match on <code>conj_id</code>.
- </p>
-
- <p>
- Some logical flows may not be represented in the OpenFlow tables on a
- given hypervisor, if they could not be used on that hypervisor. For
- example, if no VIF in a logical switch resides on a given hypervisor,
- and the logical switch is not otherwise reachable on that hypervisor
- (e.g. over a series of hops through logical switches and routers
- starting from a VIF on the hypervisor), then the logical flow may not
- be represented there.
- </p>
-
- <p>
- Most OVN actions have fairly obvious implementations in OpenFlow (with
- OVS extensions), e.g. <code>next;</code> is implemented as
- <code>resubmit</code>, <code><var>field</var> =
- <var>constant</var>;</code> as <code>set_field</code>. A few are worth
- describing in more detail:
- </p>
-
- <dl>
- <dt><code>output:</code></dt>
- <dd>
- Implemented by resubmitting the packet to table 32. If the pipeline
- executes more than one <code>output</code> action, then each one is
- separately resubmitted to table 32. This can be used to send
- multiple copies of the packet to multiple ports. (If the packet was
- not modified between the <code>output</code> actions, and some of the
- copies are destined to the same hypervisor, then using a logical
- multicast output port would save bandwidth between hypervisors.)
- </dd>
-
- <dt><code>get_arp(<var>P</var>, <var>A</var>);</code></dt>
- <dt><code>get_nd(<var>P</var>, <var>A</var>);</code></dt>
- <dd>
- <p>
- Implemented by storing arguments into OpenFlow fields, then
- resubmitting to table 66, which <code>ovn-controller</code>
- populates with flows generated from the <code>MAC_Binding</code>
- table in the OVN Southbound database. If there is a match in table
- 66, then its actions store the bound MAC in the Ethernet
- destination address field.
- </p>
-
- <p>
- (The OpenFlow actions save and restore the OpenFlow fields used for
- the arguments, so that the OVN actions do not have to be aware of
- this temporary use.)
- </p>
- </dd>
-
- <dt><code>put_arp(<var>P</var>, <var>A</var>, <var>E</var>);</code></dt>
- <dt><code>put_nd(<var>P</var>, <var>A</var>, <var>E</var>);</code></dt>
- <dd>
- <p>
- Implemented by storing the arguments into OpenFlow fields, then
- outputting a packet to <code>ovn-controller</code>, which updates
- the <code>MAC_Binding</code> table.
- </p>
-
- <p>
- (The OpenFlow actions save and restore the OpenFlow fields used for
- the arguments, so that the OVN actions do not have to be aware of
- this temporary use.)
- </p>
- </dd>
- </dl>
- </li>
-
- <li>
- <p>
- OpenFlow tables 32 through 47 implement the <code>output</code> action
- in the logical ingress pipeline. Specifically, table 32 handles
- packets to remote hypervisors, table 33 handles packets to the local
- hypervisor, and table 34 checks whether packets whose logical ingress
- and egress port are the same should be discarded.
- </p>
-
- <p>
- Logical patch ports are a special case. Logical patch ports do not
- have a physical location and effectively reside on every hypervisor.
- Thus, flow table 33, for output to ports on the local hypervisor,
- naturally implements output to unicast logical patch ports too.
- However, applying the same logic to a logical patch port that is part
- of a logical multicast group yields packet duplication, because each
- hypervisor that contains a logical port in the multicast group will
- also output the packet to the logical patch port. Thus, multicast
- groups implement output to logical patch ports in table 32.
- </p>
-
- <p>
- Each flow in table 32 matches on a logical output port for unicast or
- multicast logical ports that include a logical port on a remote
- hypervisor. Each flow's actions implement sending a packet to the port
- it matches. For unicast logical output ports on remote hypervisors,
- the actions set the tunnel key to the correct value, then send the
- packet on the tunnel port to the correct hypervisor. (When the remote
- hypervisor receives the packet, table 0 there will recognize it as a
- tunneled packet and pass it along to table 33.) For multicast logical
- output ports, the actions send one copy of the packet to each remote
- hypervisor, in the same way as for unicast destinations. If a
- multicast group includes a logical port or ports on the local
- hypervisor, then its actions also resubmit to table 33. Table 32 also
- includes:
- </p>
-
- <ul>
- <li>
- A higher-priority rule to match packets received from VXLAN tunnels,
- based on flag MLF_RCV_FROM_VXLAN, and resubmit these packets to table
- 33 for local delivery. Packets received from VXLAN tunnels reach
- here because of a lack of logical output port field in the tunnel key
- and thus these packets needed to be submitted to table 8 to
- determine the output port.
- </li>
- <li>
- A higher-priority rule to match packets received from ports of type
- <code>localport</code>, based on the logical input port, and resubmit
- these packets to table 33 for local delivery. Ports of type
- <code>localport</code> exist on every hypervisor and by definition
- their traffic should never go out through a tunnel.
- </li>
- <li>
- A higher-priority rule to match packets that have the MLF_LOCAL_ONLY
- logical flow flag set, and whose destination is a multicast address.
- This flag indicates that the packet should not be delivered to remote
- hypervisors, even if the multicast destination includes ports on
- remote hypervisors. This flag is used when
- <code>ovn-controller</code> is the originator of the multicast packet.
- Since each <code>ovn-controller</code> instance is originating these
- packets, the packets only need to be delivered to local ports.
- </li>
- <li>
- A fallback flow that resubmits to table 33 if there is no other
- match.
- </li>
- </ul>
-
- <p>
- Flows in table 33 resemble those in table 32 but for logical ports that
- reside locally rather than remotely. For unicast logical output ports
- on the local hypervisor, the actions just resubmit to table 34. For
- multicast output ports that include one or more logical ports on the
- local hypervisor, for each such logical port <var>P</var>, the actions
- change the logical output port to <var>P</var>, then resubmit to table
- 34.
- </p>
-
- <p>
- A special case is that when a localnet port exists on the datapath,
- remote port is connected by switching to the localnet port. In this
- case, instead of adding a flow in table 32 to reach the remote port, a
- flow is added in table 33 to switch the logical outport to the localnet
- port, and resubmit to table 33 as if it were unicasted to a logical
- port on the local hypervisor.
- </p>
-
- <p>
- Table 34 matches and drops packets for which the logical input and
- output ports are the same and the MLF_ALLOW_LOOPBACK flag is not
- set. It resubmits other packets to table 40.
- </p>
- </li>
-
- <li>
- <p>
- OpenFlow tables 40 through 63 execute the logical egress pipeline from
- the <code>Logical_Flow</code> table in the OVN Southbound database.
- The egress pipeline can perform a final stage of validation before
- packet delivery. Eventually, it may execute an <code>output</code>
- action, which <code>ovn-controller</code> implements by resubmitting to
- table 64. A packet for which the pipeline never executes
- <code>output</code> is effectively dropped (although it may have been
- transmitted through a tunnel across a physical network).
- </p>
-
- <p>
- The egress pipeline cannot change the logical output port or cause
- further tunneling.
- </p>
- </li>
-
- <li>
- <p>
- Table 64 bypasses OpenFlow loopback when MLF_ALLOW_LOOPBACK is set.
- Logical loopback was handled in table 34, but OpenFlow by default also
- prevents loopback to the OpenFlow ingress port. Thus, when
- MLF_ALLOW_LOOPBACK is set, OpenFlow table 64 saves the OpenFlow ingress
- port, sets it to zero, resubmits to table 65 for logical-to-physical
- transformation, and then restores the OpenFlow ingress port,
- effectively disabling OpenFlow loopback prevents. When
- MLF_ALLOW_LOOPBACK is unset, table 64 flow simply resubmits to table
- 65.
- </p>
- </li>
-
- <li>
- <p>
- OpenFlow table 65 performs logical-to-physical translation, the
- opposite of table 0. It matches the packet's logical egress port. Its
- actions output the packet to the port attached to the OVN integration
- bridge that represents that logical port. If the logical egress port
- is a container nested with a VM, then before sending the packet the
- actions push on a VLAN header with an appropriate VLAN ID.
- </p>
- </li>
- </ol>
-
- <h2>Logical Routers and Logical Patch Ports</h2>
-
- <p>
- Typically logical routers and logical patch ports do not have a
- physical location and effectively reside on every hypervisor. This is
- the case for logical patch ports between logical routers and logical
- switches behind those logical routers, to which VMs (and VIFs) attach.
- </p>
-
- <p>
- Consider a packet sent from one virtual machine or container to another
- VM or container that resides on a different subnet. The packet will
- traverse tables 0 to 65 as described in the previous section
- <code>Architectural Physical Life Cycle of a Packet</code>, using the
- logical datapath representing the logical switch that the sender is
- attached to. At table 32, the packet will use the fallback flow that
- resubmits locally to table 33 on the same hypervisor. In this case,
- all of the processing from table 0 to table 65 occurs on the hypervisor
- where the sender resides.
- </p>
-
- <p>
- When the packet reaches table 65, the logical egress port is a logical
- patch port. The implementation in table 65 differs depending on the OVS
- version, although the observed behavior is meant to be the same:
- </p>
-
- <ul>
- <li>
- In OVS versions 2.6 and earlier, table 65 outputs to an OVS patch
- port that represents the logical patch port. The packet re-enters
- the OpenFlow flow table from the OVS patch port's peer in table 0,
- which identifies the logical datapath and logical input port based
- on the OVS patch port's OpenFlow port number.
- </li>
-
- <li>
- In OVS versions 2.7 and later, the packet is cloned and resubmitted
- directly to the first OpenFlow flow table in the ingress pipeline,
- setting the logical ingress port to the peer logical patch port, and
- using the peer logical patch port's logical datapath (that
- represents the logical router).
- </li>
- </ul>
-
- <p>
- The packet re-enters the ingress pipeline in order to traverse tables
- 8 to 65 again, this time using the logical datapath representing the
- logical router. The processing continues as described in the previous
- section <code>Architectural Physical Life Cycle of a Packet</code>.
- When the packet reachs table 65, the logical egress port will once
- again be a logical patch port. In the same manner as described above,
- this logical patch port will cause the packet to be resubmitted to
- OpenFlow tables 8 to 65, this time using the logical datapath
- representing the logical switch that the destination VM or container
- is attached to.
- </p>
-
- <p>
- The packet traverses tables 8 to 65 a third and final time. If the
- destination VM or container resides on a remote hypervisor, then table
- 32 will send the packet on a tunnel port from the sender's hypervisor
- to the remote hypervisor. Finally table 65 will output the packet
- directly to the destination VM or container.
- </p>
-
- <p>
- The following sections describe two exceptions, where logical routers
- and/or logical patch ports are associated with a physical location.
- </p>
-
- <h3>Gateway Routers</h3>
-
- <p>
- A <dfn>gateway router</dfn> is a logical router that is bound to a
- physical location. This includes all of the logical patch ports of
- the logical router, as well as all of the peer logical patch ports on
- logical switches. In the OVN Southbound database, the
- <code>Port_Binding</code> entries for these logical patch ports use
- the type <code>l3gateway</code> rather than <code>patch</code>, in
- order to distinguish that these logical patch ports are bound to a
- chassis.
- </p>
-
- <p>
- When a hypervisor processes a packet on a logical datapath
- representing a logical switch, and the logical egress port is a
- <code>l3gateway</code> port representing connectivity to a gateway
- router, the packet will match a flow in table 32 that sends the
- packet on a tunnel port to the chassis where the gateway router
- resides. This processing in table 32 is done in the same manner as
- for VIFs.
- </p>
-
- <p>
- Gateway routers are typically used in between distributed logical
- routers and physical networks. The distributed logical router and
- the logical switches behind it, to which VMs and containers attach,
- effectively reside on each hypervisor. The distributed router and
- the gateway router are connected by another logical switch, sometimes
- referred to as a <code>join</code> logical switch. On the other
- side, the gateway router connects to another logical switch that has
- a localnet port connecting to the physical network.
- </p>
-
- <p>
- When using gateway routers, DNAT and SNAT rules are associated with
- the gateway router, which provides a central location that can handle
- one-to-many SNAT (aka IP masquerading).
- </p>
-
- <h3>Distributed Gateway Ports</h3>
-
- <p>
- <dfn>Distributed gateway ports</dfn> are logical router patch ports
- that directly connect distributed logical routers to logical
- switches with localnet ports.
- </p>
-
- <p>
- The primary design goal of distributed gateway ports is to allow as
- much traffic as possible to be handled locally on the hypervisor
- where a VM or container resides. Whenever possible, packets from
- the VM or container to the outside world should be processed
- completely on that VM's or container's hypervisor, eventually
- traversing a localnet port instance on that hypervisor to the
- physical network. Whenever possible, packets from the outside
- world to a VM or container should be directed through the physical
- network directly to the VM's or container's hypervisor, where the
- packet will enter the integration bridge through a localnet port.
- </p>
-
- <p>
- In order to allow for the distributed processing of packets
- described in the paragraph above, distributed gateway ports need to
- be logical patch ports that effectively reside on every hypervisor,
- rather than <code>l3gateway</code> ports that are bound to a
- particular chassis. However, the flows associated with distributed
- gateway ports often need to be associated with physical locations,
- for the following reasons:
- </p>
-
- <ul>
- <li>
- <p>
- The physical network that the localnet port is attached to
- typically uses L2 learning. Any Ethernet address used over the
- distributed gateway port must be restricted to a single physical
- location so that upstream L2 learning is not confused. Traffic
- sent out the distributed gateway port towards the localnet port
- with a specific Ethernet address must be sent out one specific
- instance of the distributed gateway port on one specific
- chassis. Traffic received from the localnet port (or from a VIF
- on the same logical switch as the localnet port) with a specific
- Ethernet address must be directed to the logical switch's patch
- port instance on that specific chassis.
- </p>
-
- <p>
- Due to the implications of L2 learning, the Ethernet address and
- IP address of the distributed gateway port need to be restricted
- to a single physical location. For this reason, the user must
- specify one chassis associated with the distributed gateway
- port. Note that traffic traversing the distributed gateway port
- using other Ethernet addresses and IP addresses (e.g. one-to-one
- NAT) is not restricted to this chassis.
- </p>
-
- <p>
- Replies to ARP and ND requests must be restricted to a single
- physical location, where the Ethernet address in the reply
- resides. This includes ARP and ND replies for the IP address
- of the distributed gateway port, which are restricted to the
- chassis that the user associated with the distributed gateway
- port.
- </p>
- </li>
-
- <li>
- In order to support one-to-many SNAT (aka IP masquerading), where
- multiple logical IP addresses spread across multiple chassis are
- mapped to a single external IP address, it will be necessary to
- handle some of the logical router processing on a specific chassis
- in a centralized manner. Since the SNAT external IP address is
- typically the distributed gateway port IP address, and for
- simplicity, the same chassis associated with the distributed
- gateway port is used.
- </li>
- </ul>
-
- <p>
- The details of flow restrictions to specific chassis are described
- in the <code>ovn-northd</code> documentation.
- </p>
-
- <p>
- While most of the physical location dependent aspects of distributed
- gateway ports can be handled by restricting some flows to specific
- chassis, one additional mechanism is required. When a packet
- leaves the ingress pipeline and the logical egress port is the
- distributed gateway port, one of two different sets of actions is
- required at table 32:
- </p>
-
- <ul>
- <li>
- If the packet can be handled locally on the sender's hypervisor
- (e.g. one-to-one NAT traffic), then the packet should just be
- resubmitted locally to table 33, in the normal manner for
- distributed logical patch ports.
- </li>
-
- <li>
- However, if the packet needs to be handled on the chassis
- associated with the distributed gateway port (e.g. one-to-many
- SNAT traffic or non-NAT traffic), then table 32 must send the
- packet on a tunnel port to that chassis.
- </li>
- </ul>
-
- <p>
- In order to trigger the second set of actions, the
- <code>chassisredirect</code> type of southbound
- <code>Port_Binding</code> has been added. Setting the logical
- egress port to the type <code>chassisredirect</code> logical port is
- simply a way to indicate that although the packet is destined for
- the distributed gateway port, it needs to be redirected to a
- different chassis. At table 32, packets with this logical egress
- port are sent to a specific chassis, in the same way that table 32
- directs packets whose logical egress port is a VIF or a type
- <code>l3gateway</code> port to different chassis. Once the packet
- arrives at that chassis, table 33 resets the logical egress port to
- the value representing the distributed gateway port. For each
- distributed gateway port, there is one type
- <code>chassisredirect</code> port, in addition to the distributed
- logical patch port representing the distributed gateway port.
- </p>
-
- <h3>High Availability for Distributed Gateway Ports</h3>
-
- <p>
- OVN allows you to specify a prioritized list of chassis for a distributed
- gateway port. This is done by associating multiple
- <code>Gateway_Chassis</code> rows with a <code>Logical_Router_Port</code>
- in the <code>OVN_Northbound</code> database.
- </p>
-
- <p>
- When multiple chassis have been specified for a gateway, all chassis that
- may send packets to that gateway will enable BFD on tunnels to all
- configured gateway chassis. The current master chassis for the gateway
- is the highest priority gateway chassis that is currently viewed as
- active based on BFD status.
- </p>
-
- <p>
- For more information on L3 gateway high availability, please refer to
- http://docs.openvswitch.org/en/latest/topics/high-availability.
- </p>
-
- <h2>Multiple localnet logical switches connected to a Logical Router</h2>
-
- <p>
- It is possible to have multiple logical switches each with a localnet port
- (representing physical networks) connected to a logical router, in which
- one localnet logical switch may provide the external connectivity via a
- distributed gateway port and rest of the localnet logical switches use
- VLAN tagging in the physical network. It is expected that
- <code>ovn-bridge-mappings</code> is configured appropriately on the
- chassis for all these localnet networks.
- </p>
-
- <h3>East West routing</h3>
- <p>
- East-West routing between these localnet VLAN tagged logical switches
- work almost the same way as normal logical switches. When the VM sends
- such a packet, then:
- </p>
- <ol>
- <li>
- It first enters the ingress pipeline, and then egress pipeline of the
- source localnet logical switch datapath. It then enters the ingress
- pipeline of the logical router datapath via the logical router port in
- the source chassis.
- </li>
-
- <li>
- Routing decision is taken.
- </li>
-
- <li>
- <p>
- From the router datapath, packet enters the ingress pipeline and then
- egress pipeline of the destination localnet logical switch datapath
- and goes out of the integration bridge to the provider bridge (
- belonging to the destination logical switch) via the localnet port.
- While sending the packet to provider bridge, we also replace router
- port MAC as source MAC with a chassis unique MAC.
- </p>
-
- <p>
- This chassis unique MAC is configured as global ovs config on each
- chassis (eg. via "<code>ovs-vsctl set open . external-ids:
- ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"</code>"). For more
- details, see <code>ovn-controller</code>(8).
- </p>
-
- <p>
- If the above is not configured, then source MAC would be the router
- port MAC. This could create problem if we have more than one chassis.
- This is because, since the router port is distributed, the same
- (MAC,VLAN) tuple will seen by physical network from other chassis as
- well, which could cause these issues:
- </p>
-
- <ul>
- <li>
- Continuous MAC moves in top-of-rack switch (ToR).
- </li>
- <li>
- ToR dropping the traffic, which is causing continuous MAC moves.
- </li>
- <li>
- ToR blocking the ports from which MAC moves are happening.
- </li>
- </ul>
- </li>
-
- <li>
- The destination chassis receives the packet via the localnet port and
- sends it to the integration bridge. The packet enters the
- ingress pipeline and then egress pipeline of the destination localnet
- logical switch and finally gets delivered to the destination VM port.
- </li>
- </ol>
-
- <h3>External traffic</h3>
-
- <p>
- The following happens when a VM sends an external traffic (which requires
- NATting) and the chassis hosting the VM doesn't have a distributed gateway
- port.
- </p>
-
- <ol>
- <li>
- The packet first enters the ingress pipeline, and then egress pipeline of
- the source localnet logical switch datapath. It then enters the ingress
- pipeline of the logical router datapath via the logical router port in
- the source chassis.
- </li>
-
- <li>
- Routing decision is taken. Since the gateway router or the distributed
- gateway port doesn't reside in the source chassis, the traffic is
- redirected to the gateway chassis via the tunnel port.
- </li>
-
- <li>
- The gateway chassis receives the packet via the tunnel port and the
- packet enters the egress pipeline of the logical router datapath. NAT
- rules are applied here. The packet then enters the ingress pipeline and
- then egress pipeline of the localnet logical switch datapath which
- provides external connectivity and finally goes out via the localnet
- port of the logical switch which provides external connectivity.
- </li>
- </ol>
-
- <p>
- Although this works, the VM traffic is tunnelled when sent from the compute
- chassis to the gateway chassis. In order for it to work properly, the MTU
- of the localnet logical switches must be lowered to account for the tunnel
- encapsulation.
- </p>
-
- <h2>
- Centralized routing for localnet VLAN tagged logical switches connected
- to a Logical Router
- </h2>
-
- <p>
- To overcome the tunnel encapsulation problem described in the previous
- section, <code>OVN</code> supports the option of enabling centralized
- routing for localnet VLAN tagged logical switches. CMS can configure the
- option <ref column="options:reside-on-redirect-chassis"
- table="Logical_Router_Port" db="OVN_NB"/> to <code>true</code> for each
- <ref table="Logical_Router_Port" db="OVN_NB"/> which connects to the
- localnet VLAN tagged logical switches. This causes the gateway
- chassis (hosting the distributed gateway port) to handle all the
- routing for these networks, making it centralized. It will reply to
- the ARP requests for the logical router port IPs.
- </p>
-
- <p>
- If the logical router doesn't have a distributed gateway port connecting
- to the localnet logical switch which provides external connectivity,
- then this option is ignored by <code>OVN</code>.
- </p>
-
- <p>
- The following happens when a VM sends an east-west traffic which needs to
- be routed:
- </p>
-
- <ol>
- <li>
- The packet first enters the ingress pipeline, and then egress pipeline of
- the source localnet logical switch datapath and is sent out via the
- localnet port of the source localnet logical switch (instead of sending
- it to router pipeline).
- </li>
-
- <li>
- The gateway chassis receives the packet via the localnet port of the
- source localnet logical switch and sends it to the integration bridge.
- The packet then enters the ingress pipeline, and then egress pipeline of
- the source localnet logical switch datapath and enters the ingress
- pipeline of the logical router datapath.
- </li>
-
- <li>
- Routing decision is taken.
- </li>
-
- <li>
- From the router datapath, packet enters the ingress pipeline and then
- egress pipeline of the destination localnet logical switch datapath.
- It then goes out of the integration bridge to the provider bridge (
- belonging to the destination logical switch) via the localnet port.
- </li>
-
- <li>
- The destination chassis receives the packet via the localnet port and
- sends it to the integration bridge. The packet enters the
- ingress pipeline and then egress pipeline of the destination localnet
- logical switch and finally delivered to the destination VM port.
- </li>
- </ol>
-
- <p>
- The following happens when a VM sends an external traffic which requires
- NATting:
- </p>
-
- <ol>
- <li>
- The packet first enters the ingress pipeline, and then egress pipeline of
- the source localnet logical switch datapath and is sent out via the
- localnet port of the source localnet logical switch (instead of sending
- it to router pipeline).
- </li>
-
- <li>
- The gateway chassis receives the packet via the localnet port of the
- source localnet logical switch and sends it to the integration bridge.
- The packet then enters the ingress pipeline, and then egress pipeline of
- the source localnet logical switch datapath and enters the ingress
- pipeline of the logical router datapath.
- </li>
-
- <li>
- Routing decision is taken and NAT rules are applied.
- </li>
-
- <li>
- From the router datapath, packet enters the ingress pipeline and then
- egress pipeline of the localnet logical switch datapath which provides
- external connectivity. It then goes out of the integration bridge to the
- provider bridge (belonging to the logical switch which provides external
- connectivity) via the localnet port.
- </li>
- </ol>
-
- <p>
- The following happens for the reverse external traffic.
- </p>
-
- <ol>
- <li>
- The gateway chassis receives the packet from the localnet port of
- the logical switch which provides external connectivity. The packet then
- enters the ingress pipeline and then egress pipeline of the localnet
- logical switch (which provides external connectivity). The packet then
- enters the ingress pipeline of the logical router datapath.
- </li>
-
- <li>
- The ingress pipeline of the logical router datapath applies the unNATting
- rules. The packet then enters the ingress pipeline and then egress
- pipeline of the source localnet logical switch. Since the source VM
- doesn't reside in the gateway chassis, the packet is sent out via the
- localnet port of the source logical switch.
- </li>
-
- <li>
- The source chassis receives the packet via the localnet port and
- sends it to the integration bridge. The packet enters the
- ingress pipeline and then egress pipeline of the source localnet
- logical switch and finally gets delivered to the source VM port.
- </li>
- </ol>
-
- <h2>Life Cycle of a VTEP gateway</h2>
-
- <p>
- A gateway is a chassis that forwards traffic between the OVN-managed
- part of a logical network and a physical VLAN, extending a
- tunnel-based logical network into a physical network.
- </p>
-
- <p>
- The steps below refer often to details of the OVN and VTEP database
- schemas. Please see <code>ovn-sb</code>(5), <code>ovn-nb</code>(5)
- and <code>vtep</code>(5), respectively, for the full story on these
- databases.
- </p>
-
- <ol>
- <li>
- A VTEP gateway's life cycle begins with the administrator registering
- the VTEP gateway as a <code>Physical_Switch</code> table entry in the
- <code>VTEP</code> database. The <code>ovn-controller-vtep</code>
- connected to this VTEP database, will recognize the new VTEP gateway
- and create a new <code>Chassis</code> table entry for it in the
- <code>OVN_Southbound</code> database.
- </li>
-
- <li>
- The administrator can then create a new <code>Logical_Switch</code>
- table entry, and bind a particular vlan on a VTEP gateway's port to
- any VTEP logical switch. Once a VTEP logical switch is bound to
- a VTEP gateway, the <code>ovn-controller-vtep</code> will detect
- it and add its name to the <var>vtep_logical_switches</var>
- column of the <code>Chassis</code> table in the <code>
- OVN_Southbound</code> database. Note, the <var>tunnel_key</var>
- column of VTEP logical switch is not filled at creation. The
- <code>ovn-controller-vtep</code> will set the column when the
- correponding vtep logical switch is bound to an OVN logical network.
- </li>
-
- <li>
- Now, the administrator can use the CMS to add a VTEP logical switch
- to the OVN logical network. To do that, the CMS must first create a
- new <code>Logical_Switch_Port</code> table entry in the <code>
- OVN_Northbound</code> database. Then, the <var>type</var> column
- of this entry must be set to "vtep". Next, the <var>
- vtep-logical-switch</var> and <var>vtep-physical-switch</var> keys
- in the <var>options</var> column must also be specified, since
- multiple VTEP gateways can attach to the same VTEP logical switch.
- </li>
-
- <li>
- The newly created logical port in the <code>OVN_Northbound</code>
- database and its configuration will be passed down to the <code>
- OVN_Southbound</code> database as a new <code>Port_Binding</code>
- table entry. The <code>ovn-controller-vtep</code> will recognize the
- change and bind the logical port to the corresponding VTEP gateway
- chassis. Configuration of binding the same VTEP logical switch to
- a different OVN logical networks is not allowed and a warning will be
- generated in the log.
- </li>
-
- <li>
- Beside binding to the VTEP gateway chassis, the <code>
- ovn-controller-vtep</code> will update the <var>tunnel_key</var>
- column of the VTEP logical switch to the corresponding <code>
- Datapath_Binding</code> table entry's <var>tunnel_key</var> for the
- bound OVN logical network.
- </li>
-
- <li>
- Next, the <code>ovn-controller-vtep</code> will keep reacting to the
- configuration change in the <code>Port_Binding</code> in the
- <code>OVN_Northbound</code> database, and updating the
- <code>Ucast_Macs_Remote</code> table in the <code>VTEP</code> database.
- This allows the VTEP gateway to understand where to forward the unicast
- traffic coming from the extended external network.
- </li>
-
- <li>
- Eventually, the VTEP gateway's life cycle ends when the administrator
- unregisters the VTEP gateway from the <code>VTEP</code> database.
- The <code>ovn-controller-vtep</code> will recognize the event and
- remove all related configurations (<code>Chassis</code> table entry
- and port bindings) in the <code>OVN_Southbound</code> database.
- </li>
-
- <li>
- When the <code>ovn-controller-vtep</code> is terminated, all related
- configurations in the <code>OVN_Southbound</code> database and
- the <code>VTEP</code> database will be cleaned, including
- <code>Chassis</code> table entries for all registered VTEP gateways
- and their port bindings, and all <code>Ucast_Macs_Remote</code> table
- entries and the <code>Logical_Switch</code> tunnel keys.
- </li>
- </ol>
-
- <h2>Native OVN services for external logical ports</h2>
-
- <p>
- To support OVN native services (like DHCP/IPv6 RA/DNS lookup) to the
- cloud resources which are external, OVN supports <code>external</code>
- logical ports.
- </p>
-
- <p>
- Below are some of the use cases where <code>external</code> ports can be
- used.
- </p>
-
- <ul>
- <li>
- VMs connected to SR-IOV nics - Traffic from these VMs by passes the
- kernel stack and local <code>ovn-controller</code> do not bind these
- ports and cannot serve the native services.
- </li>
- <li>
- When CMS supports provisioning baremetal servers.
- </li>
- </ul>
-
- <p>
- OVN will provide the native services if CMS has done the below
- configuration in the <dfn>OVN Northbound Database</dfn>.
- </p>
-
- <ul>
- <li>
- A row is created in <code>Logical_Switch_Port</code>, configuring the
- <ref column="addresses" table="Logical_Switch_Port" db="OVN_NB"/> column
- and setting the <ref column="type" table="Logical_Switch_Port"
- db="OVN_NB"/> to <code>external</code>.
- </li>
-
- <li>
- <ref column="ha_chassis_group" table="Logical_Switch_Port"
- db="OVN_NB"/> column is configured.
- </li>
-
- <li>
- The HA chassis which belongs to the HA chassis group has the
- <code>ovn-bridge-mappings</code> configured and has proper L2
- connectivity so that it can receive the DHCP and other related request
- packets from these external resources.
- </li>
-
- <li>
- The Logical_Switch of this port has a <code>localnet</code> port.
- </li>
-
- <li>
- Native OVN services are enabled by configuring the DHCP and other
- options like the way it is done for the normal logical ports.
- </li>
- </ul>
-
- <p>
- It is recommended to use the same HA chassis group for all the external
- ports of a logical switch. Otherwise, the physical switch might see MAC
- flap issue when different chassis provide the native services. For
- example when supporting native DHCPv4 service, DHCPv4 server mac
- (configured in <ref column="options:server_mac" table="DHCP_Options"
- db="OVN_NB"/> column in table <ref table="DHCP_Options"/>) originating
- from different ports can cause MAC flap issue.
- The MAC of the logical router IP(s) can also flap if the same HA chassis
- group is not set for all the external ports of a logical switch.
- </p>
-
- <h1>Security</h1>
-
- <h2>Role-Based Access Controls for the Soutbound DB</h2>
- <p>
- In order to provide additional security against the possibility of an OVN
- chassis becoming compromised in such a way as to allow rogue software to
- make arbitrary modifications to the southbound database state and thus
- disrupt the OVN network, role-based access controls (see
- <code>ovsdb-server(1)</code> for additional details) are provided for the
- southbound database.
- </p>
-
- <p>
- The implementation of role-based access controls (RBAC) requires the
- addition of two tables to an OVSDB schema: the <code>RBAC_Role</code>
- table, which is indexed by role name and maps the the names of the various
- tables that may be modifiable for a given role to individual rows in a
- permissions table containing detailed permission information for that role,
- and the permission table itself which consists of rows containing the
- following information:
- </p>
- <dl>
- <dt><code>Table Name</code></dt>
- <dd>
- The name of the associated table. This column exists primarily as an
- aid for humans reading the contents of this table.
- </dd>
-
- <dt><code>Auth Criteria</code></dt>
- <dd>
- A set of strings containing the names of columns (or column:key pairs
- for columns containing string:string maps). The contents of at least
- one of the columns or column:key values in a row to be modified,
- inserted, or deleted must be equal to the ID of the client attempting
- to act on the row in order for the authorization check to pass. If the
- authorization criteria is empty, authorization checking is disabled and
- all clients for the role will be treated as authorized.
- </dd>
-
- <dt><code>Insert/Delete</code></dt>
- <dd>
- Row insertion/deletion permission; boolean value indicating whether
- insertion and deletion of rows is allowed for the associated table.
- If true, insertion and deletion of rows is allowed for authorized
- clients.
- </dd>
-
- <dt><code>Updatable Columns</code></dt>
- <dd>
- A set of strings containing the names of columns or column:key pairs
- that may be updated or mutated by authorized clients. Modifications to
- columns within a row are only permitted when the authorization check
- for the client passes and all columns to be modified are included in
- this set of modifiable columns.
- </dd>
- </dl>
-
- <p>
- RBAC configuration for the OVN southbound database is maintained by
- ovn-northd. With RBAC enabled, modifications are only permitted for the
- <code>Chassis</code>, <code>Encap</code>, <code>Port_Binding</code>, and
- <code>MAC_Binding</code> tables, and are resstricted as follows:
- </p>
- <dl>
- <dt><code>Chassis</code></dt>
- <dd>
- <p>
- <code>Authorization</code>: client ID must match the chassis name.
- </p>
- <p>
- <code>Insert/Delete</code>: authorized row insertion and deletion
- are permitted.
- </p>
- <p>
- <code>Update</code>: The columns <code>nb_cfg</code>,
- <code>external_ids</code>, <code>encaps</code>, and
- <code>vtep_logical_switches</code> may be modified when authorized.
- </p>
- </dd>
-
- <dt><code>Encap</code></dt>
- <dd>
- <p>
- <code>Authorization</code>: client ID must match the chassis name.
- </p>
- <p>
- <code>Insert/Delete</code>: row insertion and row deletion
- are permitted.
- </p>
- <p>
- <code>Update</code>: The columns <code>type</code>,
- <code>options</code>, and <code>ip</code> can be modified.
- </p>
- </dd>
-
- <dt><code>Port_Binding</code></dt>
- <dd>
- <p>
- <code>Authorization</code>: disabled (all clients are considered
- authorized. A future enhancement may add columns (or keys to
- <code>external_ids</code>) in order to control which chassis are
- allowed to bind each port.
- </p>
- <p>
- <code>Insert/Delete</code>: row insertion/deletion are not permitted
- (ovn-northd maintains rows in this table.
- </p>
- <p>
- <code>Update</code>: Only modifications to the <code>chassis</code>
- column are permitted.
- </p>
- </dd>
-
- <dt><code>MAC_Binding</code></dt>
- <dd>
- <p>
- <code>Authorization</code>: disabled (all clients are considered
- to be authorized).
- </p>
- <p>
- <code>Insert/Delete</code>: row insertion/deletion are permitted.
- </p>
- <p>
- <code>Update</code>: The columns <code>logical_port</code>,
- <code>ip</code>, <code>mac</code>, and <code>datapath</code> may be
- modified by ovn-controller.
- </p>
- </dd>
- </dl>
-
- <p>
- Enabling RBAC for ovn-controller connections to the southbound database
- requires the following steps:
- </p>
-
- <ol>
- <li>
- Creating SSL certificates for each chassis with the certificate CN field
- set to the chassis name (e.g. for a chassis with
- <code>external-ids:system-id=chassis-1</code>, via the command
- "<code>ovs-pki -u req+sign chassis-1 switch</code>").
- </li>
- <li>
- Configuring each ovn-controller to use SSL when connecting to the
- southbound database (e.g. via "<code>ovs-vsctl set open .
- external-ids:ovn-remote=ssl:x.x.x.x:6642</code>").
- </li>
- <li>
- Configuring a southbound database SSL remote with "ovn-controller" role
- (e.g. via "<code>ovn-sbctl set-connection role=ovn-controller
- pssl:6642</code>").
- </li>
- </ol>
-
- <h2>Encrypt Tunnel Traffic with IPsec</h2>
- <p>
- OVN tunnel traffic goes through physical routers and switches. These
- physical devices could be untrusted (devices in public network) or might be
- compromised. Enabling encryption to the tunnel traffic can prevent the
- traffic data from being monitored and manipulated.
- </p>
- <p>
- The tunnel traffic is encrypted with IPsec. The CMS sets the
- <code>ipsec</code> column in the northbound <code>NB_Global</code> table to
- enable or disable IPsec encrytion. If <code>ipsec</code> is true, all OVN
- tunnels will be encrypted. If <code>ipsec</code> is false, no OVN tunnels
- will be encrypted.
- </p>
- <p>
- When CMS updates the <code>ipsec</code> column in the northbound
- <code>NB_Global</code> table, <code>ovn-northd</code> copies the value to
- the <code>ipsec</code> column in the southbound <code>SB_Global</code>
- table. <code>ovn-controller</code> in each chassis monitors the southbound
- database and sets the options of the OVS tunnel interface accordingly. OVS
- tunnel interface options are monitored by the
- <code>ovs-monitor-ipsec</code> daemon which configures IKE daemon to set up
- IPsec connections.
- </p>
- <p>
- Chassis authenticates each other by using certificate. The authentication
- succeeds if the other end in tunnel presents a certificate signed by a
- trusted CA and the common name (CN) matches the expected chassis name. The
- SSL certificates used in role-based access controls (RBAC) can be used in
- IPsec. Or use <code>ovs-pki</code> to create different certificates. The
- certificate is required to be x.509 version 3, and with CN field and
- subjectAltName field being set to the chassis name.
- </p>
- <p>
- The CA certificate, chassis certificate and private key are required to be
- installed in each chassis before enabling IPsec. Please see
- <code>ovs-vswitchd.conf.db</code>(5) for setting up CA based IPsec
- authentication.
- </p>
- <h1>Design Decisions</h1>
-
- <h2>Tunnel Encapsulations</h2>
-
- <p>
- OVN annotates logical network packets that it sends from one hypervisor to
- another with the following three pieces of metadata, which are encoded in
- an encapsulation-specific fashion:
- </p>
-
- <ul>
- <li>
- 24-bit logical datapath identifier, from the <code>tunnel_key</code>
- column in the OVN Southbound <code>Datapath_Binding</code> table.
- </li>
-
- <li>
- 15-bit logical ingress port identifier. ID 0 is reserved for internal
- use within OVN. IDs 1 through 32767, inclusive, may be assigned to
- logical ports (see the <code>tunnel_key</code> column in the OVN
- Southbound <code>Port_Binding</code> table).
- </li>
-
- <li>
- 16-bit logical egress port identifier. IDs 0 through 32767 have the same
- meaning as for logical ingress ports. IDs 32768 through 65535,
- inclusive, may be assigned to logical multicast groups (see the
- <code>tunnel_key</code> column in the OVN Southbound
- <code>Multicast_Group</code> table).
- </li>
- </ul>
-
- <p>
- For hypervisor-to-hypervisor traffic, OVN supports only Geneve and STT
- encapsulations, for the following reasons:
- </p>
-
- <ul>
- <li>
- Only STT and Geneve support the large amounts of metadata (over 32 bits
- per packet) that OVN uses (as described above).
- </li>
-
- <li>
- STT and Geneve use randomized UDP or TCP source ports that allows
- efficient distribution among multiple paths in environments that use ECMP
- in their underlay.
- </li>
-
- <li>
- NICs are available to offload STT and Geneve encapsulation and
- decapsulation.
- </li>
- </ul>
-
- <p>
- Due to its flexibility, the preferred encapsulation between hypervisors is
- Geneve. For Geneve encapsulation, OVN transmits the logical datapath
- identifier in the Geneve VNI.
-
- <!-- Keep the following in sync with ovn/controller/physical.h. -->
- OVN transmits the logical ingress and logical egress ports in a TLV with
- class 0x0102, type 0x80, and a 32-bit value encoded as follows, from MSB to
- LSB:
- </p>
-
- <diagram>
- <header name="">
- <bits name="rsv" above="1" below="0" width=".25"/>
- <bits name="ingress port" above="15" width=".75"/>
- <bits name="egress port" above="16" width=".75"/>
- </header>
- </diagram>
-
- <p>
- Environments whose NICs lack Geneve offload may prefer STT encapsulation
- for performance reasons. For STT encapsulation, OVN encodes all three
- pieces of logical metadata in the STT 64-bit tunnel ID as follows, from MSB
- to LSB:
- </p>
-
- <diagram>
- <header name="">
- <bits name="reserved" above="9" below="0" width=".5"/>
- <bits name="ingress port" above="15" width=".75"/>
- <bits name="egress port" above="16" width=".75"/>
- <bits name="datapath" above="24" width="1.25"/>
- </header>
- </diagram>
-
- <p>
- For connecting to gateways, in addition to Geneve and STT, OVN supports
- VXLAN, because only VXLAN support is common on top-of-rack (ToR) switches.
- Currently, gateways have a feature set that matches the capabilities as
- defined by the VTEP schema, so fewer bits of metadata are necessary. In
- the future, gateways that do not support encapsulations with large amounts
- of metadata may continue to have a reduced feature set.
- </p>
-</manpage>
diff --git a/ovn/utilities/automake.mk b/ovn/utilities/automake.mk
index e8c59a2eb..c2c3b7d5c 100644
--- a/ovn/utilities/automake.mk
+++ b/ovn/utilities/automake.mk
@@ -1,43 +1,16 @@
-scripts_SCRIPTS += \
- ovn/utilities/ovn-ctl \
- ovn/utilities/ovndb-servers.ocf
-
man_MANS += \
- ovn/utilities/ovn-ctl.8 \
ovn/utilities/ovn-nbctl.8 \
- ovn/utilities/ovn-sbctl.8 \
- ovn/utilities/ovn-trace.8 \
- ovn/utilities/ovn-detrace.1
+ ovn/utilities/ovn-sbctl.8
MAN_ROOTS += \
- ovn/utilities/ovn-sbctl.8.in \
- ovn/utilities/ovn-detrace.1.in
-
-# Docker drivers
-bin_SCRIPTS += \
- ovn/utilities/ovn-docker-overlay-driver \
- ovn/utilities/ovn-docker-underlay-driver \
- ovn/utilities/ovn-detrace
+ ovn/utilities/ovn-sbctl.8.in
EXTRA_DIST += \
- ovn/utilities/ovn-ctl \
- ovn/utilities/ovn-ctl.8.xml \
- ovn/utilities/ovn-docker-overlay-driver.in \
- ovn/utilities/ovn-docker-underlay-driver.in \
- ovn/utilities/ovn-nbctl.8.xml \
- ovn/utilities/ovn-trace.8.xml \
- ovn/utilities/ovn-detrace.in \
- ovn/utilities/ovndb-servers.ocf
+ ovn/utilities/ovn-nbctl.8.xml
CLEANFILES += \
- ovn/utilities/ovn-ctl.8 \
- ovn/utilities/ovn-docker-overlay-driver \
- ovn/utilities/ovn-docker-underlay-driver \
ovn/utilities/ovn-nbctl.8 \
- ovn/utilities/ovn-sbctl.8 \
- ovn/utilities/ovn-trace.8 \
- ovn/utilities/ovn-detrace.1 \
- ovn/utilities/ovn-detrace
+ ovn/utilities/ovn-sbctl.8
# ovn-nbctl
bin_PROGRAMS += ovn/utilities/ovn-nbctl
@@ -48,10 +21,3 @@ ovn_utilities_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenv
bin_PROGRAMS += ovn/utilities/ovn-sbctl
ovn_utilities_ovn_sbctl_SOURCES = ovn/utilities/ovn-sbctl.c
ovn_utilities_ovn_sbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
-
-# ovn-trace
-bin_PROGRAMS += ovn/utilities/ovn-trace
-ovn_utilities_ovn_trace_SOURCES = ovn/utilities/ovn-trace.c
-ovn_utilities_ovn_trace_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
-
-include ovn/utilities/bugtool/automake.mk
diff --git a/ovn/utilities/bugtool/automake.mk b/ovn/utilities/bugtool/automake.mk
deleted file mode 100644
index 8582074a7..000000000
--- a/ovn/utilities/bugtool/automake.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-if HAVE_PYTHON2
-bugtool_plugins += \
- ovn/utilities/bugtool/plugins/network-status/ovn.xml
-
-bugtool_scripts += \
- ovn/utilities/bugtool/ovn-bugtool-nbctl-show \
- ovn/utilities/bugtool/ovn-bugtool-sbctl-show \
- ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list
-endif
diff --git a/ovn/utilities/bugtool/ovn-bugtool-nbctl-show b/ovn/utilities/bugtool/ovn-bugtool-nbctl-show
deleted file mode 100644
index 927252745..000000000
--- a/ovn/utilities/bugtool/ovn-bugtool-nbctl-show
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /bin/sh
-
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General
-# Public License as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
-#
-# Copyright (C) 2016 Nicira, Inc.
-
-ovn-nbctl --timeout=3 show
diff --git a/ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list b/ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list
deleted file mode 100644
index 33a15d7d5..000000000
--- a/ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /bin/sh
-
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General
-# Public License as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
-#
-# Copyright (C) 2016 Nicira, Inc.
-
-ovn-sbctl --timeout=3 lflow-list
diff --git a/ovn/utilities/bugtool/ovn-bugtool-sbctl-show b/ovn/utilities/bugtool/ovn-bugtool-sbctl-show
deleted file mode 100644
index b6741bcc2..000000000
--- a/ovn/utilities/bugtool/ovn-bugtool-sbctl-show
+++ /dev/null
@@ -1,19 +0,0 @@
-#! /bin/sh
-
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General
-# Public License as published by the Free Software Foundation.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-# USA
-#
-# Copyright (C) 2016 Nicira, Inc.
-
-ovn-sbctl --timeout=3 show
diff --git a/ovn/utilities/bugtool/plugins/network-status/ovn.xml b/ovn/utilities/bugtool/plugins/network-status/ovn.xml
deleted file mode 100644
index 3b399feb3..000000000
--- a/ovn/utilities/bugtool/plugins/network-status/ovn.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- This library is free software; you can redistribute it and/or modify
- it under the terms of version 2.1 of the GNU Lesser General Public
- License as published by the Free Software Foundation.
-
- This library is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
- USA.
-
- Copyright (C) 2016 Nicira, Inc.
--->
-
-<collect>
- <command label="ovn-nbctl-show" filters="ovn">/usr/share/openvswitch/scripts/ovn-bugtool-nbctl-show</command>
- <command label="ovn-sbctl-show" filters="ovn">/usr/share/openvswitch/scripts/ovn-bugtool-sbctl-show</command>
- <command label="ovn-sbctl-lflow-list" filters="ovn">/usr/share/openvswitch/scripts/ovn-bugtool-sbctl-lflow-list</command>
-</collect>
diff --git a/ovn/utilities/ovn-ctl b/ovn/utilities/ovn-ctl
deleted file mode 100755
index 7e5cd469c..000000000
--- a/ovn/utilities/ovn-ctl
+++ /dev/null
@@ -1,822 +0,0 @@
-#!/bin/sh
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-case $0 in
- */*) dir0=`echo "$0" | sed 's,/[^/]*$,,'` ;;
- *) dir0=./ ;;
-esac
-. "$dir0/ovs-lib" || exit 1
-
-for dir in "$sbindir" "$bindir" /sbin /bin /usr/sbin /usr/bin; do
- case :$PATH: in
- *:$dir:*) ;;
- *) PATH=$PATH:$dir ;;
- esac
-done
-
-
-ovnnb_active_conf_file="$etcdir/ovnnb-active.conf"
-ovnsb_active_conf_file="$etcdir/ovnsb-active.conf"
-ovn_northd_db_conf_file="$etcdir/ovn-northd-db-params.conf"
-## ----- ##
-## start ##
-## ----- ##
-
-pidfile_is_running () {
- pidfile=$1
- test -e "$pidfile" && pid=`cat "$pidfile"` && pid_exists "$pid"
-} >/dev/null 2>&1
-
-stop_nb_ovsdb() {
- if pidfile_is_running $DB_NB_PID; then
- ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl exit
- fi
-}
-
-stop_sb_ovsdb() {
- if pidfile_is_running $DB_SB_PID; then
- ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl exit
- fi
-}
-
-stop_ovsdb () {
- stop_nb_ovsdb
- stop_sb_ovsdb
-}
-
-demote_ovnnb() {
- if test ! -z "$DB_NB_SYNC_FROM_ADDR"; then
- echo "$DB_NB_SYNC_FROM_PROTO:$DB_NB_SYNC_FROM_ADDR:$DB_NB_SYNC_FROM_PORT" > $ovnnb_active_conf_file
- fi
-
- if test -e $ovnnb_active_conf_file; then
- ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/set-active-ovsdb-server `cat $ovnnb_active_conf_file`
- ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/connect-active-ovsdb-server
- else
- echo >&2 "$0: active server details not set"
- exit 1
- fi
-}
-
-demote_ovnsb() {
- if test ! -z "$DB_SB_SYNC_FROM_ADDR"; then
- echo "$DB_SB_SYNC_FROM_PROTO:$DB_SB_SYNC_FROM_ADDR:$DB_SB_SYNC_FROM_PORT" > $ovnsb_active_conf_file
- fi
-
- if test -e $ovnsb_active_conf_file; then
- ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/set-active-ovsdb-server `cat $ovnsb_active_conf_file`
- ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/connect-active-ovsdb-server
- else
- echo >&2 "$0: active server details not set"
- exit 1
- fi
-}
-
-promote_ovnnb() {
- rm -f $ovnnb_active_conf_file
- ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/disconnect-active-ovsdb-server
-}
-
-promote_ovnsb() {
- rm -f $ovnsb_active_conf_file
- ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/disconnect-active-ovsdb-server
-}
-
-start_ovsdb__() {
- local DB=$1 db=$2 schema_name=$3 table_name=$4
- local db_pid_file
- local cluster_local_addr
- local cluster_local_port
- local cluster_local_proto
- local cluster_remote_addr
- local cluster_remote_port
- local cluster_remote_proto
- local sync_from_proto
- local sync_from_addr
- local sync_from_port
- local file
- local schema
- local logfile
- local log
- local sock
- local detach
- local create_insecure_remote
- local port
- local addr
- local active_conf_file
- local use_remote_in_db
- local ovn_db_ssl_key
- local ovn_db_ssl_cert
- local ovn_db_ssl_cacert
- eval db_pid_file=\$DB_${DB}_PID
- eval cluster_local_addr=\$DB_${DB}_CLUSTER_LOCAL_ADDR
- eval cluster_local_port=\$DB_${DB}_CLUSTER_LOCAL_PORT
- eval cluster_local_proto=\$DB_${DB}_CLUSTER_LOCAL_PROTO
- eval cluster_remote_addr=\$DB_${DB}_CLUSTER_REMOTE_ADDR
- eval cluster_remote_port=\$DB_${DB}_CLUSTER_REMOTE_PORT
- eval cluster_remote_proto=\$DB_${DB}_CLUSTER_REMOTE_PROTO
- eval sync_from_proto=\$DB_${DB}_SYNC_FROM_PROTO
- eval sync_from_addr=\$DB_${DB}_SYNC_FROM_ADDR
- eval sync_from_port=\$DB_${DB}_SYNC_FROM_PORT
- eval file=\$DB_${DB}_FILE
- eval schema=\$DB_${DB}_SCHEMA
- eval logfile=\$OVN_${DB}_LOGFILE
- eval log=\$OVN_${DB}_LOG
- eval sock=\$DB_${DB}_SOCK
- eval detach=\$DB_${DB}_DETACH
- eval create_insecure_remote=\$DB_${DB}_CREATE_INSECURE_REMOTE
- eval port=\$DB_${DB}_PORT
- eval addr=\$DB_${DB}_ADDR
- eval active_conf_file=\$ovn${db}_active_conf_file
- eval use_remote_in_db=\$DB_${DB}_USE_REMOTE_IN_DB
- eval ovn_db_ssl_key=\$OVN_${DB}_DB_SSL_KEY
- eval ovn_db_ssl_cert=\$OVN_${DB}_DB_SSL_CERT
- eval ovn_db_ssl_cacert=\$OVN_${DB}_DB_SSL_CA_CERT
-
- install_dir "$OVN_RUNDIR"
- # Check and eventually start ovsdb-server for DB
- if pidfile_is_running $db_pid_file; then
- return
- fi
-
- if test ! -z "$cluster_local_addr"; then
- mode=cluster
- elif test ! -z "$sync_from_addr"; then
- mode=active_passive
- echo "$sync_from_proto:$sync_from_addr:\
-$sync_from_port" > $active_conf_file
- else
- mode=standalone
- fi
-
- if test $mode = cluster; then
- local local=$cluster_local_proto:$cluster_local_addr:\
-$cluster_local_port
- local remote=$cluster_remote_proto:$cluster_remote_addr:\
-$cluster_remote_port
- if test -n "$cluster_remote_addr"; then
- join_cluster "$file" "$schema_name" "$local" "$remote"
- else
- create_cluster "$file" "$schema" "$local"
- fi
- else
- upgrade_db "$file" "$schema"
- fi
-
- set ovsdb-server
- set "$@" $log --log-file=$logfile
- set "$@" --remote=punix:$sock --pidfile=$db_pid_file
- set "$@" --unixctl=ovn${db}_db.ctl
-
- [ "$OVS_USER" != "" ] && set "$@" --user "$OVS_USER"
-
- if test X"$detach" != Xno; then
- set "$@" --detach --monitor
- else
- set exec "$@"
- fi
-
- if test X"$use_remote_in_db" != Xno; then
- set "$@" --remote=db:$schema_name,$table_name,connections
- fi
-
- if test X"$ovn_db_ssl_key" != X; then
- set "$@" --private-key=$ovn_db_ssl_key
- else
- set "$@" --private-key=db:$schema_name,SSL,private_key
- fi
- if test X"$ovn_db_ssl_cert" != X; then
- set "$@" --certificate=$ovn_db_ssl_cert
- else
- set "$@" --certificate=db:$schema_name,SSL,certificate
- fi
- if test X"$ovn_db_ssl_cacert" != X; then
- set "$@" --ca-cert=$ovn_db_ssl_cacert
- else
- set "$@" --ca-cert=db:$schema_name,SSL,ca_cert
- fi
-
- set "$@" --ssl-protocols=db:$schema_name,SSL,ssl_protocols
- set "$@" --ssl-ciphers=db:$schema_name,SSL,ssl_ciphers
-
- if test X"$create_insecure_remote" = Xyes; then
- set "$@" --remote=ptcp:$port:$addr
- fi
-
- if test $mode = active_passive; then
- set "$@" --sync-from=`cat $active_conf_file`
- fi
-
- "$@" "$file"
-
- # Initialize the database if it's running standalone,
- # active-passive, or is the first server in a cluster.
- if test -z "$cluster_remote_addr"; then
- ovn-${db}ctl init
- fi
-
- if test $mode = cluster; then
- upgrade_cluster "$schema" "unix:$sock"
- fi
-}
-
-start_nb_ovsdb() {
- start_ovsdb__ NB nb OVN_Northbound NB_Global
-}
-
-start_sb_ovsdb() {
- # Increase the limit on the number of open file descriptors, because
- # SB DB may connect to large number of chassises, on top of connections
- # for cluster members, northd, and serveral local unix sockets.
- MAXFD=8192
- if [ $(ulimit -n) -lt $MAXFD ]; then
- ulimit -n $MAXFD
- fi
-
- start_ovsdb__ SB sb OVN_Southbound SB_Global
-}
-
-start_ovsdb () {
- start_nb_ovsdb
- start_sb_ovsdb
-}
-
-sync_status() {
- ovs-appctl -t $OVN_RUNDIR/ovn${1}_db.ctl ovsdb-server/sync-status | awk '{if(NR==1) print $2}'
-}
-
-status_ovnnb() {
- if ! pidfile_is_running $DB_NB_PID; then
- echo "not-running"
- else
- echo "running/$(sync_status nb)"
- fi
-}
-
-status_ovnsb() {
- if ! pidfile_is_running $DB_SB_PID; then
- echo "not-running"
- else
- echo "running/$(sync_status sb)"
- fi
-}
-
-status_ovsdb () {
- if ! pidfile_is_running $DB_NB_PID; then
- log_success_msg "OVN Northbound DB is not running"
- else
- log_success_msg "OVN Northbound DB is running"
- fi
-
- if ! pidfile_is_running $DB_SB_PID; then
- log_success_msg "OVN Southbound DB is not running"
- else
- log_success_msg "OVN Southbound DB is running"
- fi
-}
-
-run_nb_ovsdb() {
- DB_NB_DETACH=no
- start_nb_ovsdb
-}
-
-run_sb_ovsdb() {
- DB_SB_DETACH=no
- start_sb_ovsdb
-}
-
-start_northd () {
- if [ ! -e $ovn_northd_db_conf_file ]; then
- if test X"$OVN_MANAGE_OVSDB" = Xyes; then
- start_ovsdb
-
- if ! pidfile_is_running $DB_NB_PID; then
- log_failure_msg "OVN Northbound DB is not running"
- exit
- fi
- if ! pidfile_is_running $DB_SB_PID; then
- log_failure_msg "OVN Southbound DB is not running"
- exit
- fi
- fi
- ovn_northd_params="--ovnnb-db=$OVN_NORTHD_NB_DB \
- --ovnsb-db=$OVN_NORTHD_SB_DB"
- else
- ovn_northd_params="`cat $ovn_northd_db_conf_file`"
- fi
-
- if daemon_is_running ovn-northd; then
- log_success_msg "ovn-northd is already running"
- else
- set ovn-northd
- if test X"$OVN_NORTHD_LOGFILE" != X; then
- set "$@" --log-file=$OVN_NORTHD_LOGFILE
- fi
-
- [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER"
-
- set "$@" $OVN_NORTHD_LOG $ovn_northd_params
-
- OVS_RUNDIR=${OVN_RUNDIR} start_daemon "$OVN_NORTHD_PRIORITY" "$OVN_NORTHD_WRAPPER" "$@"
- fi
-}
-
-start_controller () {
- set ovn-controller "unix:$DB_SOCK"
- set "$@" $OVN_CONTROLLER_LOG
- if test X"$OVN_CONTROLLER_SSL_KEY" != X; then
- set "$@" --private-key=$OVN_CONTROLLER_SSL_KEY
- fi
- if test X"$OVN_CONTROLLER_SSL_CERT" != X; then
- set "$@" --certificate=$OVN_CONTROLLER_SSL_CERT
- fi
- if test X"$OVN_CONTROLLER_SSL_CA_CERT" != X; then
- set "$@" --ca-cert=$OVN_CONTROLLER_SSL_CA_CERT
- fi
- if test X"$OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT" != X; then
- set "$@" --bootstrap-ca-cert=$OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT
- fi
-
- [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER"
-
- OVS_RUNDIR=${OVN_RUNDIR} start_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@"
-}
-
-start_controller_vtep () {
- set ovn-controller-vtep
- set "$@" -vconsole:emer -vsyslog:err -vfile:info
- if test X"$OVN_CONTROLLER_SSL_KEY" != X; then
- set "$@" --private-key=$OVN_CONTROLLER_SSL_KEY
- fi
- if test X"$OVN_CONTROLLER_SSL_CERT" != X; then
- set "$@" --certificate=$OVN_CONTROLLER_SSL_CERT
- fi
- if test X"$OVN_CONTROLLER_SSL_CA_CERT" != X; then
- set "$@" --ca-cert=$OVN_CONTROLLER_SSL_CA_CERT
- fi
- if test X"$OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT" != X; then
- set "$@" --bootstrap-ca-cert=$OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT
- fi
- if test X"$DB_SOCK" != X; then
- set "$@" --vtep-db=$DB_SOCK
- fi
- if test X"$DB_SB_SOCK" != X; then
- set "$@" --ovnsb-db=$DB_SB_SOCK
- fi
-
- [ "$OVN_USER" != "" ] && set "$@" --user "$OVN_USER"
-
- OVS_RUNDIR=${OVN_RUNDIR} start_daemon "$OVN_CONTROLLER_PRIORITY" "$OVN_CONTROLLER_WRAPPER" "$@"
-}
-
-## ---- ##
-## stop ##
-## ---- ##
-
-stop_northd () {
- OVS_RUNDIR=${OVN_RUNDIR} stop_daemon ovn-northd
-
- if [ ! -e $ovn_northd_db_conf_file ]; then
- if test X"$OVN_MANAGE_OVSDB" = Xyes; then
- stop_ovsdb
- fi
- fi
-}
-
-stop_controller () {
- OVS_RUNDIR=${OVN_RUNDIR} stop_daemon ovn-controller "$@"
-}
-
-stop_controller_vtep () {
- OVS_RUNDIR=${OVN_RUNDIR} stop_daemon ovn-controller-vtep
-}
-
-## ------- ##
-## restart ##
-## ------- ##
-
-restart_northd () {
- stop_northd
- start_northd
-}
-
-restart_controller () {
- stop_controller --restart
- start_controller
-}
-
-restart_controller_vtep () {
- stop_controller_vtep
- start_controller_vtep
-}
-
-restart_ovsdb () {
- stop_ovsdb
- start_ovsdb
-}
-
-restart_nb_ovsdb () {
- stop_nb_ovsdb
- start_nb_ovsdb
-}
-
-restart_sb_ovsdb () {
- stop_sb_ovsdb
- start_sb_ovsdb
-}
-
-## ---- ##
-## main ##
-## ---- ##
-
-set_defaults () {
- OVN_MANAGE_OVSDB=yes
-
- OVS_RUNDIR=${OVS_RUNDIR:-${rundir}}
- OVN_RUNDIR=${OVN_RUNDIR:-${OVS_RUNDIR}}
-
- DB_NB_SOCK=$OVN_RUNDIR/ovnnb_db.sock
- DB_NB_PID=$OVN_RUNDIR/ovnnb_db.pid
- DB_NB_FILE=$dbdir/ovnnb_db.db
- DB_NB_ADDR=0.0.0.0
- DB_NB_PORT=6641
- DB_NB_SYNC_FROM_PROTO=tcp
- DB_NB_SYNC_FROM_ADDR=
- DB_NB_SYNC_FROM_PORT=6641
-
- DB_SB_SOCK=$OVN_RUNDIR/ovnsb_db.sock
- DB_SB_PID=$OVN_RUNDIR/ovnsb_db.pid
- DB_SB_FILE=$dbdir/ovnsb_db.db
- DB_SB_ADDR=0.0.0.0
- DB_SB_PORT=6642
- DB_SB_SYNC_FROM_PROTO=tcp
- DB_SB_SYNC_FROM_ADDR=
- DB_SB_SYNC_FROM_PORT=6642
-
- DB_NB_SCHEMA=$datadir/ovn-nb.ovsschema
- DB_SB_SCHEMA=$datadir/ovn-sb.ovsschema
-
- DB_SOCK=$OVN_RUNDIR/db.sock
- DB_CONF_FILE=$dbdir/conf.db
-
- OVN_NORTHD_PRIORITY=-10
- OVN_NORTHD_WRAPPER=
- OVN_CONTROLLER_PRIORITY=-10
- OVN_CONTROLLER_WRAPPER=
-
- OVN_USER=
- OVS_USER=
-
- OVN_CONTROLLER_LOG="-vconsole:emer -vsyslog:err -vfile:info"
- OVN_NORTHD_LOG="-vconsole:emer -vsyslog:err -vfile:info"
- OVN_NORTHD_LOGFILE=""
- OVN_NB_LOG="-vconsole:off -vfile:info"
- OVN_SB_LOG="-vconsole:off -vfile:info"
- OVN_NB_LOGFILE="$logdir/ovsdb-server-nb.log"
- OVN_SB_LOGFILE="$logdir/ovsdb-server-sb.log"
-
- OVN_CONTROLLER_SSL_KEY=""
- OVN_CONTROLLER_SSL_CERT=""
- OVN_CONTROLLER_SSL_CA_CERT=""
- OVN_CONTROLLER_SSL_BOOTSTRAP_CA_CERT=""
-
- DB_SB_CREATE_INSECURE_REMOTE="no"
- DB_NB_CREATE_INSECURE_REMOTE="no"
-
- MONITOR="yes"
-
- DB_NB_DETACH="yes"
- DB_SB_DETACH="yes"
-
- DB_NB_CLUSTER_LOCAL_ADDR=""
- DB_NB_CLUSTER_LOCAL_PROTO="tcp"
- DB_NB_CLUSTER_LOCAL_PORT=6643
- DB_NB_CLUSTER_REMOTE_ADDR=""
- DB_NB_CLUSTER_REMOTE_PROTO="tcp"
- DB_NB_CLUSTER_REMOTE_PORT=6643
-
- DB_SB_CLUSTER_LOCAL_ADDR=""
- DB_SB_CLUSTER_LOCAL_PROTO="tcp"
- DB_SB_CLUSTER_LOCAL_PORT=6644
- DB_SB_CLUSTER_REMOTE_ADDR=""
- DB_SB_CLUSTER_REMOTE_PROTO="tcp"
- DB_SB_CLUSTER_REMOTE_PORT=6644
-
- OVN_NORTHD_NB_DB="unix:$DB_NB_SOCK"
- OVN_NORTHD_SB_DB="unix:$DB_SB_SOCK"
- DB_NB_USE_REMOTE_IN_DB="yes"
- DB_SB_USE_REMOTE_IN_DB="yes"
-
- OVN_NB_DB_SSL_KEY=""
- OVN_NB_DB_SSL_CERT=""
- OVN_NB_DB_SSL_CA_CERT=""
-
- OVN_SB_DB_SSL_KEY=""
- OVN_SB_DB_SSL_CERT=""
- OVN_SB_DB_SSL_CA_CERT=""
-
-}
-
-set_option () {
- var=`echo "$option" | tr abcdefghijklmnopqrstuvwxyz- ABCDEFGHIJKLMNOPQRSTUVWXYZ_`
- eval set=\${$var+yes}
- eval old_value=\$$var
- if test X$set = X || \
- (test $type = bool && \
- test X"$old_value" != Xno && test X"$old_value" != Xyes); then
- echo >&2 "$0: unknown option \"$arg\" (use --help for help)"
- return
- fi
- eval $var=\$value
-}
-
-usage () {
- set_defaults
- cat << EOF
-$0: controls Open Virtual Network daemons
-usage: $0 [OPTIONS] COMMAND
-
-This program is intended to be invoked internally by Open Virtual Network
-startup scripts. System administrators should not normally invoke it directly.
-
-Commands:
- start_northd start ovn-northd
- start_ovsdb start ovn related ovsdb-server processes
- start_nb_ovsdb start ovn northbound db ovsdb-server process
- start_sb_ovsdb start ovn southbound db ovsdb-server process
- start_controller start ovn-controller
- start_controller_vtep start ovn-controller-vtep
- stop_northd stop ovn-northd
- stop_ovsdb stop ovn related ovsdb-server processes
- stop_nb_ovsdb stop ovn northbound db ovsdb-server process
- stop_sb_ovsdb stop ovn southbound db ovsdb-server process
- stop_controller stop ovn-controller
- stop_controller_vtep stop ovn-controller-vtep
- restart_northd restart ovn-northd
- restart_ovsdb restart ovn related ovsdb-server processes
- restart_nb_ovsdb restart ovn northbound db ovsdb-server process
- restart_sb_ovsdb restart ovn southbound db ovsdb-server process
- restart_controller restart ovn-controller
- restart_controller_vtep restart ovn-controller-vtep
- status_northd status ovs-northd
- status_ovsdb status related ovsdb-server processes
- status_controller status ovn-controller
- status_controller_vtep status ovn-controller-vtep
- promote_ovnnb promote ovn northbound db backup server to active
- promote_ovnsb promote ovn southbound db backup server to active
- demote_ovnnb demote ovn northbound db active server to backup
- demote_ovnsb demote ovn southbound db active server to backup
- run_nb_ovsdb run ovn northbound db ovsdb-server process
- run_sb_ovsdb run ovn southbound db ovsdb-server process
-
-Options:
- --ovn-northd-priority=NICE set ovn-northd's niceness (default: $OVN_NORTHD_PRIORITY)
- --ovn-northd-wrapper=WRAPPER run with a wrapper like valgrind for debugging
- --ovn-controller-priority=NICE set ovn-controller's niceness (default: $OVN_CONTROLLER_PRIORITY)
- --ovn-controller-wrapper=WRAPPER run with a wrapper like valgrind for debugging
- --ovn-controller-ssl-key=KEY OVN Southbound SSL private key file
- --ovn-controller-ssl-cert=CERT OVN Southbound SSL certificate file
- --ovn-controller-ssl-ca-cert=CERT OVN Southbound SSL CA certificate file
- --ovn-controller-ssl-bootstrap-ca-cert=CERT Bootstrapped OVN Southbound SSL CA certificate file
- --ovn-nb-db-ssl-key=KEY OVN Northbound DB SSL private key file
- --ovn-nb-db-ssl-cert=CERT OVN Northbound DB SSL certificate file
- --ovn-nb-db-ssl-ca-cert=CERT OVN Northbound DB SSL CA certificate file
- --ovn-sb-db-ssl-key=KEY OVN Southbound DB SSL private key file
- --ovn-sb-db-ssl-cert=CERT OVN Southbound DB SSL certificate file
- --ovn-sb-db-ssl-ca-cert=CERT OVN Southbound DB SSL CA certificate file
- --ovn-manage-ovsdb=yes|no Whether or not the OVN databases should be
- automatically started and stopped along
- with ovn-northd. The default is "yes". If
- this is set to "no", the "start_ovsdb" and
- "stop_ovsdb" commands must be used to start
- and stop the OVN databases.
- --ovn-controller-log=STRING ovn controller process logging params (default: $OVN_CONTROLLER_LOG)
- --ovn-northd-log=STRING ovn northd process logging params (default: $OVN_NORTHD_LOG)
- --ovn-northd-logfile=STRING ovn northd process log file (default: $OVN_NORTHD_LOGFILE)
- --ovn-nb-log=STRING ovn NB ovsdb-server processes logging params (default: $OVN_NB_LOG)
- --ovn-sb-log=STRING ovn SB ovsdb-server processes logging params (default: $OVN_SB_LOG)
- --ovn-user="user[:group]" pass the --user flag to the ovn daemons
- --ovs-user="user[:group]" pass the --user flag to ovs daemons
- -h, --help display this help message
-
-File location options:
- --db-sock=SOCKET JSON-RPC socket name (default: $DB_SOCK)
- --db-nb-sock=SOCKET OVN_Northbound db socket (default: $DB_NB_SOCK)
- --db-sb-scok=SOCKET OVN_Southbound db socket (default: $DB_SB_SOCK)
- --db-nb-file=FILE OVN_Northbound db file (default: $DB_NB_FILE)
- --db-sb-file=FILE OVN_Southbound db file (default: $DB_SB_FILE)
- --db-nb-schema=FILE OVN_Northbound db file (default: $DB_NB_SCHEMA)
- --db-sb-schema=FILE OVN_Southbound db file (default: $DB_SB_SCHEMA)
- --db-nb-addr=ADDR OVN Northbound db ptcp address (default: $DB_NB_ADDR)
- --db-nb-port=PORT OVN Northbound db ptcp port (default: $DB_NB_PORT)
- --db-sb-addr=ADDR OVN Southbound db ptcp address (default: $DB_SB_ADDR)
- --db-sb-port=PORT OVN Southbound db ptcp port (default: $DB_SB_PORT)
- --ovn-nb-logfile=FILE OVN Northbound log file (default: $OVN_NB_LOGFILE)
- --ovn-sb-logfile=FILE OVN Southbound log file (default: $OVN_SB_LOGFILE)
- --db-nb-sync-from-addr=ADDR OVN Northbound active db tcp address (default: $DB_NB_SYNC_FROM_ADDR)
- --db-nb-sync-from-port=PORT OVN Northbound active db tcp port (default: $DB_NB_SYNC_FROM_PORT)
- --db-nb-sync-from-proto=PROTO OVN Northbound active db transport (default: $DB_NB_SYNC_FROM_PROTO)
- --db-nb-create-insecure-remote=yes|no Create ptcp OVN Northbound remote (default: $DB_NB_CREATE_INSECURE_REMOTE)
- --db-sb-sync-from-addr=ADDR OVN Southbound active db tcp address (default: $DB_SB_SYNC_FROM_ADDR)
- --db-sb-sync-from-port=ADDR OVN Southbound active db tcp port (default: $DB_SB_SYNC_FROM_PORT)
- --db-sb-sync-from-proto=PROTO OVN Southbound active db transport (default: $DB_SB_SYNC_FROM_PROTO)
- --db-sb-create-insecure-remote=yes|no Create ptcp OVN Southbound remote (default: $DB_SB_CREATE_INSECURE_REMOTE)
- --db-nb-cluster-local-addr=ADDR OVN_Northbound cluster local address \
- (default: $DB_NB_CLUSTER_LOCAL_ADDR)
- --db-nb-cluster-local-port=PORT OVN_Northbound cluster local tcp port \
- (default: $DB_NB_CLUSTER_LOCAL_PORT)
- --db-nb-cluster-local-proto=PROTO OVN_Northbound cluster local db transport \
- (default: $DB_NB_CLUSTER_LOCAL_PROTO)
- --db-nb-cluster-remote-addr=ADDR OVN_Northbound cluster remote address \
- (default: $DB_NB_CLUSTER_REMOTE_ADDR)
- --db-nb-cluster-remote-port=PORT OVN_Northbound cluster remote tcp port \
- (default: $DB_NB_CLUSTER_REMOTE_PORT)
- --db-nb-cluster-remote-proto=PROTO OVN_Northbound cluster remote db \
- transport (default: $DB_NB_CLUSTER_REMOTE_PROTO)
- --db-sb-cluster-local-addr=ADDR OVN_Southbound cluster local address \
- (default: $DB_SB_CLUSTER_LOCAL_ADDR)
- --db-sb-cluster-local-port=PORT OVN_Southbound cluster local tcp port \
- (default: $DB_SB_CLUSTER_LOCAL_PORT)
- --db-sb-cluster-local-proto=PROTO OVN_Southbound cluster local db transport \
- (default: $DB_SB_CLUSTER_LOCAL_PROTO)
- --db-sb-cluster-remote-addr=ADDR OVN_Southbound cluster remote address \
- (default: $DB_SB_CLUSTER_REMOTE_ADDR)
- --db-sb-cluster-remote-port=PORT OVN_Southbound cluster remote tcp port \
- (default: $DB_SB_CLUSTER_REMOTE_PORT)
- --db-sb-cluster-remote-proto=PROTO OVN_Southbound cluster remote db \
- transport (default: $DB_SB_CLUSTER_REMOTE_PROTO)
- --ovn-northd-nb-db=NB DB address(es) (default: $OVN_NORTHD_NB_DB)
- --ovn-northd-sb-db=SB DB address(es) (default: $OVN_NORTHD_SB_DB)
- --db-nb-use-remote-in-db=yes|no OVN_Northbound db listen on target connection table (default: $DB_NB_USE_REMOTE_IN_DB)
- --db-sb-use-remote-in-db=yes|no OVN_Southbound db listen on target connection table (default: $DB_SB_USE_REMOTE_IN_DB)
-
-Default directories with "configure" option and environment variable override:
- logs: /usr/local/var/log/openvswitch (--with-logdir, OVS_LOGDIR)
- pidfiles and sockets: /usr/local/var/run/openvswitch (--with-rundir, OVS_RUNDIR)
- ovn-nb.db: /usr/local/etc/openvswitch (--with-dbdir, OVS_DBDIR)
- ovn-sb.db: /usr/local/etc/openvswitch (--with-dbdir, OVS_DBDIR)
- system configuration: /usr/local/etc (--sysconfdir, OVS_SYSCONFDIR)
- data files: /usr/local/share/openvswitch (--pkgdatadir, OVS_PKGDATADIR)
- user binaries: /usr/local/bin (--bindir, OVS_BINDIR)
- system binaries: /usr/local/sbin (--sbindir, OVS_SBINDIR)
-EOF
-}
-
-set_defaults
-command=
-for arg
-do
- case $arg in
- -h | --help)
- usage
- ;;
- --[a-z]*=*)
- option=`expr X"$arg" : 'X--\([^=]*\)'`
- value=`expr X"$arg" : 'X[^=]*=\(.*\)'`
- type=string
- set_option
- ;;
- --no-[a-z]*)
- option=`expr X"$arg" : 'X--no-\(.*\)'`
- value=no
- type=bool
- set_option
- ;;
- --[a-z]*)
- option=`expr X"$arg" : 'X--\(.*\)'`
- value=yes
- type=bool
- set_option
- ;;
- -*)
- echo >&2 "$0: unknown option \"$arg\" (use --help for help)"
- exit 1
- ;;
- *)
- if test X"$command" = X; then
- command=$arg
- else
- echo >&2 "$0: exactly one non-option argument required (use --help for help)"
- exit 1
- fi
- ;;
- esac
-done
-case $command in
- start_northd)
- start_northd
- ;;
- start_ovsdb)
- start_ovsdb
- ;;
- start_nb_ovsdb)
- start_nb_ovsdb
- ;;
- start_sb_ovsdb)
- start_sb_ovsdb
- ;;
- start_controller)
- start_controller
- ;;
- start_controller_vtep)
- start_controller_vtep
- ;;
- stop_northd)
- stop_northd
- ;;
- stop_ovsdb)
- stop_ovsdb
- ;;
- stop_nb_ovsdb)
- stop_nb_ovsdb
- ;;
- stop_sb_ovsdb)
- stop_sb_ovsdb
- ;;
- stop_controller)
- stop_controller
- ;;
- stop_controller_vtep)
- stop_controller_vtep
- ;;
- restart_northd)
- restart_northd
- ;;
- restart_ovsdb)
- restart_ovsdb
- ;;
- restart_nb_ovsdb)
- restart_nb_ovsdb
- ;;
- restart_sb_ovsdb)
- restart_sb_ovsdb
- ;;
- restart_controller)
- restart_controller
- ;;
- restart_controller_vtep)
- restart_controller_vtep
- ;;
- status_northd)
- daemon_status ovn-northd || exit 1
- ;;
- status_ovsdb)
- status_ovsdb
- ;;
- status_controller)
- daemon_status ovn-controller || exit 1
- ;;
- status_controller_vtep)
- daemon_status ovn-controller-vtep || exit 1
- ;;
- promote_ovnnb)
- promote_ovnnb
- ;;
- promote_ovnsb)
- promote_ovnsb
- ;;
- demote_ovnnb)
- demote_ovnnb
- ;;
- demote_ovnsb)
- demote_ovnsb
- ;;
- status_ovnnb)
- status_ovnnb
- ;;
- status_ovnsb)
- status_ovnsb
- ;;
- run_nb_ovsdb)
- run_nb_ovsdb
- ;;
- run_sb_ovsdb)
- run_sb_ovsdb
- ;;
- help)
- usage
- ;;
- preheat)
- echo >&2 "$0: preheating ovn to 350 degrees F."
- exit 1
- ;;
- '')
- echo >&2 "$0: missing command name (use --help for help)"
- exit 1
- ;;
- *)
- echo >&2 "$0: unknown command \"$command\" (use --help for help)"
- exit 1
- ;;
-esac
diff --git a/ovn/utilities/ovn-ctl.8.xml b/ovn/utilities/ovn-ctl.8.xml
deleted file mode 100644
index c5294d794..000000000
--- a/ovn/utilities/ovn-ctl.8.xml
+++ /dev/null
@@ -1,215 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manpage program="ovn-ctl" section="8" title="ovn-ctl">
- <h1>Name</h1>
- <p>ovn-ctl -- Open Virtual Network northbound daemon lifecycle utility</p>
-
- <h1>Synopsis</h1>
- <p><code>ovn-ctl</code> [<var>options</var>] <var>command</var></p>
-
- <h1>Description</h1>
- <p>This program is intended to be invoked internally by Open Virtual Network
- startup scripts. System administrators should not normally invoke it directly.</p>
-
- <h1>Commands</h1>
-
- <dl>
- <dt><code>start_northd</code></dt>
- <dt><code>start_controller</code></dt>
- <dt><code>start_controller_vtep</code></dt>
- <dt><code>stop_northd</code></dt>
- <dt><code>stop_controller</code></dt>
- <dt><code>stop_controller_vtep</code></dt>
- <dt><code>restart_northd</code></dt>
- <dt><code>restart_controller</code></dt>
- <dt><code>restart_controller_vtep</code></dt>
- <dt><code>promote_ovnnb</code></dt>
- <dt><code>promote_ovnsb</code></dt>
- <dt><code>demote_ovnnb</code></dt>
- <dt><code>demote_ovnsb</code></dt>
- <dt><code>status_ovnnb</code></dt>
- <dt><code>status_ovnsb</code></dt>
- <dt><code>start_ovsdb</code></dt>
- <dt><code>start_nb_ovsdb</code></dt>
- <dt><code>start_sb_ovsdb</code></dt>
- <dt><code>stop_ovsdb</code></dt>
- <dt><code>stop_nb_ovsdb</code></dt>
- <dt><code>stop_sb_ovsdb</code></dt>
- <dt><code>restart_ovsdb</code></dt>
- <dt><code>run_nb_ovsdb</code></dt>
- <dt><code>run_sb_ovsdb</code></dt>
- </dl>
-
- <h1>Options</h1>
- <p><code>--ovn-northd-priority=<var>NICE</var></code></p>
- <p><code>--ovn-northd-wrapper=<var>WRAPPER</var></code></p>
- <p><code>--ovn-controller-priority=<var>NICE</var></code></p>
- <p><code>--ovn-controller-wrapper=<var>WRAPPER</var></code></p>
- <p><code>--ovn-user=<var>USER:GROUP</var></code></p>
- <p><code>--ovs-user=<var>USER:GROUP</var></code></p>
- <p><code>-h</code> | <code>--help</code></p>
-
- <h1>File location options</h1>
- <p><code>--db-sock=<var>SOCKET</var></code></p>
- <p><code>--db-nb-file=<var>FILE</var></code></p>
- <p><code>--db-sb-file=<var>FILE</var></code></p>
- <p><code>--db-nb-schema=<var>FILE</var></code></p>
- <p><code>--db-sb-schema=<var>FILE</var></code></p>
- <p><code>--db-sb-create-insecure-remote=<var>yes|no</var></code></p>
- <p><code>--db-nb-create-insecure-remote=<var>yes|no</var></code></p>
- <p><code>--ovn-controller-ssl-key=<var>KEY</var></code></p>
- <p><code>--ovn-controller-ssl-cert=<var>CERT</var></code></p>
- <p><code>--ovn-controller-ssl-ca-cert=<var>CERT</var></code></p>
- <p><code>--ovn-controller-ssl-bootstrap-ca-cert=<var>CERT</var></code></p>
-
- <h1>Address and port options</h1>
- <p><code>--db-nb-sync-from-addr=<var>IP ADDRESS</var></code></p>
- <p><code>--db-nb-sync-from-port=<var>PORT NUMBER</var></code></p>
- <p><code>--db-nb-sync-from-proto=<var>PROTO</var></code></p>
- <p><code>--db-sb-sync-from-addr=<var>IP ADDRESS</var></code></p>
- <p><code>--db-sb-sync-from-port=<var>PORT NUMBER</var></code></p>
- <p><code>--db-sb-sync-from-proto=<var>PROTO</var></code></p>
- <p>
- <code>
- --ovn-northd-nb-db=<var>PROTO</var>:<var>IP ADDRESS</var>:
- <var>PORT</var>..
- </code>
- </p>
- <p>
- <code>
- --ovn-northd-sb-db=<var>PROTO</var>:<var>IP ADDRESS</var>:
- <var>PORT</var>..
- </code>
- </p>
- <h1> Clustering options </h1>
- <p><code>--db-nb-cluster-local-addr=<var>IP ADDRESS</var></code></p>
- <p><code>--db-nb-cluster-local-port=<var>PORT NUMBER</var></code></p>
- <p><code>--db-nb-cluster-local-proto=<var>PROTO (tcp/ssl)</var></code></p>
- <p><code>--db-nb-cluster-remote-addr=<var>IP ADDRESS</var></code></p>
- <p><code>--db-nb-cluster-remote-port=<var>PORT NUMBER</var></code></p>
- <p><code>--db-nb-cluster-remote-proto=<var>PROTO (tcp/ssl)</var></code></p>
- <p><code>--db-sb-cluster-local-addr=<var>IP ADDRESS</var></code></p>
- <p><code>--db-sb-cluster-local-port=<var>PORT NUMBER</var></code></p>
- <p><code>--db-sb-cluster-local-proto=<var>PROTO (tcp/ssl)</var></code></p>
- <p><code>--db-sb-cluster-remote-addr=<var>IP ADDRESS</var></code></p>
- <p><code>--db-sb-cluster-remote-port=<var>PORT NUMBER</var></code></p>
- <p><code>--db-sb-cluster-remote-proto=<var>PROTO (tcp/ssl)</var></code></p>
-
- <h1>Configuration files</h1>
- <p>Following are the optional configuration files. If present, it should be located in the etc dir</p>
-
- <h2>ovnnb-active.conf</h2>
- <p>
- If present, this file should hold the url to connect to the active
- Northbound DB server
- </p>
- <p><code>tcp:x.x.x.x:6641</code></p>
-
- <h2>ovnsb-active.conf</h2>
- <p>
- If present, this file should hold the url to connect to the active
- Southbound DB server
- </p>
- <p><code>tcp:x.x.x.x:6642</code></p>
-
- <h2>ovn-northd-db-params.conf</h2>
- <p>
- If present, start_northd will not start the DB server even if
- <code>--ovn-manage-ovsdb=yes</code>. This file should hold the database url
- parameters to be passed to ovn-northd.
- </p>
- <p><code>--ovnnb-db=tcp:x.x.x.x:6641 --ovnsb-db=tcp:x.x.x.x:6642</code></p>
-
- <h1> Running OVN db servers without detaching </h1>
- <p><code># ovn-ctl run_nb_ovsdb</code></p>
- <p>
- This command runs the OVN nb ovsdb-server without passing the
- <code>detach</code> option, making it to block until ovsdb-server exits.
- This command will be useful for starting the OVN nb ovsdb-server in a
- container.
- </p>
- <p><code># ovn-ctl run_sb_ovsdb</code></p>
- <p>
- This command runs the OVN sb ovsdb-server without passing the
- <code>detach</code> option, making it to block until ovsdb-server exits.
- This command will be useful for starting the OVN sb ovsdb-server in a
- container.
- </p>
-
- <h1>Example Usage</h1>
- <h2>Run ovn-controller on a host already running OVS</h2>
- <p><code># ovn-ctl start_controller</code></p>
-
- <h2>Run ovn-northd on a host already running OVS</h2>
- <p><code># ovn-ctl start_northd</code></p>
-
- <h2>All-in-one OVS+OVN for testing</h2>
- <p><code># ovs-ctl start --system-id="random"</code></p>
- <p><code># ovn-ctl start_northd</code></p>
- <p><code># ovn-ctl start_controller</code></p>
-
- <h2>Promote and demote ovsdb servers</h2>
- <p><code># ovn-ctl promote_ovnnb</code></p>
- <p><code># ovn-ctl promote_ovnsb</code></p>
- <p><code># ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 demote_ovnnb</code></p>
- <p><code># ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 demote_ovnsb</code></p>
-
- <h2>Creating a clustered db on 3 nodes with IPs x.x.x.x, y.y.y.y and z.z.z.z</h2>
- <h3>Starting OVN ovsdb servers and ovn-northd on the node with IP x.x.x.x</h3>
- <p>
- <code>
- # ovn-ctl --db-nb-addr=x.x.x.x --db-nb-create-insecure-remote=yes
- --db-sb-addr=x.x.x.x --db-sb-create-insecure-remote=yes
- --db-nb-cluster-local-addr=x.x.x.x
- --db-sb-cluster-local-addr=x.x.x.x
- --ovn-northd-nb-db=tcp:x.x.x.x:6641,tcp:y.y.y.y:6641,tcp:z.z.z.z:6641
- --ovn-northd-sb-db=tcp:x.x.x.x:6642,tcp:y.y.y.y:6642,tcp:z.z.z.z:6642
- start_northd
- </code>
- </p>
-
- <h3>Starting OVN ovsdb-servers and ovn-northd on the node with IP y.y.y.y and joining the cluster started at x.x.x.x</h3>
- <p>
- <code>
- # ovn-ctl --db-nb-addr=y.y.y.y --db-nb-create-insecure-remote=yes
- --db-sb-addr=y.y.y.y --db-sb-create-insecure-remote=yes
- --db-nb-cluster-local-addr=y.y.y.y
- --db-sb-cluster-local-addr=y.y.y.y
- --db-nb-cluster-remote-addr=x.x.x.x
- --db-sb-cluster-remote-addr=x.x.x.x
- --ovn-northd-nb-db=tcp:x.x.x.x:6641,tcp:y.y.y.y:6641,tcp:z.z.z.z:6641
- --ovn-northd-sb-db=tcp:x.x.x.x:6642,tcp:y.y.y.y:6642,tcp:z.z.z.z:6642
- start_northd
- </code>
- </p>
-
- <h3>Starting OVN ovsdb-servers and ovn-northd on the node with IP z.z.z.z and joining the cluster started at x.x.x.x</h3>
- <p>
- <code>
- # ovn-ctl --db-nb-addr=z.z.z.z
- --db-nb-create-insecure-remote=yes
- --db-nb-cluster-local-addr=z.z.z.z
- --db-sb-addr=z.z.z.z
- --db-sb-create-insecure-remote=yes
- --db-sb-cluster-local-addr=z.z.z.z
- --db-nb-cluster-remote-addr=x.x.x.x
- --db-sb-cluster-remote-addr=x.x.x.x
- --ovn-northd-nb-db=tcp:x.x.x.x:6641,tcp:y.y.y.y:6641,tcp:z.z.z.z:6641
- --ovn-northd-sb-db=tcp:x.x.x.x:6642,tcp:y.y.y.y:6642,tcp:z.z.z.z:6642
- start_northd
- </code>
- </p>
-
- <h2>Passing ssl keys when starting OVN dbs will supercede the default ssl values in db</h2>
- <h3>Starting standalone ovn db server passing SSL certificates</h3>
- <p>
- <code>
- # ovn-ctl --ovn-nb-db-ssl-key=/etc/openvswitch/ovnnb-privkey.pem
- --ovn-nb-db-ssl-cert=/etc/openvswitch/ovnnb-cert.pem
- --ovn-nb-db-ssl-ca-cert=/etc/openvswitch/cacert.pem
- --ovn-sb-db-ssl-key=/etc/openvswitch/ovnsb-privkey.pem
- --ovn-sb-db-ssl-cert=/etc/openvswitch/ovnsb-cert.pem
- --ovn-sb-db-ssl-ca-cert=/etc/openvswitch/cacert.pem
- start_northd
- </code>
- </p>
-</manpage>
diff --git a/ovn/utilities/ovn-detrace.1.in b/ovn/utilities/ovn-detrace.1.in
deleted file mode 100644
index 2f662d4fe..000000000
--- a/ovn/utilities/ovn-detrace.1.in
+++ /dev/null
@@ -1,38 +0,0 @@
-.so lib/ovs.tmac
-.TH ovn\-detrace 1 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
-.
-.SH NAME
-ovn\-detrace \- convert ``ovs\-appctl ofproto/trace'' output to combine
-OVN logical flow information.
-.
-.SH SYNOPSIS
-\fBovn\-detrace < \fIfile\fR
-.so lib/common-syn.man
-.
-.SH DESCRIPTION
-The \fBovn\-detrace\fR program reads \fBovs\-appctl ofproto/trace\fR output on
-stdin, looking for flow cookies, and expand each cookie with corresponding OVN
-logical flows. It expands logical flow further with the north-bound information
-e.g. the ACL that generated the logical flow, when relevant.
-.PP
-.
-.SH "OPTIONS"
-.so lib/common.man
-.
-.IP "\fB\-\-ovnsb=\fIserver\fR"
-The OVN Southbound DB remote to contact. If the \fBOVN_SB_DB\fR
-environment variable is set, its value is used as the default.
-Otherwise, the default is \fBunix:@RUNDIR@/ovnsb_db.sock\fR, but this
-default is unlikely to be useful outside of single-machine OVN test
-environments.
-.
-.IP "\fB\-\-ovnnb=\fIserver\fR"
-The OVN Northbound DB remote to contact. If the \fBOVN_NB_DB\fR
-environment variable is set, its value is used as the default.
-Otherwise, the default is \fBunix:@RUNDIR@/ovnnb_db.sock\fR, but this
-default is unlikely to be useful outside of single-machine OVN test
-environments.
-.
-.SH "SEE ALSO"
-.
-.BR ovs\-appctl (8), ovn\-sbctl (8), ovn-\-nbctl (8), ovn\-trace (8)
diff --git a/ovn/utilities/ovn-detrace.in b/ovn/utilities/ovn-detrace.in
deleted file mode 100755
index c842adc32..000000000
--- a/ovn/utilities/ovn-detrace.in
+++ /dev/null
@@ -1,215 +0,0 @@
-#! @PYTHON@
-#
-# Copyright (c) 2017 eBay Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import getopt
-import os
-import re
-import sys
-import time
-
-try:
- from ovs.db import idl
- from ovs import jsonrpc
- from ovs.poller import Poller
- from ovs.stream import Stream
-except Exception:
- print("ERROR: Please install the correct Open vSwitch python support")
- print(" libraries (@VERSION@).")
- print(" Alternatively, check that your PYTHONPATH is pointing to")
- print(" the correct location.")
- sys.exit(1)
-
-
-argv0 = sys.argv[0]
-
-
-def usage():
- print """\
-%(argv0)s:
-usage: %(argv0)s < FILE
-where FILE is output from ovs-appctl ofproto/trace.
-
-The following options are also available:
- -h, --help display this help message
- -V, --version display version information
- --ovnsb=DATABASE use DATABASE as southbound DB
- --ovnnb=DATABASE use DATABASE as northbound DB\
-""" % {'argv0': argv0}
- sys.exit(0)
-
-
-class OVSDB(object):
- @staticmethod
- def wait_for_db_change(idl):
- seq = idl.change_seqno
- stop = time.time() + 10
- while idl.change_seqno == seq and not idl.run():
- poller = Poller()
- idl.wait(poller)
- poller.block()
- if time.time() >= stop:
- raise Exception('Retry Timeout')
-
- def __init__(self, db_sock, schema_name):
- self._db_sock = db_sock
- self._txn = None
- schema = self._get_schema(schema_name)
- schema.register_all()
- self._idl_conn = idl.Idl(db_sock, schema)
- OVSDB.wait_for_db_change(self._idl_conn) # Initial Sync with DB
-
- def _get_schema(self, schema_name):
- error, strm = Stream.open_block(Stream.open(self._db_sock))
- if error:
- raise Exception("Unable to connect to %s" % self._db_sock)
- rpc = jsonrpc.Connection(strm)
- req = jsonrpc.Message.create_request('get_schema', [schema_name])
- error, resp = rpc.transact_block(req)
- rpc.close()
-
- if error or resp.error:
- raise Exception('Unable to retrieve schema.')
- return idl.SchemaHelper(None, resp.result)
-
- def get_table(self, table_name):
- return self._idl_conn.tables[table_name]
-
- def _find_row(self, table_name, find):
- return next(
- (row for row in self.get_table(table_name).rows.values()
- if find(row)), None)
-
- def _find_row_by_name(self, table_name, value):
- return self._find_row(table_name, lambda row: row.name == value)
-
- def find_row_by_partial_uuid(self, table_name, value):
- return self._find_row(table_name, lambda row: value in str(row.uuid))
-
-
-def get_lflow_from_cookie(ovnsb_db, cookie):
- return ovnsb_db.find_row_by_partial_uuid('Logical_Flow', cookie)
-
-
-def print_lflow(lflow, prefix):
- ldp_uuid = lflow.logical_datapath.uuid
- ldp_name = str(lflow.logical_datapath.external_ids.get('name'))
-
- print '%sLogical datapath: "%s" (%s) [%s]' % (prefix,
- ldp_name,
- ldp_uuid,
- lflow.pipeline)
- print "%sLogical flow: table=%s (%s), priority=%s, " \
- "match=(%s), actions=(%s)" % (prefix,
- lflow.table_id,
- lflow.external_ids.get('stage-name'),
- lflow.priority,
- str(lflow.match).strip('"'),
- str(lflow.actions).strip('"'))
-
-
-def print_lflow_nb_hint(lflow, prefix, ovnnb_db):
- external_ids = lflow.external_ids
- if external_ids.get('stage-name') in ['ls_in_acl',
- 'ls_out_acl']:
- acl_hint = external_ids.get('stage-hint')
- if not acl_hint:
- return
- acl = ovnnb_db.find_row_by_partial_uuid('ACL', acl_hint)
- if not acl:
- return
- output = "%sACL: %s, priority=%s, " \
- "match=(%s), %s" % (prefix,
- acl.direction,
- acl.priority,
- acl.match.strip('"'),
- acl.action)
- if acl.log:
- output += ' (log)'
- print output
-
-
-def main():
- try:
- options, args = getopt.gnu_getopt(sys.argv[1:], 'hV',
- ['help', 'version', 'ovnsb=', 'ovnnb='])
- except getopt.GetoptError, geo:
- sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
- sys.exit(1)
-
- ovnsb_db = None
- ovnnb_db = None
-
- for key, value in options:
- if key in ['-h', '--help']:
- usage()
- elif key in ['-V', '--version']:
- print "%s (Open vSwitch) @VERSION@" % argv0
- elif key in ['--ovnsb']:
- ovnsb_db = value
- elif key in ['--ovnnb']:
- ovnnb_db = value
- else:
- sys.exit(0)
-
- if len(args) != 0:
- sys.stderr.write("%s: non-option argument not supported "
- "(use --help for help)\n" % argv0)
- sys.exit(1)
-
- ovs_rundir = os.getenv('OVS_RUNDIR', '@RUNDIR@')
- if not ovnsb_db:
- ovnsb_db = os.getenv('OVN_SB_DB')
- if not ovnsb_db:
- ovnsb_db = 'unix:%s/ovnsb_db.sock' % ovs_rundir
-
- if not ovnnb_db:
- ovnnb_db = os.getenv('OVN_NB_DB')
- if not ovnnb_db:
- ovnnb_db = 'unix:%s/ovnnb_db.sock' % ovs_rundir
-
- ovsdb_ovnsb = OVSDB(ovnsb_db, 'OVN_Southbound')
- ovsdb_ovnnb = OVSDB(ovnnb_db, 'OVN_Northbound')
-
- regex_cookie = re.compile(r'^.*cookie 0x([0-9a-fA-F]+)')
- regex_table_id = re.compile(r'^[0-9]+\.')
- cookie = None
- while True:
- line = sys.stdin.readline()
- if cookie:
- # print lflow info when the current flow block ends
- if regex_table_id.match(line) or line.strip() == '':
- lflow = get_lflow_from_cookie(ovsdb_ovnsb, cookie)
- print_lflow(lflow, " * ")
- print_lflow_nb_hint(lflow, " * ", ovsdb_ovnnb)
- cookie = None
-
- print line.strip()
- if line == "":
- break
-
- m = regex_cookie.match(line)
- if not m:
- continue
- cookie = m.group(1)
-
-
-if __name__ == "__main__":
- main()
-
-
-# Local variables:
-# mode: python
-# End:
diff --git a/ovn/utilities/ovn-docker-overlay-driver.in b/ovn/utilities/ovn-docker-overlay-driver.in
deleted file mode 100755
index 65edfcd9d..000000000
--- a/ovn/utilities/ovn-docker-overlay-driver.in
+++ /dev/null
@@ -1,442 +0,0 @@
-#! @PYTHON@
-# Copyright (C) 2015 Nicira, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import argparse
-import ast
-import atexit
-import json
-import os
-import random
-import re
-import shlex
-import subprocess
-import sys
-
-import ovs.dirs
-import ovs.util
-import ovs.daemon
-import ovs.vlog
-
-from flask import Flask, jsonify
-from flask import request, abort
-
-app = Flask(__name__)
-vlog = ovs.vlog.Vlog("ovn-docker-overlay-driver")
-
-OVN_BRIDGE = "br-int"
-OVN_NB = ""
-PLUGIN_DIR = "/etc/docker/plugins"
-PLUGIN_FILE = "/etc/docker/plugins/openvswitch.spec"
-
-
-def call_popen(cmd):
- child = subprocess.Popen(cmd, stdout=subprocess.PIPE)
- output = child.communicate()
- if child.returncode:
- raise RuntimeError("Fatal error executing %s" % (cmd))
- if len(output) == 0 or output[0] == None:
- output = ""
- else:
- output = output[0].strip()
- return output
-
-
-def call_prog(prog, args_list):
- cmd = [prog, "--timeout=5", "-vconsole:off"] + args_list
- return call_popen(cmd)
-
-
-def ovs_vsctl(*args):
- return call_prog("ovs-vsctl", list(args))
-
-
-def ovn_nbctl(*args):
- args_list = list(args)
- database_option = "%s=%s" % ("--db", OVN_NB)
- args_list.insert(0, database_option)
- return call_prog("ovn-nbctl", args_list)
-
-
-def cleanup():
- if os.path.isfile(PLUGIN_FILE):
- os.remove(PLUGIN_FILE)
-
-
-def ovn_init_overlay():
- br_list = ovs_vsctl("list-br").split()
- if OVN_BRIDGE not in br_list:
- ovs_vsctl("--", "--may-exist", "add-br", OVN_BRIDGE,
- "--", "set", "bridge", OVN_BRIDGE,
- "external_ids:bridge-id=" + OVN_BRIDGE,
- "other-config:disable-in-band=true", "fail-mode=secure")
-
- global OVN_NB
- OVN_NB = ovs_vsctl("get", "Open_vSwitch", ".",
- "external_ids:ovn-nb").strip('"')
- if not OVN_NB:
- sys.exit("OVN central database's ip address not set")
-
- ovs_vsctl("set", "open_vswitch", ".",
- "external_ids:ovn-bridge=" + OVN_BRIDGE)
-
-
-def prepare():
- parser = argparse.ArgumentParser()
-
- ovs.vlog.add_args(parser)
- ovs.daemon.add_args(parser)
- args = parser.parse_args()
- ovs.vlog.handle_args(args)
- ovs.daemon.handle_args(args)
- ovn_init_overlay()
-
- if not os.path.isdir(PLUGIN_DIR):
- os.makedirs(PLUGIN_DIR)
-
- ovs.daemon.daemonize()
- try:
- fo = open(PLUGIN_FILE, "w")
- fo.write("tcp://0.0.0.0:5000")
- fo.close()
- except Exception as e:
- ovs.util.ovs_fatal(0, "Failed to write to spec file (%s)" % str(e),
- vlog)
-
- atexit.register(cleanup)
-
-
-@app.route('/Plugin.Activate', methods=['POST'])
-def plugin_activate():
- return jsonify({"Implements": ["NetworkDriver"]})
-
-
-@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
-def get_capability():
- return jsonify({"Scope": "global"})
-
-
-@app.route('/NetworkDriver.DiscoverNew', methods=['POST'])
-def new_discovery():
- return jsonify({})
-
-
-@app.route('/NetworkDriver.DiscoverDelete', methods=['POST'])
-def delete_discovery():
- return jsonify({})
-
-
-@app.route('/NetworkDriver.CreateNetwork', methods=['POST'])
-def create_network():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- # NetworkID will have docker generated network uuid and it
- # becomes 'name' in a OVN Logical switch record.
- network = data.get("NetworkID", "")
- if not network:
- abort(400)
-
- # Limit subnet handling to ipv4 till ipv6 usecase is clear.
- ipv4_data = data.get("IPv4Data", "")
- if not ipv4_data:
- error = "create_network: No ipv4 subnet provided"
- return jsonify({'Err': error})
-
- subnet = ipv4_data[0].get("Pool", "")
- if not subnet:
- error = "create_network: no subnet in ipv4 data from libnetwork"
- return jsonify({'Err': error})
-
- gateway_ip = ipv4_data[0].get("Gateway", "").rsplit('/', 1)[0]
- if not gateway_ip:
- error = "create_network: no gateway in ipv4 data from libnetwork"
- return jsonify({'Err': error})
-
- try:
- ovn_nbctl("ls-add", network, "--", "set", "Logical_Switch",
- network, "external_ids:subnet=" + subnet,
- "external_ids:gateway_ip=" + gateway_ip)
- except Exception as e:
- error = "create_network: ls-add %s" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({})
-
-
-@app.route('/NetworkDriver.DeleteNetwork', methods=['POST'])
-def delete_network():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- try:
- ovn_nbctl("ls-del", nid)
- except Exception as e:
- error = "delete_network: ls-del %s" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({})
-
-
-@app.route('/NetworkDriver.CreateEndpoint', methods=['POST'])
-def create_endpoint():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- interface = data.get("Interface", "")
- if not interface:
- error = "create_endpoint: no interfaces structure supplied by " \
- "libnetwork"
- return jsonify({'Err': error})
-
- ip_address_and_mask = interface.get("Address", "")
- if not ip_address_and_mask:
- error = "create_endpoint: ip address not provided by libnetwork"
- return jsonify({'Err': error})
-
- ip_address = ip_address_and_mask.rsplit('/', 1)[0]
- mac_address_input = interface.get("MacAddress", "")
- mac_address_output = ""
-
- try:
- ovn_nbctl("lsp-add", nid, eid)
- except Exception as e:
- error = "create_endpoint: lsp-add (%s)" % (str(e))
- return jsonify({'Err': error})
-
- if not mac_address_input:
- mac_address = "02:%02x:%02x:%02x:%02x:%02x" % (random.randint(0, 255),
- random.randint(0, 255),
- random.randint(0, 255),
- random.randint(0, 255),
- random.randint(0, 255))
- else:
- mac_address = mac_address_input
-
- try:
- ovn_nbctl("lsp-set-addresses", eid,
- mac_address + " " + ip_address)
- except Exception as e:
- error = "create_endpoint: lsp-set-addresses (%s)" % (str(e))
- return jsonify({'Err': error})
-
- # Only return a mac address if one did not come as request.
- mac_address_output = ""
- if not mac_address_input:
- mac_address_output = mac_address
-
- return jsonify({"Interface": {
- "Address": "",
- "AddressIPv6": "",
- "MacAddress": mac_address_output
- }})
-
-
-def get_lsp_addresses(eid):
- ret = ovn_nbctl("--if-exists", "get", "Logical_Switch_Port", eid,
- "addresses")
- if not ret:
- error = "endpoint not found in OVN database"
- return (None, None, error)
- addresses = ast.literal_eval(ret)
- if len(addresses) == 0:
- error = "unexpected return while fetching addresses"
- return (None, None, error)
- (mac_address, ip_address) = addresses[0].split()
- return (mac_address, ip_address, None)
-
-
-@app.route('/NetworkDriver.EndpointOperInfo', methods=['POST'])
-def show_endpoint():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- try:
- (mac_address, ip_address, error) = get_lsp_addresses(eid)
- if error:
- jsonify({'Err': error})
- except Exception as e:
- error = "show_endpoint: get Logical_Switch_Port addresses. (%s)" \
- % (str(e))
- return jsonify({'Err': error})
-
- veth_outside = eid[0:15]
- return jsonify({"Value": {"ip_address": ip_address,
- "mac_address": mac_address,
- "veth_outside": veth_outside
- }})
-
-
-@app.route('/NetworkDriver.DeleteEndpoint', methods=['POST'])
-def delete_endpoint():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- try:
- ovn_nbctl("lsp-del", eid)
- except Exception as e:
- error = "delete_endpoint: lsp-del %s" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({})
-
-
-@app.route('/NetworkDriver.Join', methods=['POST'])
-def network_join():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- sboxkey = data.get("SandboxKey", "")
- if not sboxkey:
- abort(400)
-
- # sboxkey is of the form: /var/run/docker/netns/CONTAINER_ID
- vm_id = sboxkey.rsplit('/')[-1]
-
- try:
- (mac_address, ip_address, error) = get_lsp_addresses(eid)
- if error:
- jsonify({'Err': error})
- except Exception as e:
- error = "network_join: %s" % (str(e))
- return jsonify({'Err': error})
-
- veth_outside = eid[0:15]
- veth_inside = eid[0:13] + "_c"
- command = "ip link add %s type veth peer name %s" \
- % (veth_inside, veth_outside)
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_join: failed to create veth pair (%s)" % (str(e))
- return jsonify({'Err': error})
-
- command = "ip link set dev %s address %s" \
- % (veth_inside, mac_address)
-
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_join: failed to set veth mac address (%s)" % (str(e))
- return jsonify({'Err': error})
-
- command = "ip link set %s up" % (veth_outside)
-
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_join: failed to up the veth interface (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- ovs_vsctl("add-port", OVN_BRIDGE, veth_outside)
- ovs_vsctl("set", "interface", veth_outside,
- "external_ids:attached-mac=" + mac_address,
- "external_ids:iface-id=" + eid,
- "external_ids:vm-id=" + vm_id,
- "external_ids:iface-status=active")
- except Exception as e:
- error = "network_join: failed to create a port (%s)" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({"InterfaceName": {
- "SrcName": veth_inside,
- "DstPrefix": "eth"
- },
- "Gateway": "",
- "GatewayIPv6": ""})
-
-
-@app.route('/NetworkDriver.Leave', methods=['POST'])
-def network_leave():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- veth_outside = eid[0:15]
- command = "ip link delete %s" % (veth_outside)
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_leave: failed to delete veth pair (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- ovs_vsctl("--if-exists", "del-port", veth_outside)
- except Exception as e:
- error = "network_leave: failed to delete port (%s)" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({})
-
-if __name__ == '__main__':
- prepare()
- app.run(host='0.0.0.0')
diff --git a/ovn/utilities/ovn-docker-underlay-driver.in b/ovn/utilities/ovn-docker-underlay-driver.in
deleted file mode 100755
index d91ce9fca..000000000
--- a/ovn/utilities/ovn-docker-underlay-driver.in
+++ /dev/null
@@ -1,677 +0,0 @@
-#! @PYTHON@
-# Copyright (C) 2015 Nicira, Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at:
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import argparse
-import atexit
-import getpass
-import json
-import os
-import re
-import shlex
-import subprocess
-import sys
-import time
-import uuid
-
-import ovs.dirs
-import ovs.util
-import ovs.daemon
-import ovs.unixctl.server
-import ovs.vlog
-
-from neutronclient.v2_0 import client
-from flask import Flask, jsonify
-from flask import request, abort
-
-app = Flask(__name__)
-vlog = ovs.vlog.Vlog("ovn-docker-underlay-driver")
-
-AUTH_STRATEGY = ""
-AUTH_URL = ""
-ENDPOINT_URL = ""
-OVN_BRIDGE = ""
-PASSWORD = ""
-PLUGIN_DIR = "/etc/docker/plugins"
-PLUGIN_FILE = "/etc/docker/plugins/openvswitch.spec"
-TENANT_ID = ""
-USERNAME = ""
-VIF_ID = ""
-
-
-def call_popen(cmd):
- child = subprocess.Popen(cmd, stdout=subprocess.PIPE)
- output = child.communicate()
- if child.returncode:
- raise RuntimeError("Fatal error executing %s" % (cmd))
- if len(output) == 0 or output[0] == None:
- output = ""
- else:
- output = output[0].strip()
- return output
-
-
-def call_prog(prog, args_list):
- cmd = [prog, "--timeout=5", "-vconsole:off"] + args_list
- return call_popen(cmd)
-
-
-def ovs_vsctl(*args):
- return call_prog("ovs-vsctl", list(args))
-
-
-def cleanup():
- if os.path.isfile(PLUGIN_FILE):
- os.remove(PLUGIN_FILE)
-
-
-def ovn_init_underlay(args):
- global USERNAME, PASSWORD, TENANT_ID, AUTH_URL, AUTH_STRATEGY, VIF_ID
- global OVN_BRIDGE
-
- if not args.bridge:
- sys.exit("OVS bridge name not provided")
- OVN_BRIDGE = args.bridge
-
- VIF_ID = os.environ.get('OS_VIF_ID', '')
- if not VIF_ID:
- sys.exit("env OS_VIF_ID not set")
- USERNAME = os.environ.get('OS_USERNAME', '')
- if not USERNAME:
- sys.exit("env OS_USERNAME not set")
- TENANT_ID = os.environ.get('OS_TENANT_ID', '')
- if not TENANT_ID:
- sys.exit("env OS_TENANT_ID not set")
- AUTH_URL = os.environ.get('OS_AUTH_URL', '')
- if not AUTH_URL:
- sys.exit("env OS_AUTH_URL not set")
- AUTH_STRATEGY = "keystone"
-
- PASSWORD = os.environ.get('OS_PASSWORD', '')
- if not PASSWORD:
- PASSWORD = getpass.getpass()
-
-
-def prepare():
- parser = argparse.ArgumentParser()
- parser.add_argument('--bridge', help="The Bridge to which containers "
- "interfaces connect to.")
-
- ovs.vlog.add_args(parser)
- ovs.daemon.add_args(parser)
- args = parser.parse_args()
- ovs.vlog.handle_args(args)
- ovs.daemon.handle_args(args)
- ovn_init_underlay(args)
-
- if not os.path.isdir(PLUGIN_DIR):
- os.makedirs(PLUGIN_DIR)
-
- ovs.daemon.daemonize()
- try:
- fo = open(PLUGIN_FILE, "w")
- fo.write("tcp://127.0.0.1:5000")
- fo.close()
- except Exception as e:
- ovs.util.ovs_fatal(0, "Failed to write to spec file (%s)" % str(e),
- vlog)
-
- atexit.register(cleanup)
-
-
-@app.route('/Plugin.Activate', methods=['POST'])
-def plugin_activate():
- return jsonify({"Implements": ["NetworkDriver"]})
-
-
-@app.route('/NetworkDriver.GetCapabilities', methods=['POST'])
-def get_capability():
- return jsonify({"Scope": "global"})
-
-
-@app.route('/NetworkDriver.DiscoverNew', methods=['POST'])
-def new_discovery():
- return jsonify({})
-
-
-@app.route('/NetworkDriver.DiscoverDelete', methods=['POST'])
-def delete_discovery():
- return jsonify({})
-
-
-def neutron_login():
- try:
- neutron = client.Client(username=USERNAME,
- password=PASSWORD,
- tenant_id=TENANT_ID,
- auth_url=AUTH_URL,
- endpoint_url=ENDPOINT_URL,
- auth_strategy=AUTH_STRATEGY)
- except Exception as e:
- raise RuntimeError("Failed to login into Neutron(%s)" % str(e))
- return neutron
-
-
-def get_networkuuid_by_name(neutron, name):
- param = {'fields': 'id', 'name': name}
- ret = neutron.list_networks(**param)
- if len(ret['networks']) > 1:
- raise RuntimeError("More than one network for the given name")
- elif len(ret['networks']) == 0:
- network = None
- else:
- network = ret['networks'][0]['id']
- return network
-
-
-def get_subnetuuid_by_name(neutron, name):
- param = {'fields': 'id', 'name': name}
- ret = neutron.list_subnets(**param)
- if len(ret['subnets']) > 1:
- raise RuntimeError("More than one subnet for the given name")
- elif len(ret['subnets']) == 0:
- subnet = None
- else:
- subnet = ret['subnets'][0]['id']
- return subnet
-
-
-@app.route('/NetworkDriver.CreateNetwork', methods=['POST'])
-def create_network():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- # NetworkID will have docker generated network uuid and it
- # becomes 'name' in a neutron network record.
- network = data.get("NetworkID", "")
- if not network:
- abort(400)
-
- # Limit subnet handling to ipv4 till ipv6 usecase is clear.
- ipv4_data = data.get("IPv4Data", "")
- if not ipv4_data:
- error = "create_network: No ipv4 subnet provided"
- return jsonify({'Err': error})
-
- subnet = ipv4_data[0].get("Pool", "")
- if not subnet:
- error = "create_network: no subnet in ipv4 data from libnetwork"
- return jsonify({'Err': error})
-
- gateway_ip = ipv4_data[0].get("Gateway", "").rsplit('/', 1)[0]
- if not gateway_ip:
- error = "create_network: no gateway in ipv4 data from libnetwork"
- return jsonify({'Err': error})
-
- try:
- neutron = neutron_login()
- except Exception as e:
- error = "create_network: neutron login. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- if get_networkuuid_by_name(neutron, network):
- error = "create_network: network has already been created"
- return jsonify({'Err': error})
- except Exception as e:
- error = "create_network: neutron network uuid by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- body = {'network': {'name': network, 'admin_state_up': True}}
- ret = neutron.create_network(body)
- network_id = ret['network']['id']
- except Exception as e:
- error = "create_network: neutron net-create call. (%s)" % str(e)
- return jsonify({'Err': error})
-
- subnet_name = "docker-%s" % (network)
-
- try:
- body = {'subnet': {'network_id': network_id,
- 'ip_version': 4,
- 'cidr': subnet,
- 'gateway_ip': gateway_ip,
- 'name': subnet_name}}
- created_subnet = neutron.create_subnet(body)
- except Exception as e:
- error = "create_network: neutron subnet-create call. (%s)" % str(e)
- return jsonify({'Err': error})
-
- return jsonify({})
-
-
-@app.route('/NetworkDriver.DeleteNetwork', methods=['POST'])
-def delete_network():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- try:
- neutron = neutron_login()
- except Exception as e:
- error = "delete_network: neutron login. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- network = get_networkuuid_by_name(neutron, nid)
- if not network:
- error = "delete_network: failed in network by name. (%s)" % (nid)
- return jsonify({'Err': error})
- except Exception as e:
- error = "delete_network: network uuid by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- neutron.delete_network(network)
- except Exception as e:
- error = "delete_network: neutron net-delete. (%s)" % str(e)
- return jsonify({'Err': error})
-
- return jsonify({})
-
-
-def reserve_vlan():
- reserved_vlan = 0
- vlans = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".",
- "external_ids:vlans").strip('"')
- if not vlans:
- reserved_vlan = 1
- ovs_vsctl("set", "Open_vSwitch", ".",
- "external_ids:vlans=" + str(reserved_vlan))
- return reserved_vlan
-
- vlan_set = str(vlans).split(',')
-
- for vlan in range(1, 4095):
- if str(vlan) not in vlan_set:
- vlan_set.append(str(vlan))
- reserved_vlan = vlan
- vlans = re.sub(r'[ \[\]\']', '', str(vlan_set))
- ovs_vsctl("set", "Open_vSwitch", ".",
- "external_ids:vlans=" + vlans)
- return reserved_vlan
-
- if not reserved_vlan:
- raise RuntimeError("No more vlans available on this host")
-
-
-def unreserve_vlan(reserved_vlan):
- vlans = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".",
- "external_ids:vlans").strip('"')
- if not vlans:
- return
-
- vlan_set = str(vlans).split(',')
- if str(reserved_vlan) not in vlan_set:
- return
-
- vlan_set.remove(str(reserved_vlan))
- vlans = re.sub(r'[ \[\]\']', '', str(vlan_set))
- if vlans:
- ovs_vsctl("set", "Open_vSwitch", ".", "external_ids:vlans=" + vlans)
- else:
- ovs_vsctl("remove", "Open_vSwitch", ".", "external_ids", "vlans")
-
-
-def create_port_underlay(neutron, network, eid, ip_address, mac_address):
- reserved_vlan = reserve_vlan()
- if mac_address:
- body = {'port': {'network_id': network,
- 'binding:profile': {'parent_name': VIF_ID,
- 'tag': int(reserved_vlan)},
- 'mac_address': mac_address,
- 'fixed_ips': [{'ip_address': ip_address}],
- 'name': eid,
- 'admin_state_up': True}}
- else:
- body = {'port': {'network_id': network,
- 'binding:profile': {'parent_name': VIF_ID,
- 'tag': int(reserved_vlan)},
- 'fixed_ips': [{'ip_address': ip_address}],
- 'name': eid,
- 'admin_state_up': True}}
-
- try:
- ret = neutron.create_port(body)
- mac_address = ret['port']['mac_address']
- except Exception as e:
- unreserve_vlan(reserved_vlan)
- raise RuntimeError("Failed in creation of neutron port (%s)." % str(e))
-
- ovs_vsctl("set", "Open_vSwitch", ".",
- "external_ids:" + eid + "_vlan=" + str(reserved_vlan))
-
- return mac_address
-
-
-def get_endpointuuid_by_name(neutron, name):
- param = {'fields': 'id', 'name': name}
- ret = neutron.list_ports(**param)
- if len(ret['ports']) > 1:
- raise RuntimeError("More than one endpoint for the given name")
- elif len(ret['ports']) == 0:
- endpoint = None
- else:
- endpoint = ret['ports'][0]['id']
- return endpoint
-
-
-@app.route('/NetworkDriver.CreateEndpoint', methods=['POST'])
-def create_endpoint():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- interface = data.get("Interface", "")
- if not interface:
- error = "create_endpoint: no interfaces supplied by libnetwork"
- return jsonify({'Err': error})
-
- ip_address_and_mask = interface.get("Address", "")
- if not ip_address_and_mask:
- error = "create_endpoint: ip address not provided by libnetwork"
- return jsonify({'Err': error})
-
- ip_address = ip_address_and_mask.rsplit('/', 1)[0]
- mac_address_input = interface.get("MacAddress", "")
- mac_address_output = ""
-
- try:
- neutron = neutron_login()
- except Exception as e:
- error = "create_endpoint: neutron login. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- endpoint = get_endpointuuid_by_name(neutron, eid)
- if endpoint:
- error = "create_endpoint: Endpoint has already been created"
- return jsonify({'Err': error})
- except Exception as e:
- error = "create_endpoint: endpoint uuid by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- network = get_networkuuid_by_name(neutron, nid)
- if not network:
- error = "Failed to get neutron network record for (%s)" % (nid)
- return jsonify({'Err': error})
- except Exception as e:
- error = "create_endpoint: network uuid by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- mac_address = create_port_underlay(neutron, network, eid, ip_address,
- mac_address_input)
- except Exception as e:
- error = "create_endpoint: neutron port-create (%s)" % (str(e))
- return jsonify({'Err': error})
-
- if not mac_address_input:
- mac_address_output = mac_address
-
- return jsonify({"Interface": {
- "Address": "",
- "AddressIPv6": "",
- "MacAddress": mac_address_output
- }})
-
-
-@app.route('/NetworkDriver.EndpointOperInfo', methods=['POST'])
-def show_endpoint():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- try:
- neutron = neutron_login()
- except Exception as e:
- error = "%s" % (str(e))
- return jsonify({'Err': error})
-
- try:
- endpoint = get_endpointuuid_by_name(neutron, eid)
- if not endpoint:
- error = "show_endpoint: Failed to get endpoint by name"
- return jsonify({'Err': error})
- except Exception as e:
- error = "show_endpoint: get endpoint by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- ret = neutron.show_port(endpoint)
- mac_address = ret['port']['mac_address']
- ip_address = ret['port']['fixed_ips'][0]['ip_address']
- except Exception as e:
- error = "show_endpoint: show port (%s)" % (str(e))
- return jsonify({'Err': error})
-
- veth_outside = eid[0:15]
- return jsonify({"Value": {"ip_address": ip_address,
- "mac_address": mac_address,
- "veth_outside": veth_outside
- }})
-
-
-@app.route('/NetworkDriver.DeleteEndpoint', methods=['POST'])
-def delete_endpoint():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- try:
- neutron = neutron_login()
- except Exception as e:
- error = "delete_endpoint: neutron login (%s)" % (str(e))
- return jsonify({'Err': error})
-
- endpoint = get_endpointuuid_by_name(neutron, eid)
- if not endpoint:
- return jsonify({})
-
- reserved_vlan = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".",
- "external_ids:" + eid + "_vlan").strip('"')
- if reserved_vlan:
- unreserve_vlan(reserved_vlan)
- ovs_vsctl("remove", "Open_vSwitch", ".", "external_ids",
- eid + "_vlan")
-
- try:
- neutron.delete_port(endpoint)
- except Exception as e:
- error = "delete_endpoint: neutron port-delete. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({})
-
-
-@app.route('/NetworkDriver.Join', methods=['POST'])
-def network_join():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- sboxkey = data.get("SandboxKey", "")
- if not sboxkey:
- abort(400)
-
- # sboxkey is of the form: /var/run/docker/netns/CONTAINER_ID
- vm_id = sboxkey.rsplit('/')[-1]
-
- try:
- neutron = neutron_login()
- except Exception as e:
- error = "network_join: neutron login. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- subnet_name = "docker-%s" % (nid)
- try:
- subnet = get_subnetuuid_by_name(neutron, subnet_name)
- if not subnet:
- error = "network_join: can't find subnet in neutron"
- return jsonify({'Err': error})
- except Exception as e:
- error = "network_join: subnet uuid by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- ret = neutron.show_subnet(subnet)
- gateway_ip = ret['subnet']['gateway_ip']
- if not gateway_ip:
- error = "network_join: no gateway_ip for the subnet"
- return jsonify({'Err': error})
- except Exception as e:
- error = "network_join: neutron show subnet. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- endpoint = get_endpointuuid_by_name(neutron, eid)
- if not endpoint:
- error = "network_join: Failed to get endpoint by name"
- return jsonify({'Err': error})
- except Exception as e:
- error = "network_join: neutron endpoint by name. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- ret = neutron.show_port(endpoint)
- mac_address = ret['port']['mac_address']
- except Exception as e:
- error = "network_join: neutron show port. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- veth_outside = eid[0:15]
- veth_inside = eid[0:13] + "_c"
- command = "ip link add %s type veth peer name %s" \
- % (veth_inside, veth_outside)
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_join: failed to create veth pair. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- command = "ip link set dev %s address %s" \
- % (veth_inside, mac_address)
-
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_join: failed to set veth mac address. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- command = "ip link set %s up" % (veth_outside)
-
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_join: failed to up the veth iface. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- reserved_vlan = ovs_vsctl("--if-exists", "get", "Open_vSwitch", ".",
- "external_ids:" + eid + "_vlan").strip('"')
- if not reserved_vlan:
- error = "network_join: no reserved vlan for this endpoint"
- return jsonify({'Err': error})
- ovs_vsctl("add-port", OVN_BRIDGE, veth_outside, "tag=" + reserved_vlan)
- except Exception as e:
- error = "network_join: failed to create a OVS port. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({"InterfaceName": {
- "SrcName": veth_inside,
- "DstPrefix": "eth"
- },
- "Gateway": gateway_ip,
- "GatewayIPv6": ""})
-
-
-@app.route('/NetworkDriver.Leave', methods=['POST'])
-def network_leave():
- if not request.data:
- abort(400)
-
- data = json.loads(request.data)
-
- nid = data.get("NetworkID", "")
- if not nid:
- abort(400)
-
- eid = data.get("EndpointID", "")
- if not eid:
- abort(400)
-
- veth_outside = eid[0:15]
- command = "ip link delete %s" % (veth_outside)
- try:
- call_popen(shlex.split(command))
- except Exception as e:
- error = "network_leave: failed to delete veth pair. (%s)" % (str(e))
- return jsonify({'Err': error})
-
- try:
- ovs_vsctl("--if-exists", "del-port", veth_outside)
- except Exception as e:
- error = "network_leave: Failed to delete port (%s)" % (str(e))
- return jsonify({'Err': error})
-
- return jsonify({})
-
-if __name__ == '__main__':
- prepare()
- app.run(host='127.0.0.1')
diff --git a/ovn/utilities/ovn-trace.8.xml b/ovn/utilities/ovn-trace.8.xml
deleted file mode 100644
index 01e741119..000000000
--- a/ovn/utilities/ovn-trace.8.xml
+++ /dev/null
@@ -1,485 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manpage program="ovn-trace" section="8" title="ovn-trace">
- <h1>Name</h1>
- <p>ovn-trace -- Open Virtual Network logical network tracing utility</p>
-
- <h1>Synopsis</h1>
- <p><code>ovn-trace</code> [<var>options</var>] <var>datapath</var> <var>microflow</var></p>
- <p><code>ovn-trace</code> [<var>options</var>] <code>--detach</code></p>
-
- <h1>Description</h1>
- <p>
- This utility simulates packet forwarding within an OVN logical network.
- It can be used to run through ``what-if'' scenarios: if a packet
- originates at a logical port, what will happen to it and where will it
- ultimately end up? Users already familiar with the Open vSwitch
- <code>ofproto/trace</code> command described in
- <code>ovs-vswitch</code>(8) will find <code>ovn-trace</code> to be a
- similar tool for logical networks.
- </p>
-
- <p>
- <code>ovn-trace</code> works by reading the <code>Logical_Flow</code> and
- other tables from the OVN southbound database (see
- <code>ovn-sb</code>(5)). It simulates a packet's path through logical
- networks by repeatedly looking it up in the logical flow table, following
- the entire tree of possibilities.
- </p>
-
- <p>
- <code>ovn-trace</code> simulates only the OVN logical network. It does
- not simulate the physical elements on which the logical network is
- layered. This means that, for example, it is unimportant how VMs are
- distributed among hypervisors, or whether their hypervisors are
- functioning and reachable, so <code>ovn-trace</code> will yield the same
- results regardless. There is one important exception:
- <code>ovn-northd</code>, the daemon that generates the logical flows that
- <code>ovn-trace</code> simulates, treats logical ports differently based
- on whether they are up or down. Thus, if you see surprising results,
- ensure that the ports involved in a simulation are up.
- </p>
-
- <p>
- The simplest way to use <code>ovn-trace</code> is to provide
- <var>datapath</var> and <var>microflow</var> arguments on the command
- line. In this case, it simulates the behavior of a single packet and
- exits. For an alternate usage model, see <code>Daemon Mode</code> below.
- </p>
-
- <p>
- The <var>datapath</var> argument specifies the name of a logical
- datapath. Acceptable names are the <code>name</code> from the northbound
- <code>Logical_Switch</code> or <code>Logical_Router</code> table, the
- UUID of a record from one of those tables, or the UUID of a record from
- the southbound <code>Datapath_Binding</code> table.
- </p>
-
- <p>
- The <var>microflow</var> argument describes the packet whose forwarding
- is to be simulated, in the syntax of an OVN logical expression, as
- described in <code>ovn-sb</code>(5), to express constraints. The parser
- understands prerequisites; for example, if the expression refers to
- <code>ip4.src</code>, there is no need to explicitly state
- <code>ip4</code> or <code>eth.type == 0x800</code>.
- </p>
-
- <p>
- For reasonable L2 behavior, the microflow should include at least
- <code>inport</code> and <code>eth.dst</code>, plus <code>eth.src</code>
- if port security is enabled. For example:
- </p>
- <pre>
- inport == "lp11" &amp;&amp; eth.src == 00:01:02:03:04:05 &amp;&amp; eth.dst == ff:ff:ff:ff:ff:ff
- </pre>
-
- <p>
- For reasonable L3 behavior, <var>microflow</var> should also include
- <code>ip4.src</code> and <code>ip4.dst</code> (or <code>ip6.src</code>
- and <code>ip6.dst</code>) and <code>ip.ttl</code>. For example:
- </p>
- <pre>
- inport == "lp111" &amp;&amp; eth.src == f0:00:00:00:01:11 &amp;&amp; eth.dst == 00:00:00:00:ff:11
- &amp;&amp; ip4.src == 192.168.11.1 &amp;&amp; ip4.dst == 192.168.22.2 &amp;&amp; ip.ttl == 64
- </pre>
-
- <p>Here's an ARP microflow example:</p>
- <pre>
- inport == "lp123"
- &amp;&amp; eth.dst == ff:ff:ff:ff:ff:ff &amp;&amp; eth.src == f0:00:00:00:01:11
- &amp;&amp; arp.op == 1 &amp;&amp; arp.sha == f0:00:00:00:01:11 &amp;&amp; arp.spa == 192.168.1.11
- &amp;&amp; arp.tha == ff:ff:ff:ff:ff:ff &amp;&amp; arp.tpa == 192.168.2.22
- </pre>
-
- <p>
- <code>ovn-trace</code> will reject erroneous microflow expressions, which
- beyond syntax errors fall into two categories. First, they can be
- ambiguous. For example, <code>tcp.src == 80</code> is ambiguous because
- it does not state IPv4 or IPv6 as the Ethernet type. <code>ip4
- &amp;&amp; tcp.src > 1024</code> is also ambiguous because it does not
- constrain bits of <code>tcp.src</code> to particular values. Second,
- they can be contradictory, e.g. <code>ip4 &amp;&amp; ip6</code>.
- </p>
-
- <h1>Output</h1>
-
- <p>
- <code>ovn-trace</code> supports the three different forms of output, each
- described in a separate section below. Regardless of the selected output
- format, <code>ovn-trace</code> starts the output with a line that shows
- the microflow being traced in OpenFlow syntax.
- </p>
-
- <h2>Detailed Output</h2>
-
- <p>
- The detailed form of output is also the default form. This form groups
- output into sections headed up by the ingress or egress pipeline being
- traversed. Each pipeline lists each table that was visited (by number and
- name), the <code>ovn-northd</code> source file and line number of the code
- that added the flow, the match expression and priority of the logical flow
- that was matched, and the actions that were executed.
- </p>
-
- <p>
- The execution of OVN logical actions naturally forms a ``control stack''
- that resembles that of a program in conventional programming languages
- such as C or Java. Because the <code>next</code> action that calls into
- another logical flow table for a lookup is a recursive construct, OVN
- ``programs'' in practice tend to form deep control stacks that, displayed
- in the obvious way using additional indentation for each level, quickly
- use up the horizontal space on all but the widest displays. To make
- detailed output more readable, without loss of generality,
- <code>ovn-trace</code> omits indentation for ``tail recursion,'' that is,
- when <code>next</code> is the last action in a logical flow, it does not
- indent details of the next table lookup more deeply. Output still uses
- indentation when it is needed for clarity.
- </p>
-
- <p>
- OVN ``programs'' traces also tend to encounter long strings of logical
- flows with match expression <code>1</code> (which matches every packet)
- and the single action <code>next;</code>. These are uninteresting
- and merely clutter output, so <code>ovn-trace</code> omits them
- entirely even from detailed output.
- </p>
-
- <p>
- The following excerpt from detailed <code>ovn-trace</code> output shows a
- section for a packet traversing the ingress pipeline of logical datapath
- <code>ls1</code> with ingress logical port <code>lp111</code>. The
- packet matches a logical flow in table 0 (aka
- <code>ls_in_port_sec_l2</code>) with priority 50 and executes
- <code>next(1);</code> to pass to table 1. Tables 1 through 11 are
- trivial and omitted. In table 12 (aka <code>ls_in_l2_lkup</code>), the
- packet matches a flow with priority 50 based on its Ethernet destination
- address and the flow's actions output the packet to the
- <code>lrp11-attachement</code> logical port.
- </p>
-
- <pre fixed="yes">
- ingress(dp="ls1", inport="lp111")
- ---------------------------------
- 0. ls_in_port_sec_l2: inport == "lp111", priority 50
- next(1);
- 12. ls_in_l2_lkup: eth.dst == 00:00:00:00:ff:11, priority 50
- outport = "lrp11-attachment";
- output;
- </pre>
-
- <h2>Summary Output</h2>
-
- <p>
- Summary output includes the logical pipelines visited by a packet and the
- logical actions executed on it. Compared to the detailed output,
- however, it removes details of tables and logical flows traversed by a
- packet. It uses a format closer to that of a programming language and
- does not attempt to avoid indentation. The summary output equivalent to
- the above detailed output fragment is:
- </p>
-
- <pre fixed="yes">
- ingress(dp="ls1", inport="lp111") {
- outport = "lrp11-attachment";
- output;
- ...
- };
- </pre>
-
- <h2>Minimal Output</h2>
-
- <p>
- Minimal output includes only actions that modify packet data (not
- including OVN registers or metadata such as <code>outport</code>) and
- <code>output</code> actions that actually deliver a packet to a logical
- port (excluding patch ports). The operands of actions that modify packet
- data are displayed reduced to constants, e.g. <code>ip4.dst =
- reg0;</code> might be show as <code>ip4.dst = 192.168.0.1;</code> if that
- was the value actually loaded. This yields output even simpler than the
- summary format. (Users familiar with Open vSwitch may recognize this as
- similar in spirit to the datapath actions listed at the bottom of
- <code>ofproto/trace</code> output.)
- </p>
-
- <p>
- The minimal output format reflects the externally seen behavior of the
- logical networks more than it does the implementation. This makes this
- output format the most suitable for use in regression tests, because it
- is least likely to change when logical flow tables are rearranged without
- semantic change.
- </p>
-
- <h1>Stateful Actions</h1>
-
- <p>
- Some OVN logical actions use or update state that is not available in the
- southbound database. <code>ovn-trace</code> handles these actions as
- described below:
- </p>
-
- <dl>
- <dt><code>ct_next</code></dt>
- <dd>
- By default <code>ovn-trace</code> treats flows as ``tracked'' and
- ``established.'' See the description of the <code>--ct</code> option for
- a way to override this behavior.
- </dd>
-
- <dt><code>ct_dnat</code> (without an argument)</dt>
- <dd>
- Forks the pipeline. In one fork, advances to the next table as if
- <code>next;</code> were executed. The packet is not changed, on the
- assumption that no NAT state was available. In the other fork, the
- pipeline continues without change after the <code>ct_dnat</code> action.
- </dd>
-
- <dt><code>ct_snat</code> (without an argument)</dt>
- <dd>
- This action distinguishes between gateway routers and distributed
- routers. A gateway router is defined as a logical datapath that contains
- an <code>l3gateway</code> port; any other logical datapath is a
- distributed router. On a gateway router, <code>ct_snat;</code> is
- treated as a no-op. On a distributed router, it is treated the same way
- as <code>ct_dnat;</code>.
- </dd>
-
- <dt><code>ct_dnat(<var>ip</var>)</code></dt>
- <dt><code>ct_snat(<var>ip</var>)</code></dt>
- <dd>
- Forks the pipeline. In one fork, sets <code>ip4.dst</code> (or
- <code>ip4.src</code>) to <var>ip</var> and <code>ct.dnat</code> (or
- <code>ct.snat</code>) to 1 and advances to the next table as if
- <code>next;</code> were executed. In the other fork, the pipeline
- continues without change after the <code>ct_dnat</code> (or
- <code>ct_snat</code>) action.
- </dd>
-
- <dt><code>ct_lb;</code></dt>
- <dt><code>ct_lb(<var>ip</var></code>[<code>:<var>port</var></code>]...<code>);</code></dt>
- <dd>
- Forks the pipeline. In one fork, sets <code>ip4.dst</code> (or
- <code>ip6.dst</code>) to one of the load-balancer addresses and the
- destination port to its associated port, if any, and sets
- <code>ct.dnat</code> to 1. With one or more arguments, gives preference
- to the address specified on <code>--lb-dst</code>, if any; without
- arguments, uses the address and port specified on <code>--lb-dst</code>.
- In the other fork, the pipeline continues without change after the
- <code>ct_lb</code> action.
- </dd>
-
- <dt><code>ct_commit</code></dt>
- <dt><code>put_arp</code></dt>
- <dt><code>put_nd</code></dt>
- <dd>
- These actions are treated as no-ops.
- </dd>
- </dl>
-
- <h1>Daemon Mode</h1>
-
- <p>
- If <code>ovn-trace</code> is invoked with the <code>--detach</code> option
- (see <code>Daemon Options</code>, below), it runs in the background as a
- daemon and accepts commands from <code>ovs-appctl</code> (or another
- JSON-RPC client) indefinitely. The currently supported commands are
- described below.
- </p>
-
- <p>
-
- </p>
-
- <dl>
- <dt><code>trace</code> [<var>options</var>] <var>datapath</var> <var>microflow</var></dt>
- <dd>
- Traces <var>microflow</var> through <var>datapath</var> and replies with
- the results of the trace. Accepts the <var>options</var> described under
- <code>Trace Options</code> below.
- </dd>
-
- <dt><code>exit</code></dt>
- <dd>Causes <code>ovn-trace</code> to gracefully terminate.</dd>
- </dl>
-
- <h1>Options</h1>
-
- <h2>Trace Options</h2>
-
- <dl>
- <dt><code>--detailed</code></dt>
- <dt><code>--summary</code></dt>
- <dt><code>--minimal</code></dt>
- <dd>
- These options control the form and level of detail in
- <code>ovn-trace</code> output. If more than one of these options is
- specified, all of the selected forms are output, in the order listed
- above, each headed by a banner line. If none of these options is
- given, <code>--detailed</code> is the default. See
- <code>Output</code>, above, for a description of each kind of output.
- </dd>
-
- <dt><code>--all</code></dt>
- <dd>
- Selects all three forms of output.
- </dd>
-
- <dt><code>--ovs</code>[<code>=</code><var>remote</var>]</dt>
- <dd>
- <p>
- Makes <code>ovn-trace</code> attempt to obtain and display the OpenFlow
- flows that correspond to each OVN logical flow. To do so,
- <code>ovn-trace</code> connects to <var>remote</var> (by default,
- <code>unix:@RUNDIR@/br-int.mgmt</code>) over OpenFlow and retrieves the
- flows. If <var>remote</var> is specified, it must be an active
- OpenFlow connection method described in <code>ovsdb</code>(7).
- </p>
-
- <p>
- To make the best use of the output, it is important to understand the
- relationship between logical flows and OpenFlow flows.
- <code>ovn-architecture</code>(7), under <em>Architectural Physical Life
- Cycle of a Packet</em>, describes this relationship. Keep in mind the
- following points:
- </p>
-
- <ul>
- <li>
- <code>ovn-trace</code> currently shows all the OpenFlow flows to
- which a logical flow corresponds, even though an actual packet
- ordinarily matches only one of these.
- </li>
-
- <li>
- Some logical flows can map to the Open vSwitch ``conjunctive match''
- extension (see <code>ovs-fields</code>(7)). Currently
- <code>ovn-trace</code> cannot display the flows with
- <code>conjunction</code> actions that effectively produce the
- <code>conj_id</code> match.
- </li>
-
- <li>
- Some logical flows may not be represented in the OpenFlow tables on a
- given hypervisor, if they could not be used on that hypervisor.
- </li>
-
- <li>
- Some OpenFlow flows do not correspond to logical flows, such as
- OpenFlow flows that map between physical and logical ports. These
- flows will never show up in a trace.
- </li>
-
- <li>
- When <code>ovn-trace</code> omits uninteresting logical flows from
- output, it does not look up the corresponding OpenFlow flows.
- </li>
- </ul>
- </dd>
-
- <dt><code>--ct=<var>flags</var></code></dt>
- <dd>
- <p>
- This option sets the <code>ct_state</code> flags that a
- <code>ct_next</code> logical action will report. The <var>flags</var>
- must be a comma- or space-separated list of the following connection
- tracking flags:
- </p>
-
- <ul>
- <li>
- <code>trk</code>: Include to indicate connection tracking has taken
- place. (This bit is set automatically even if not listed in
- <var>flags</var>.
- </li>
- <li><code>new</code>: Include to indicate a new flow.</li>
- <li><code>est</code>: Include to indicate an established flow.</li>
- <li><code>rel</code>: Include to indicate a related flow.</li>
- <li><code>rpl</code>: Include to indicate a reply flow.</li>
- <li><code>inv</code>: Include to indicate a connection entry in a
- bad state.</li>
- <li><code>dnat</code>: Include to indicate a packet whose
- destination IP address has been changed.</li>
- <li><code>snat</code>: Include to indicate a packet whose source IP
- address has been changed.</li>
- </ul>
-
- <p>
- The <code>ct_next</code> action is used to implement the OVN
- distributed firewall. For testing, useful flag combinations include:
- </p>
-
- <ul>
- <li><code>trk,new</code>: A packet in a flow in either direction
- through a firewall that has not yet been committed (with
- <code>ct_commit</code>).</li>
- <li><code>trk,est</code>: A packet in an established flow going out
- through a firewall.</li>
- <li><code>trk,rpl</code>: A packet coming in through a firewall in
- reply to an established flow.</li>
- <li><code>trk,inv</code>: An invalid packet in either direction.</li>
- </ul>
-
- <p>
- A packet might pass through the connection tracker twice in one trip
- through OVN: once following egress from a VM as it passes outward
- through a firewall, and once preceding ingress to a second VM as it
- passes inward through a firewall. Use multiple <code>--ct</code>
- options to specify the flags for multiple <code>ct_next</code> actions.
- </p>
-
- <p>
- When <code>--ct</code> is unspecified, or when there are fewer
- <code>--ct</code> options than <code>ct_next</code> actions, the
- <var>flags</var> default to <code>trk,est</code>.
- </p>
- </dd>
-
- <dt><code>--lb-dst=</code><var>ip</var>[<code>:<var>port</var></code>]</dt>
- <dd>
- Sets the IP from VIP pool to use as destination of the packet.
- <code>--lb-dst</code> is not available in daemon mode.
- </dd>
-
- <dt><code>--friendly-names</code></dt>
- <dt><code>--no-friendly-names</code></dt>
- <dd>
- When cloud management systems such as OpenStack are layered on top of
- OVN, they often use long, human-unfriendly names for ports and datapaths,
- for example, ones that include entire UUIDs. They do usually include
- friendlier names, but the long, hard-to-read names are the ones that
- appear in matches and actions. By default, or with
- <code>--friendly-names</code>, <code>ovn-trace</code> substitutes these
- friendlier names for the long names in its output. Use
- <code>--no-friendly-names</code> to disable this behavior; this option
- might be useful, for example, if a program is going to parse
- <code>ovn-trace</code> output.
- </dd>
- </dl>
-
- <h2>Daemon Options</h2>
- <xi:include href="lib/daemon.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>Logging Options</h2>
- <xi:include href="lib/vlog.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>PKI Options</h2>
- <p>
- PKI configuration is required to use SSL for the connection to the
- database (and the switch, if <code>--ovs</code> is specified).
- </p>
- <xi:include href="lib/ssl.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
- <h2>Other Options</h2>
-
- <dl>
- <dt><code>--db</code> <var>database</var></dt>
- <dd>
- The OVSDB database remote to contact. If the <env>OVN_SB_DB</env>
- environment variable is set, its value is used as the default.
- Otherwise, the default is <code>unix:@RUNDIR@/db.sock</code>, but this
- default is unlikely to be useful outside of single-machine OVN test
- environments.
- </dd>
- </dl>
-
- <xi:include href="lib/common.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
-
-</manpage>
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
deleted file mode 100644
index 044eb1cc2..000000000
--- a/ovn/utilities/ovn-trace.c
+++ /dev/null
@@ -1,2373 +0,0 @@
-/*
- * Copyright (c) 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-
-#include <getopt.h>
-
-#include "command-line.h"
-#include "compiler.h"
-#include "daemon.h"
-#include "dirs.h"
-#include "fatal-signal.h"
-#include "flow.h"
-#include "nx-match.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/json.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofp-flow.h"
-#include "openvswitch/ofp-print.h"
-#include "openvswitch/vconn.h"
-#include "openvswitch/vlog.h"
-#include "ovn/actions.h"
-#include "ovn/expr.h"
-#include "ovn/lex.h"
-#include "ovn/logical-fields.h"
-#include "ovn/lib/acl-log.h"
-#include "ovn/lib/ovn-l7.h"
-#include "ovn/lib/ovn-sb-idl.h"
-#include "ovn/lib/ovn-util.h"
-#include "ovsdb-idl.h"
-#include "openvswitch/poll-loop.h"
-#include "stream-ssl.h"
-#include "stream.h"
-#include "unixctl.h"
-#include "util.h"
-#include "random.h"
-
-VLOG_DEFINE_THIS_MODULE(ovntrace);
-
-/* --db: The database server to contact. */
-static const char *db;
-
-/* --unixctl-path: Path to use for unixctl server, for "monitor" and "snoop"
- commands. */
-static char *unixctl_path;
-
-/* The southbound database. */
-static struct ovsdb_idl *ovnsb_idl;
-
-/* --detailed: Show a detailed, table-by-table trace. */
-static bool detailed;
-
-/* --summary: Show a trace that omits table information. */
-static bool summary;
-
-/* --minimal: Show a trace with only minimal information. */
-static bool minimal;
-
-/* --ovs: OVS instance to contact to get OpenFlow flows. */
-static const char *ovs;
-static struct vconn *vconn;
-
-/* --ct: Connection tracking state to use for ct_next() actions. */
-static uint32_t *ct_states;
-static size_t n_ct_states;
-static size_t ct_state_idx;
-
-/* --lb-dst: load balancer destination info. */
-static struct ovnact_ct_lb_dst lb_dst;
-
-/* --friendly-names, --no-friendly-names: Whether to substitute human-friendly
- * port and datapath names for the awkward UUIDs typically used in the actual
- * logical flows. */
-static bool use_friendly_names = true;
-
-OVS_NO_RETURN static void usage(void);
-static void parse_options(int argc, char *argv[]);
-static char *trace(const char *datapath, const char *flow);
-static void read_db(void);
-static unixctl_cb_func ovntrace_exit;
-static unixctl_cb_func ovntrace_trace;
-
-int
-main(int argc, char *argv[])
-{
- set_program_name(argv[0]);
- service_start(&argc, &argv);
- fatal_ignore_sigpipe();
- vlog_set_levels_from_string_assert("reconnect:warn");
-
- /* Parse command line. */
- parse_options(argc, argv);
- argc -= optind;
- argv += optind;
-
- if (get_detach()) {
- if (argc != 0) {
- ovs_fatal(0, "non-option arguments not supported with --detach "
- "(use --help for help)");
- }
- } else {
- if (argc != 2) {
- ovs_fatal(0, "exactly two non-option arguments are required "
- "(use --help for help)");
- }
- }
-
- struct unixctl_server *server = NULL;
- bool exiting = false;
- if (get_detach()) {
- daemonize_start(false);
- int error = unixctl_server_create(unixctl_path, &server);
- if (error) {
- ovs_fatal(error, "failed to create unixctl server");
- }
- unixctl_command_register("exit", "", 0, 0, ovntrace_exit, &exiting);
- unixctl_command_register("trace", "[OPTIONS] DATAPATH MICROFLOW",
- 2, INT_MAX, ovntrace_trace, NULL);
- }
- ovnsb_idl = ovsdb_idl_create(db, &sbrec_idl_class, true, false);
-
- bool already_read = false;
- for (;;) {
- ovsdb_idl_run(ovnsb_idl);
- unixctl_server_run(server);
- if (!ovsdb_idl_is_alive(ovnsb_idl)) {
- int retval = ovsdb_idl_get_last_error(ovnsb_idl);
- ovs_fatal(0, "%s: database connection failed (%s)",
- db, ovs_retval_to_string(retval));
- }
-
- if (ovsdb_idl_has_ever_connected(ovnsb_idl)) {
- if (!already_read) {
- already_read = true;
- read_db();
- }
-
- daemonize_complete();
- if (!get_detach()) {
- char *output = trace(argv[0], argv[1]);
- fputs(output, stdout);
- free(output);
- return 0;
- }
- }
-
- if (exiting) {
- break;
- }
- ovsdb_idl_wait(ovnsb_idl);
- unixctl_server_wait(server);
- poll_block();
- }
-}
-
-static char *
-default_ovs(void)
-{
- return xasprintf("unix:%s/br-int.mgmt", ovs_rundir());
-}
-
-static void
-parse_ct_option(const char *state_s_)
-{
- uint32_t state;
- struct ds ds = DS_EMPTY_INITIALIZER;
-
- if (!parse_ct_state(state_s_, CS_TRACKED, &state, &ds)) {
- ovs_fatal(0, "%s", ds_cstr(&ds));
- }
- if (!validate_ct_state(state, &ds)) {
- VLOG_WARN("%s", ds_cstr(&ds));
- }
- ds_destroy(&ds);
-
- ct_states = xrealloc(ct_states, (n_ct_states + 1) * sizeof *ct_states);
- ct_states[n_ct_states++] = state;
-}
-
-static void
-parse_lb_option(const char *s)
-{
- struct sockaddr_storage ss;
- if (!inet_parse_active(s, 0, &ss, false)) {
- ovs_fatal(0, "%s: bad address", s);
- }
-
- lb_dst.family = ss.ss_family;
- struct in6_addr a = ss_get_address(&ss);
- if (ss.ss_family == AF_INET) {
- lb_dst.ipv4 = in6_addr_get_mapped_ipv4(&a);
- } else {
- lb_dst.ipv6 = a;
- }
- lb_dst.port = ss_get_port(&ss);
-}
-
-static void
-parse_options(int argc, char *argv[])
-{
- enum {
- OPT_DB = UCHAR_MAX + 1,
- OPT_UNIXCTL,
- OPT_DETAILED,
- OPT_SUMMARY,
- OPT_MINIMAL,
- OPT_ALL,
- OPT_OVS,
- OPT_CT,
- OPT_FRIENDLY_NAMES,
- OPT_NO_FRIENDLY_NAMES,
- DAEMON_OPTION_ENUMS,
- SSL_OPTION_ENUMS,
- VLOG_OPTION_ENUMS,
- OPT_LB_DST
- };
- static const struct option long_options[] = {
- {"db", required_argument, NULL, OPT_DB},
- {"unixctl", required_argument, NULL, OPT_UNIXCTL},
- {"detailed", no_argument, NULL, OPT_DETAILED},
- {"summary", no_argument, NULL, OPT_SUMMARY},
- {"minimal", no_argument, NULL, OPT_MINIMAL},
- {"all", no_argument, NULL, OPT_ALL},
- {"ovs", optional_argument, NULL, OPT_OVS},
- {"ct", required_argument, NULL, OPT_CT},
- {"friendly-names", no_argument, NULL, OPT_FRIENDLY_NAMES},
- {"no-friendly-names", no_argument, NULL, OPT_NO_FRIENDLY_NAMES},
- {"help", no_argument, NULL, 'h'},
- {"version", no_argument, NULL, 'V'},
- {"lb-dst", required_argument, NULL, OPT_LB_DST},
- DAEMON_LONG_OPTIONS,
- VLOG_LONG_OPTIONS,
- STREAM_SSL_LONG_OPTIONS,
- {NULL, 0, NULL, 0},
- };
- char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
-
- for (;;) {
- int idx;
- int c;
-
- c = getopt_long(argc, argv, short_options, long_options, &idx);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case OPT_DB:
- db = optarg;
- break;
-
- case OPT_UNIXCTL:
- unixctl_path = optarg;
- break;
-
- case OPT_DETAILED:
- detailed = true;
- break;
-
- case OPT_SUMMARY:
- summary = true;
- break;
-
- case OPT_MINIMAL:
- minimal = true;
- break;
-
- case OPT_ALL:
- detailed = summary = minimal = true;
- break;
-
- case OPT_OVS:
- ovs = optarg ? optarg : default_ovs();
- break;
-
- case OPT_CT:
- parse_ct_option(optarg);
- break;
-
- case OPT_FRIENDLY_NAMES:
- use_friendly_names = true;
- break;
-
- case OPT_NO_FRIENDLY_NAMES:
- use_friendly_names = false;
- break;
-
- case OPT_LB_DST:
- parse_lb_option(optarg);
- break;
-
- case 'h':
- usage();
-
- case 'V':
- ovs_print_version(0, 0);
- printf("DB Schema %s\n", sbrec_get_db_version());
- exit(EXIT_SUCCESS);
-
- DAEMON_OPTION_HANDLERS
- VLOG_OPTION_HANDLERS
- STREAM_SSL_OPTION_HANDLERS
-
- case '?':
- exit(EXIT_FAILURE);
-
- default:
- abort();
- }
- }
- free(short_options);
-
- if (!db) {
- db = default_sb_db();
- }
-
- if (!detailed && !summary && !minimal) {
- detailed = true;
- }
-}
-
-static void
-usage(void)
-{
- printf("\
-%s: OVN trace utility\n\
-usage: %s [OPTIONS] DATAPATH MICROFLOW\n\
- %s [OPTIONS] --detach\n\
-\n\
-Output format options:\n\
- --detailed table-by-table \"backtrace\" (default)\n\
- --summary less detailed, more parseable\n\
- --minimal minimum to explain externally visible behavior\n\
- --all provide all forms of output\n\
-Output style options:\n\
- --no-friendly-names do not substitute human friendly names for UUIDs\n",
- program_name, program_name, program_name);
- daemon_usage();
- vlog_usage();
- printf("\n\
-Other options:\n\
- --db=DATABASE connect to DATABASE\n\
- (default: %s)\n\
- --ovs[=REMOTE] obtain corresponding OpenFlow flows from REMOTE\n\
- (default: %s)\n\
- --unixctl=SOCKET set control socket name\n\
- -h, --help display this help message\n\
- -V, --version display version information\n",
- default_sb_db(), default_ovs());
- stream_usage("database", true, true, false);
- vconn_usage(true, false, false);
- exit(EXIT_SUCCESS);
-}
-
-struct ovntrace_datapath {
- struct hmap_node sb_uuid_node;
- struct uuid sb_uuid;
- struct uuid nb_uuid;
- char *name;
- char *name2;
- char *friendly_name;
- uint32_t tunnel_key;
-
- struct ovs_list mcgroups; /* Contains "struct ovntrace_mcgroup"s. */
-
- struct ovntrace_flow **flows;
- size_t n_flows, allocated_flows;
-
- struct hmap mac_bindings; /* Contains "struct ovntrace_mac_binding"s. */
-
- bool has_local_l3gateway;
-};
-
-struct ovntrace_port {
- struct ovntrace_datapath *dp;
- struct uuid uuid;
- char *name;
- char *name2;
- const char *friendly_name;
- char *type;
- uint16_t tunnel_key;
- struct ovntrace_port *peer; /* Patch ports only. */
- struct ovntrace_port *distributed_port; /* chassisredirect ports only. */
-};
-
-struct ovntrace_mcgroup {
- struct ovs_list list_node; /* In struct ovntrace_datapath's 'mcgroups'. */
-
- struct ovntrace_datapath *dp;
- char *name;
-
- uint16_t tunnel_key;
-
- struct ovntrace_port **ports;
- size_t n_ports;
-};
-
-struct ovntrace_flow {
- struct uuid uuid;
- enum ovnact_pipeline pipeline;
- int table_id;
- char *stage_name;
- char *source;
- int priority;
- char *match_s;
- struct expr *match;
- struct ovnact *ovnacts;
- size_t ovnacts_len;
-};
-
-struct ovntrace_mac_binding {
- struct hmap_node node;
- uint16_t port_key;
- struct in6_addr ip;
- struct eth_addr mac;
-};
-
-static inline uint32_t
-hash_mac_binding(uint16_t port_key, const struct in6_addr *ip)
-{
- return hash_bytes(ip, sizeof *ip, port_key);
-}
-
-/* Every ovntrace_datapath, by southbound Datapath_Binding record UUID. */
-static struct hmap datapaths;
-
-/* Every ovntrace_port, by name. */
-static struct shash ports;
-
-/* Symbol table for expressions and actions. */
-static struct shash symtab;
-
-/* Address sets. */
-static struct shash address_sets;
-
-/* Port groups. */
-static struct shash port_groups;
-
-/* DHCP options. */
-static struct hmap dhcp_opts; /* Contains "struct gen_opts_map"s. */
-static struct hmap dhcpv6_opts; /* Contains "struct gen_opts_map"s. */
-static struct hmap nd_ra_opts; /* Contains "struct gen_opts_map"s. */
-
-static struct ovntrace_datapath *
-ovntrace_datapath_find_by_sb_uuid(const struct uuid *sb_uuid)
-{
- struct ovntrace_datapath *dp;
- HMAP_FOR_EACH_WITH_HASH (dp, sb_uuid_node, uuid_hash(sb_uuid),
- &datapaths) {
- if (uuid_equals(&dp->sb_uuid, sb_uuid)) {
- return dp;
- }
- }
- return NULL;
-}
-
-static const struct ovntrace_datapath *
-ovntrace_datapath_find_by_name(const char *name)
-{
- struct ovntrace_datapath *dp;
- HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
- if (!strcmp(name, dp->name)
- || (dp->name2 && !strcmp(name, dp->name2))) {
- return dp;
- }
- }
-
- struct ovntrace_datapath *match = NULL;
- HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
- if (uuid_is_partial_match(&dp->sb_uuid, name) >= 4 ||
- uuid_is_partial_match(&dp->nb_uuid, name) >= 4) {
- if (match) {
- VLOG_WARN("name \"%s\" matches multiple datapaths", name);
- return NULL;
- }
- match = dp;
- }
- }
- return match;
-}
-
-static const struct ovntrace_port *
-ovntrace_port_find_by_key(const struct ovntrace_datapath *dp,
- uint16_t tunnel_key)
-{
- const struct shash_node *node;
- SHASH_FOR_EACH (node, &ports) {
- const struct ovntrace_port *port = node->data;
- if (port->dp == dp && port->tunnel_key == tunnel_key) {
- return port;
- }
- }
- return NULL;
-}
-
-static const char *
-ovntrace_port_key_to_name(const struct ovntrace_datapath *dp,
- uint16_t key)
-{
- const struct ovntrace_port *port = ovntrace_port_find_by_key(dp, key);
- return (port ? port->friendly_name
- : !key ? ""
- : "(unnamed)");
-}
-
-static const struct ovntrace_mcgroup *
-ovntrace_mcgroup_find_by_key(const struct ovntrace_datapath *dp,
- uint16_t tunnel_key)
-{
- const struct ovntrace_mcgroup *mcgroup;
- LIST_FOR_EACH (mcgroup, list_node, &dp->mcgroups) {
- if (mcgroup->tunnel_key == tunnel_key) {
- return mcgroup;
- }
- }
- return NULL;
-}
-
-static const struct ovntrace_mcgroup *
-ovntrace_mcgroup_find_by_name(const struct ovntrace_datapath *dp,
- const char *name)
-{
- const struct ovntrace_mcgroup *mcgroup;
- LIST_FOR_EACH (mcgroup, list_node, &dp->mcgroups) {
- if (!strcmp(mcgroup->name, name)) {
- return mcgroup;
- }
- }
- return NULL;
-}
-
-static const struct ovntrace_mac_binding *
-ovntrace_mac_binding_find(const struct ovntrace_datapath *dp,
- uint16_t port_key, const struct in6_addr *ip)
-{
- const struct ovntrace_mac_binding *bind;
- HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip),
- &dp->mac_bindings) {
- if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip)) {
- return bind;
- }
- }
- return NULL;
-}
-
-/* If 's' ends with a UUID, returns a copy of it with the UUID truncated to
- * just the first 6 characters; otherwise, returns a copy of 's'. */
-static char *
-shorten_uuid(const char *s)
-{
- size_t len = strlen(s);
- return (len >= UUID_LEN && uuid_is_partial_string(s + (len - UUID_LEN))
- ? xmemdup0(s, len - (UUID_LEN - 6))
- : xstrdup(s));
-}
-
-static void
-read_datapaths(void)
-{
- hmap_init(&datapaths);
- const struct sbrec_datapath_binding *sbdb;
- SBREC_DATAPATH_BINDING_FOR_EACH (sbdb, ovnsb_idl) {
- struct ovntrace_datapath *dp = xzalloc(sizeof *dp);
- const struct smap *ids = &sbdb->external_ids;
-
- dp->sb_uuid = sbdb->header_.uuid;
- if (!smap_get_uuid(ids, "logical-switch", &dp->nb_uuid) &&
- !smap_get_uuid(ids, "logical-router", &dp->nb_uuid)) {
- dp->nb_uuid = dp->sb_uuid;
- }
-
- const char *name = smap_get(ids, "name");
- dp->name = (name
- ? xstrdup(name)
- : xasprintf(UUID_FMT, UUID_ARGS(&dp->nb_uuid)));
-
- dp->name2 = nullable_xstrdup(smap_get(ids, "name2"));
- dp->friendly_name = (!use_friendly_names
- ? xasprintf(UUID_FMT, UUID_ARGS(&dp->nb_uuid))
- : shorten_uuid(dp->name2 ? dp->name2 : dp->name));
-
- dp->tunnel_key = sbdb->tunnel_key;
-
- ovs_list_init(&dp->mcgroups);
- hmap_init(&dp->mac_bindings);
-
- hmap_insert(&datapaths, &dp->sb_uuid_node, uuid_hash(&dp->sb_uuid));
- }
-}
-
-static void
-read_ports(void)
-{
- shash_init(&ports);
- const struct sbrec_port_binding *sbpb;
- SBREC_PORT_BINDING_FOR_EACH (sbpb, ovnsb_idl) {
- const char *port_name = sbpb->logical_port;
- struct ovntrace_datapath *dp
- = ovntrace_datapath_find_by_sb_uuid(&sbpb->datapath->header_.uuid);
- if (!dp) {
- VLOG_WARN("logical port %s missing datapath", port_name);
- continue;
- }
-
- struct ovntrace_port *port = xzalloc(sizeof *port);
- if (!shash_add_once(&ports, port_name, port)) {
- VLOG_WARN("duplicate logical port name %s", port_name);
- free(port);
- continue;
- }
- port->dp = dp;
- port->uuid = sbpb->header_.uuid;
- port->name = xstrdup(port_name);
- port->type = xstrdup(sbpb->type);
- port->tunnel_key = sbpb->tunnel_key;
-
- port->name2 = nullable_xstrdup(smap_get(&sbpb->external_ids, "name"));
- port->friendly_name = (!use_friendly_names ? xstrdup(port->name)
- : shorten_uuid(port->name2
- ? port->name2 : port->name));
-
- if (!strcmp(sbpb->type, "patch")) {
- const char *peer_name = smap_get(&sbpb->options, "peer");
- if (peer_name) {
- struct ovntrace_port *peer
- = shash_find_data(&ports, peer_name);
- if (peer) {
- port->peer = peer;
- port->peer->peer = port;
- }
- }
- } else if (!strcmp(sbpb->type, "l3gateway")) {
- /* Treat all gateways as local for our purposes. */
- dp->has_local_l3gateway = true;
- const char *peer_name = smap_get(&sbpb->options, "peer");
- if (peer_name) {
- struct ovntrace_port *peer
- = shash_find_data(&ports, peer_name);
- if (peer) {
- port->peer = peer;
- port->peer->peer = port;
- }
- }
- }
- }
-
- SBREC_PORT_BINDING_FOR_EACH (sbpb, ovnsb_idl) {
- if (!strcmp(sbpb->type, "chassisredirect")) {
- struct ovntrace_port *port
- = shash_find_data(&ports, sbpb->logical_port);
- if (port) {
- const char *distributed_name = smap_get(&sbpb->options,
- "distributed-port");
- if (distributed_name) {
- struct ovntrace_port *distributed_port
- = shash_find_data(&ports, distributed_name);
- if (distributed_port && distributed_port->dp == port->dp) {
- port->distributed_port = distributed_port;
- }
- }
- }
- }
- }
-}
-
-static int
-compare_port(const void *a_, const void *b_)
-{
- struct ovntrace_port *const *ap = a_;
- struct ovntrace_port *const *bp = b_;
- const struct ovntrace_port *a = *ap;
- const struct ovntrace_port *b = *bp;
-
- return strcmp(a->name, b->name);
-}
-
-static void
-read_mcgroups(void)
-{
- const struct sbrec_multicast_group *sbmg;
- SBREC_MULTICAST_GROUP_FOR_EACH (sbmg, ovnsb_idl) {
- struct ovntrace_datapath *dp
- = ovntrace_datapath_find_by_sb_uuid(&sbmg->datapath->header_.uuid);
- if (!dp) {
- VLOG_WARN("logical multicast group %s missing datapath",
- sbmg->name);
- continue;
- }
-
- struct ovntrace_mcgroup *mcgroup = xzalloc(sizeof *mcgroup);
- ovs_list_push_back(&dp->mcgroups, &mcgroup->list_node);
- mcgroup->dp = dp;
- mcgroup->tunnel_key = sbmg->tunnel_key;
- mcgroup->name = xstrdup(sbmg->name);
- mcgroup->ports = xmalloc(sbmg->n_ports * sizeof *mcgroup->ports);
- for (size_t i = 0; i < sbmg->n_ports; i++) {
- const char *port_name = sbmg->ports[i]->logical_port;
- struct ovntrace_port *p = shash_find_data(&ports, port_name);
- if (!p) {
- VLOG_WARN("missing port %s", port_name);
- continue;
- }
- if (!uuid_equals(&sbmg->ports[i]->datapath->header_.uuid,
- &p->dp->sb_uuid)) {
- VLOG_WARN("multicast group %s in datapath %s contains "
- "port %s outside that datapath",
- mcgroup->name, mcgroup->dp->name, port_name);
- continue;
- }
- mcgroup->ports[mcgroup->n_ports++] = p;
- }
-
- /* Sort the ports in alphabetical order to make output more
- * predictable. */
- qsort(mcgroup->ports, mcgroup->n_ports, sizeof *mcgroup->ports,
- compare_port);
- }
-}
-
-static void
-read_address_sets(void)
-{
- shash_init(&address_sets);
-
- const struct sbrec_address_set *sbas;
- SBREC_ADDRESS_SET_FOR_EACH (sbas, ovnsb_idl) {
- expr_const_sets_add(&address_sets, sbas->name,
- (const char *const *) sbas->addresses,
- sbas->n_addresses, true);
- }
-}
-
-static void
-read_port_groups(void)
-{
- shash_init(&port_groups);
-
- const struct sbrec_port_group *sbpg;
- SBREC_PORT_GROUP_FOR_EACH (sbpg, ovnsb_idl) {
- expr_const_sets_add(&port_groups, sbpg->name,
- (const char *const *) sbpg->ports,
- sbpg->n_ports, false);
- }
-}
-
-static bool
-ovntrace_is_chassis_resident(const void *aux OVS_UNUSED,
- const char *port_name)
-{
- if (port_name[0] == '\0') {
- return true;
- }
-
- const struct ovntrace_port *port = shash_find_data(&ports, port_name);
- if (port) {
- /* Since ovntrace is not chassis specific, assume any port
- * that exists is resident. */
- return true;
- }
-
- VLOG_WARN("%s: unknown logical port\n", port_name);
- return false;
-}
-
-static int
-compare_flow(const void *a_, const void *b_)
-{
- struct ovntrace_flow *const *ap = a_;
- struct ovntrace_flow *const *bp = b_;
- const struct ovntrace_flow *a = *ap;
- const struct ovntrace_flow *b = *bp;
-
- if (a->pipeline != b->pipeline) {
- /* Sort OVNACT_P_INGRESS before OVNACT_P_EGRESS. */
- return a->pipeline == OVNACT_P_EGRESS ? 1 : -1;
- } else if (a->table_id != b->table_id) {
- /* Sort in increasing order of table_id. */
- return a->table_id > b->table_id ? 1 : -1;
- } else if (a->priority != b->priority) {
- /* Sort in decreasing order of priority. */
- return a->priority > b->priority ? -1 : 1;
- } else {
- /* Otherwise who cares. */
- return 0;
- }
-}
-
-static char *
-ovntrace_make_names_friendly(const char *in)
-{
- if (!use_friendly_names) {
- return xstrdup(in);
- }
-
- struct ds out = DS_EMPTY_INITIALIZER;
- while (*in) {
- struct lex_token token;
- const char *start;
- const char *next;
-
- next = lex_token_parse(&token, in, &start);
- if (token.type == LEX_T_STRING) {
- const struct ovntrace_port *port = shash_find_data(&ports,
- token.s);
- if (port) {
- ds_put_buffer(&out, in, start - in);
- json_string_escape(port->friendly_name, &out);
- } else {
- ds_put_buffer(&out, in, next - in);
- }
- } else if (token.type != LEX_T_END) {
- ds_put_buffer(&out, in, next - in);
- } else {
- break;
- }
- lex_token_destroy(&token);
- in = next;
- }
- return ds_steal_cstr(&out);
-}
-
-static void
-read_flows(void)
-{
- ovn_init_symtab(&symtab);
-
- const struct sbrec_logical_flow *sblf;
- SBREC_LOGICAL_FLOW_FOR_EACH (sblf, ovnsb_idl) {
- const struct sbrec_datapath_binding *sbdb = sblf->logical_datapath;
- struct ovntrace_datapath *dp
- = ovntrace_datapath_find_by_sb_uuid(&sbdb->header_.uuid);
- if (!dp) {
- VLOG_WARN("logical flow missing datapath");
- continue;
- }
-
- char *error;
- struct expr *match;
- match = expr_parse_string(sblf->match, &symtab, &address_sets,
- &port_groups, NULL, &error);
- if (error) {
- VLOG_WARN("%s: parsing expression failed (%s)",
- sblf->match, error);
- free(error);
- continue;
- }
-
- struct ovnact_parse_params pp = {
- .symtab = &symtab,
- .dhcp_opts = &dhcp_opts,
- .dhcpv6_opts = &dhcpv6_opts,
- .nd_ra_opts = &nd_ra_opts,
- .pipeline = (!strcmp(sblf->pipeline, "ingress")
- ? OVNACT_P_INGRESS
- : OVNACT_P_EGRESS),
- .n_tables = 24,
- .cur_ltable = sblf->table_id,
- };
- uint64_t stub[1024 / 8];
- struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(stub);
- struct expr *prereqs;
- error = ovnacts_parse_string(sblf->actions, &pp, &ovnacts, &prereqs);
- if (error) {
- VLOG_WARN("%s: parsing actions failed (%s)", sblf->actions, error);
- free(error);
- expr_destroy(match);
- continue;
- }
-
- match = expr_combine(EXPR_T_AND, match, prereqs);
- match = expr_annotate(match, &symtab, &error);
- if (error) {
- VLOG_WARN("match annotation failed (%s)", error);
- free(error);
- expr_destroy(match);
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
- continue;
- }
- if (match) {
- match = expr_simplify(match, ovntrace_is_chassis_resident, NULL);
- }
-
- struct ovntrace_flow *flow = xzalloc(sizeof *flow);
- flow->uuid = sblf->header_.uuid;
- flow->pipeline = (!strcmp(sblf->pipeline, "ingress")
- ? OVNACT_P_INGRESS
- : OVNACT_P_EGRESS);
- flow->table_id = sblf->table_id;
- flow->stage_name = nullable_xstrdup(smap_get(&sblf->external_ids,
- "stage-name"));
- flow->source = nullable_xstrdup(smap_get(&sblf->external_ids,
- "source"));
- flow->priority = sblf->priority;
- flow->match_s = ovntrace_make_names_friendly(sblf->match);
- flow->match = match;
- flow->ovnacts_len = ovnacts.size;
- flow->ovnacts = ofpbuf_steal_data(&ovnacts);
-
- if (dp->n_flows >= dp->allocated_flows) {
- dp->flows = x2nrealloc(dp->flows, &dp->allocated_flows,
- sizeof *dp->flows);
- }
- dp->flows[dp->n_flows++] = flow;
- }
-
- const struct ovntrace_datapath *dp;
- HMAP_FOR_EACH (dp, sb_uuid_node, &datapaths) {
- qsort(dp->flows, dp->n_flows, sizeof *dp->flows, compare_flow);
- }
-}
-
-static void
-read_gen_opts(void)
-{
- hmap_init(&dhcp_opts);
- const struct sbrec_dhcp_options *sdo;
- SBREC_DHCP_OPTIONS_FOR_EACH (sdo, ovnsb_idl) {
- dhcp_opt_add(&dhcp_opts, sdo->name, sdo->code, sdo->type);
- }
-
-
- hmap_init(&dhcpv6_opts);
- const struct sbrec_dhcpv6_options *sdo6;
- SBREC_DHCPV6_OPTIONS_FOR_EACH(sdo6, ovnsb_idl) {
- dhcp_opt_add(&dhcpv6_opts, sdo6->name, sdo6->code, sdo6->type);
- }
-
- hmap_init(&nd_ra_opts);
- nd_ra_opts_init(&nd_ra_opts);
-}
-
-static void
-read_mac_bindings(void)
-{
- const struct sbrec_mac_binding *sbmb;
- SBREC_MAC_BINDING_FOR_EACH (sbmb, ovnsb_idl) {
- const struct ovntrace_port *port = shash_find_data(
- &ports, sbmb->logical_port);
- if (!port) {
- VLOG_WARN("missing port %s", sbmb->logical_port);
- continue;
- }
-
- if (!uuid_equals(&port->dp->sb_uuid, &sbmb->datapath->header_.uuid)) {
- VLOG_WARN("port %s is in wrong datapath", sbmb->logical_port);
- continue;
- }
-
- struct in6_addr ip6;
- ovs_be32 ip4;
- if (ip_parse(sbmb->ip, &ip4)) {
- ip6 = in6_addr_mapped_ipv4(ip4);
- } else if (!ipv6_parse(sbmb->ip, &ip6)) {
- VLOG_WARN("%s: bad IP address", sbmb->ip);
- continue;
- }
-
- struct eth_addr mac;
- if (!eth_addr_from_string(sbmb->mac, &mac)) {
- VLOG_WARN("%s: bad Ethernet address", sbmb->mac);
- continue;
- }
-
- struct ovntrace_mac_binding *binding = xmalloc(sizeof *binding);
- binding->port_key = port->tunnel_key;
- binding->ip = ip6;
- binding->mac = mac;
- hmap_insert(&port->dp->mac_bindings, &binding->node,
- hash_mac_binding(binding->port_key, &ip6));
- }
-}
-
-static void
-read_db(void)
-{
- read_datapaths();
- read_ports();
- read_mcgroups();
- read_address_sets();
- read_port_groups();
- read_gen_opts();
- read_flows();
- read_mac_bindings();
-}
-
-static const struct ovntrace_port *
-ovntrace_port_lookup_by_name(const char *name)
-{
- const struct ovntrace_port *port = shash_find_data(&ports, name);
- if (port) {
- return port;
- }
-
- const struct ovntrace_port *match = NULL;
-
- struct shash_node *node;
- SHASH_FOR_EACH (node, &ports) {
- port = node->data;
-
- if (port->name2 && !strcmp(port->name2, name)) {
- if (match) {
- VLOG_WARN("name \"%s\" matches multiple ports", name);
- return NULL;
- }
- match = port;
- }
- }
-
- if (uuid_is_partial_string(name) >= 4) {
- SHASH_FOR_EACH (node, &ports) {
- port = node->data;
-
- struct uuid name_uuid;
- if (uuid_is_partial_match(&port->uuid, name)
- || (uuid_from_string(&name_uuid, port->name)
- && uuid_is_partial_match(&name_uuid, name))) {
- if (match && match != port) {
- VLOG_WARN("name \"%s\" matches multiple ports", name);
- return NULL;
- }
- match = port;
- }
- }
- }
-
- return match;
-}
-
-static bool
-ovntrace_lookup_port(const void *dp_, const char *port_name,
- unsigned int *portp)
-{
- const struct ovntrace_datapath *dp = dp_;
-
- if (port_name[0] == '\0') {
- *portp = 0;
- return true;
- }
-
- const struct ovntrace_port *port = ovntrace_port_lookup_by_name(port_name);
- if (port) {
- if (port->dp == dp) {
- *portp = port->tunnel_key;
- return true;
- }
- /* The port is found but not in this datapath. It happens when port
- * group is used in match conditions. */
- return false;
- }
-
- const struct ovntrace_mcgroup *mcgroup = ovntrace_mcgroup_find_by_name(dp, port_name);
- if (mcgroup) {
- *portp = mcgroup->tunnel_key;
- return true;
- }
-
- VLOG_WARN("%s: unknown logical port", port_name);
- return false;
-}
-
-static const struct ovntrace_flow *
-ovntrace_flow_lookup(const struct ovntrace_datapath *dp,
- const struct flow *uflow,
- uint8_t table_id, enum ovnact_pipeline pipeline)
-{
- for (size_t i = 0; i < dp->n_flows; i++) {
- const struct ovntrace_flow *flow = dp->flows[i];
- if (flow->pipeline == pipeline &&
- flow->table_id == table_id &&
- expr_evaluate(flow->match, uflow, ovntrace_lookup_port, dp)) {
- return flow;
- }
- }
- return NULL;
-}
-
-static char *
-ovntrace_stage_name(const struct ovntrace_datapath *dp,
- uint8_t table_id, enum ovnact_pipeline pipeline)
-{
- for (size_t i = 0; i < dp->n_flows; i++) {
- const struct ovntrace_flow *flow = dp->flows[i];
- if (flow->pipeline == pipeline && flow->table_id == table_id) {
- return xstrdup(flow->stage_name);;
- }
- }
- return NULL;
-}
-
-/* Type of a node within a trace. */
-enum ovntrace_node_type {
- /* Nodes that may have children (nonterminal nodes). */
- OVNTRACE_NODE_OUTPUT,
- OVNTRACE_NODE_MODIFY,
- OVNTRACE_NODE_ACTION,
- OVNTRACE_NODE_ERROR,
-
- /* Nodes that never have children (terminal nodes). */
- OVNTRACE_NODE_PIPELINE,
- OVNTRACE_NODE_TABLE,
- OVNTRACE_NODE_TRANSFORMATION
-};
-
-static bool
-ovntrace_node_type_is_terminal(enum ovntrace_node_type type)
-{
- switch (type) {
- case OVNTRACE_NODE_OUTPUT:
- case OVNTRACE_NODE_MODIFY:
- case OVNTRACE_NODE_ACTION:
- case OVNTRACE_NODE_ERROR:
- return true;
-
- case OVNTRACE_NODE_PIPELINE:
- case OVNTRACE_NODE_TABLE:
- case OVNTRACE_NODE_TRANSFORMATION:
- return false;
- }
-
- OVS_NOT_REACHED();
-}
-
-struct ovntrace_node {
- struct ovs_list node; /* In parent. */
-
- enum ovntrace_node_type type;
- char *name;
- bool always_indent;
- struct ovs_list subs; /* List of children. */
-};
-
-static void ovntrace_node_destroy(struct ovntrace_node *);
-
-static struct ovntrace_node * OVS_PRINTF_FORMAT(3, 4)
-ovntrace_node_append(struct ovs_list *super, enum ovntrace_node_type type,
- const char *format, ...)
-{
- va_list args;
- va_start(args, format);
- char *s = xvasprintf(format, args);
- va_end(args);
-
- struct ovntrace_node *node = xmalloc(sizeof *node);
- ovs_list_push_back(super, &node->node);
- node->type = type;
- node->name = s;
- node->always_indent = false;
- ovs_list_init(&node->subs);
-
- return node;
-}
-
-static void
-ovntrace_node_clone(const struct ovs_list *old, struct ovs_list *new)
-{
- const struct ovntrace_node *osub;
- LIST_FOR_EACH (osub, node, old) {
- struct ovntrace_node *nsub = ovntrace_node_append(new, osub->type,
- "%s", osub->name);
- nsub->always_indent = osub->always_indent;
- ovntrace_node_clone(&osub->subs, &nsub->subs);
- }
-}
-
-static void
-ovntrace_node_list_destroy(struct ovs_list *list)
-{
- if (list) {
- struct ovntrace_node *node, *next;
-
- LIST_FOR_EACH_SAFE (node, next, node, list) {
- ovs_list_remove(&node->node);
- ovntrace_node_destroy(node);
- }
- }
-}
-
-static void
-ovntrace_node_destroy(struct ovntrace_node *node)
-{
- if (node) {
- ovntrace_node_list_destroy(&node->subs);
- free(node->name);
- free(node);
- }
-}
-
-static void
-ovntrace_node_print_details(struct ds *output,
- const struct ovs_list *nodes, int level)
-{
- const struct ovntrace_node *sub;
- LIST_FOR_EACH (sub, node, nodes) {
- if (sub->type == OVNTRACE_NODE_MODIFY) {
- continue;
- }
-
- bool more = (sub->node.next != nodes
- || sub->always_indent
- || ovntrace_node_type_is_terminal(sub->type));
- bool title = (sub->type == OVNTRACE_NODE_PIPELINE ||
- sub->type == OVNTRACE_NODE_TRANSFORMATION);
- if (title) {
- ds_put_char(output, '\n');
- }
- ds_put_char_multiple(output, ' ', (level + more) * 4);
- ds_put_format(output, "%s\n", sub->name);
- if (title) {
- ds_put_char_multiple(output, ' ', (level + more) * 4);
- ds_put_char_multiple(output, '-', strlen(sub->name));
- ds_put_char(output, '\n');
- }
-
- ovntrace_node_print_details(output, &sub->subs, level + more + more);
- }
-}
-
-static void
-ovntrace_node_prune_summary(struct ovs_list *nodes)
-{
- struct ovntrace_node *sub, *next;
- LIST_FOR_EACH_SAFE (sub, next, node, nodes) {
- ovntrace_node_prune_summary(&sub->subs);
- if (sub->type == OVNTRACE_NODE_MODIFY ||
- sub->type == OVNTRACE_NODE_TABLE) {
- /* Replace 'sub' by its children, if any, */
- ovs_list_remove(&sub->node);
- ovs_list_splice(&next->node, sub->subs.next, &sub->subs);
- ovntrace_node_destroy(sub);
- }
- }
-}
-
-static void
-ovntrace_node_print_summary(struct ds *output, const struct ovs_list *nodes,
- int level)
-{
- const struct ovntrace_node *sub;
- LIST_FOR_EACH (sub, node, nodes) {
- if (sub->type == OVNTRACE_NODE_ACTION
- && !strncmp(sub->name, "next(", 5)) {
- continue;
- }
-
- ds_put_char_multiple(output, ' ', level * 4);
- ds_put_cstr(output, sub->name);
- if (!ovs_list_is_empty(&sub->subs)) {
- ds_put_cstr(output, " {\n");
- ovntrace_node_print_summary(output, &sub->subs, level + 1);
- ds_put_char_multiple(output, ' ', level * 4);
- ds_put_char(output, '}');
- }
- if (sub->type != OVNTRACE_NODE_ACTION) {
- ds_put_char(output, ';');
- }
- ds_put_char(output, '\n');
- }
-}
-
-static void
-ovntrace_node_prune_hard(struct ovs_list *nodes)
-{
- struct ovntrace_node *sub, *next;
- LIST_FOR_EACH_SAFE (sub, next, node, nodes) {
- ovntrace_node_prune_hard(&sub->subs);
- if (sub->type == OVNTRACE_NODE_ACTION ||
- sub->type == OVNTRACE_NODE_PIPELINE ||
- sub->type == OVNTRACE_NODE_TABLE ||
- sub->type == OVNTRACE_NODE_OUTPUT) {
- /* Replace 'sub' by its children, if any, */
- ovs_list_remove(&sub->node);
- ovs_list_splice(&next->node, sub->subs.next, &sub->subs);
- ovntrace_node_destroy(sub);
- }
- }
-}
-
-static void
-execute_load(const struct ovnact_load *load,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- struct ovs_list *super OVS_UNUSED)
-{
- const struct ovnact_encode_params ep = {
- .lookup_port = ovntrace_lookup_port,
- .aux = dp,
- };
- uint64_t stub[512 / 8];
- struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
-
- ovnacts_encode(&load->ovnact, sizeof *load, &ep, &ofpacts);
-
- struct ofpact *a;
- OFPACT_FOR_EACH (a, ofpacts.data, ofpacts.size) {
- struct ofpact_set_field *sf = ofpact_get_SET_FIELD(a);
-
- if (!mf_is_register(sf->field->id)) {
- struct ds s = DS_EMPTY_INITIALIZER;
- ovnacts_format(&load->ovnact, OVNACT_LOAD_SIZE, &s);
- ds_chomp(&s, ';');
-
- char *friendly = ovntrace_make_names_friendly(ds_cstr(&s));
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s", friendly);
- free(friendly);
-
- ds_destroy(&s);
- }
-
- if (mf_are_prereqs_ok(sf->field, uflow, NULL)) {
- mf_set_flow_value_masked(sf->field, sf->value,
- ofpact_set_field_mask(sf), uflow);
- }
- }
- ofpbuf_uninit(&ofpacts);
-}
-
-static void
-summarize_move(const struct mf_subfield *rsrc,
- const struct expr_field *dst, const struct mf_subfield *rdst,
- const struct flow *uflow, struct ovs_list *super OVS_UNUSED)
-{
- if (!mf_is_register(rdst->field->id)) {
- struct ds s = DS_EMPTY_INITIALIZER;
- expr_field_format(dst, &s);
- ds_put_cstr(&s, " = ");
-
- if (rsrc->ofs == 0 && rsrc->n_bits >= rsrc->field->n_bits) {
- union mf_value value;
- mf_get_value(rsrc->field, uflow, &value);
- mf_format(rsrc->field, &value, NULL, NULL, &s);
- } else {
- union mf_subvalue cst;
- mf_read_subfield(rsrc, uflow, &cst);
- ds_put_hex(&s, &cst, sizeof cst);
- }
-
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s", ds_cstr(&s));
-
- ds_destroy(&s);
- }
-}
-
-static void
-execute_move(const struct ovnact_move *move, struct flow *uflow,
- struct ovs_list *super)
-{
- struct mf_subfield dst = expr_resolve_field(&move->lhs);
- struct mf_subfield src = expr_resolve_field(&move->rhs);
- summarize_move(&src, &move->lhs, &dst, uflow, super);
- mf_subfield_copy(&src, &dst, uflow, NULL);
-}
-
-static void
-execute_exchange(const struct ovnact_move *move, struct flow *uflow,
- struct ovs_list *super)
-{
- struct mf_subfield a = expr_resolve_field(&move->lhs);
- struct mf_subfield b = expr_resolve_field(&move->rhs);
- summarize_move(&b, &move->lhs, &a, uflow, super);
- summarize_move(&a, &move->rhs, &b, uflow, super);
- mf_subfield_swap(&a, &b, uflow, NULL);
-}
-
-static void
-trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
- uint8_t table_id, enum ovnact_pipeline pipeline,
- struct ovs_list *super);
-
-static void
-trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- uint8_t table_id, enum ovnact_pipeline pipeline,
- struct ovs_list *super);
-static void
-execute_output(const struct ovntrace_datapath *dp, struct flow *uflow,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- uint16_t key = uflow->regs[MFF_LOG_OUTPORT - MFF_REG0];
- if (!key) {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** output to null logical port");
- return;
- }
-
- const struct ovntrace_port *port = ovntrace_port_find_by_key(dp, key);
- const struct ovntrace_mcgroup *mcgroup = ovntrace_mcgroup_find_by_key(dp,
- key);
- const char *out_name = (port ? port->friendly_name
- : mcgroup ? mcgroup->name
- : "(unnamed)");
- if (!port && !mcgroup) {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** unknown port or multicast group %"PRIu16,
- key);
- }
-
- if (pipeline == OVNACT_P_EGRESS) {
- ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT,
- "/* output to \"%s\", type \"%s\" */",
- out_name, port ? port->type : "");
- if (port && port->peer) {
- const struct ovntrace_port *peer = port->peer;
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_PIPELINE,
- "ingress(dp=\"%s\", inport=\"%s\")",
- peer->dp->friendly_name, peer->friendly_name);
-
- struct flow new_uflow = *uflow;
- new_uflow.regs[MFF_LOG_INPORT - MFF_REG0] = peer->tunnel_key;
- new_uflow.regs[MFF_LOG_OUTPORT - MFF_REG0] = 0;
- trace__(peer->dp, &new_uflow, 0, OVNACT_P_INGRESS, &node->subs);
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
- "output(\"%s\")", out_name);
-
- }
- return;
- }
-
- struct flow egress_uflow = *uflow;
- for (int i = 0; i < FLOW_N_REGS; i++) {
- if (i != MFF_LOG_INPORT - MFF_REG0 &&
- i != MFF_LOG_OUTPORT - MFF_REG0) {
- egress_uflow.regs[i] = 0;
- }
- }
-
- uint16_t in_key = uflow->regs[MFF_LOG_INPORT - MFF_REG0];
- const char *inport_name = ovntrace_port_key_to_name(dp, in_key);
- uint32_t flags = uflow->regs[MFF_LOG_FLAGS - MFF_REG0];
- bool allow_loopback = (flags & MLF_ALLOW_LOOPBACK) != 0;
-
- if (mcgroup) {
- struct ovntrace_node *mcnode = ovntrace_node_append(
- super, OVNTRACE_NODE_PIPELINE,
- "multicast(dp=\"%s\", mcgroup=\"%s\")",
- dp->friendly_name, mcgroup->name);
- for (size_t i = 0; i < mcgroup->n_ports; i++) {
- const struct ovntrace_port *p = mcgroup->ports[i];
-
- struct ovntrace_node *node = ovntrace_node_append(
- &mcnode->subs, OVNTRACE_NODE_PIPELINE,
- "egress(dp=\"%s\", inport=\"%s\", outport=\"%s\")",
- dp->friendly_name, inport_name, p->friendly_name);
-
- if (p->tunnel_key != in_key || allow_loopback) {
- node->always_indent = true;
-
- egress_uflow.regs[MFF_LOG_OUTPORT - MFF_REG0] = p->tunnel_key;
- trace__(dp, &egress_uflow, 0, OVNACT_P_EGRESS, &node->subs);
- } else {
- ovntrace_node_append(&node->subs, OVNTRACE_NODE_OUTPUT,
- "/* omitting output because inport == outport && !flags.loopback */");
- }
- }
- return;
- }
-
- if (port && !strcmp(port->type, "chassisredirect")) {
- if (port->distributed_port) {
- ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT,
- "/* Replacing type \"%s\" outport \"%s\""
- " with distributed port \"%s\". */",
- port->type, port->friendly_name,
- port->distributed_port->friendly_name);
- port = port->distributed_port;
- out_name = port->friendly_name;
- egress_uflow.regs[MFF_LOG_OUTPORT - MFF_REG0] = port->tunnel_key;
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** output to type \"%s\" port \"%s\""
- " with no or invalid distributed port",
- port->type, out_name);
- return;
- }
- }
-
- if ((port && port->tunnel_key != in_key) || allow_loopback) {
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_PIPELINE,
- "egress(dp=\"%s\", inport=\"%s\", outport=\"%s\")",
- dp->friendly_name, inport_name, out_name);
-
- trace__(dp, &egress_uflow, 0, OVNACT_P_EGRESS, &node->subs);
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_OUTPUT,
- "/* omitting output because inport == outport && !flags.loopback */");
- }
-}
-
-static void
-execute_clone(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow cloned_flow = *uflow;
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "clone");
-
- trace_actions(on->nested, on->nested_len, dp, &cloned_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_arp(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow arp_flow = *uflow;
-
- /* Zero fields that are no longer relevant. */
- arp_flow.nw_frag = 0;
- arp_flow.nw_tos = 0;
- arp_flow.nw_ttl = 0;
- arp_flow.tcp_flags = 0;
-
- /* Update fields for ARP. */
- arp_flow.dl_type = htons(ETH_TYPE_ARP);
- arp_flow.nw_proto = ARP_OP_REQUEST;
- arp_flow.arp_sha = arp_flow.dl_src;
- arp_flow.arp_tha = eth_addr_zero;
- /* ARP SPA is already in arp_flow.nw_src. */
- /* ARP TPA is already in arp_flow.nw_dst. */
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "arp");
-
- trace_actions(on->nested, on->nested_len, dp, &arp_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_nd_na(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow na_flow = *uflow;
-
- /* Update fields for NA. */
- na_flow.dl_src = uflow->dl_dst;
- na_flow.dl_dst = uflow->dl_src;
- na_flow.ipv6_dst = uflow->ipv6_src;
- na_flow.ipv6_src = uflow->nd_target;
- na_flow.tp_src = htons(136);
- na_flow.arp_sha = eth_addr_zero;
- na_flow.arp_tha = uflow->dl_dst;
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "nd_na");
-
- trace_actions(on->nested, on->nested_len, dp, &na_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_nd_ns(const struct ovnact_nest *on, const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow na_flow = *uflow;
-
- /* Update fields for NA. */
- na_flow.dl_src = uflow->dl_src;
- na_flow.ipv6_src = uflow->ipv6_src;
- na_flow.ipv6_dst = uflow->ipv6_dst;
- struct in6_addr sn_addr;
- in6_addr_solicited_node(&sn_addr, &uflow->ipv6_dst);
- ipv6_multicast_to_ethernet(&na_flow.dl_dst, &sn_addr);
- na_flow.tp_src = htons(135);
- na_flow.arp_sha = eth_addr_zero;
- na_flow.arp_tha = uflow->dl_dst;
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "nd_ns");
-
- trace_actions(on->nested, on->nested_len, dp, &na_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_icmp4(const struct ovnact_nest *on,
- const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow icmp4_flow = *uflow;
-
- /* Update fields for ICMP. */
- icmp4_flow.dl_dst = uflow->dl_dst;
- icmp4_flow.dl_src = uflow->dl_src;
- icmp4_flow.nw_dst = uflow->nw_dst;
- icmp4_flow.nw_src = uflow->nw_src;
- icmp4_flow.nw_proto = IPPROTO_ICMP;
- icmp4_flow.nw_ttl = 255;
- icmp4_flow.tp_src = htons(ICMP4_DST_UNREACH); /* icmp type */
- icmp4_flow.tp_dst = htons(1); /* icmp code */
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "icmp4");
-
- trace_actions(on->nested, on->nested_len, dp, &icmp4_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_icmp6(const struct ovnact_nest *on,
- const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow icmp6_flow = *uflow;
-
- /* Update fields for ICMPv6. */
- icmp6_flow.dl_dst = uflow->dl_dst;
- icmp6_flow.dl_src = uflow->dl_src;
- icmp6_flow.ipv6_dst = uflow->ipv6_dst;
- icmp6_flow.ipv6_src = uflow->ipv6_src;
- icmp6_flow.nw_proto = IPPROTO_ICMPV6;
- icmp6_flow.nw_ttl = 255;
- icmp6_flow.tp_src = htons(ICMP6_DST_UNREACH); /* icmp type */
- icmp6_flow.tp_dst = htons(1); /* icmp code */
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "icmp6");
-
- trace_actions(on->nested, on->nested_len, dp, &icmp6_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_tcp_reset(const struct ovnact_nest *on,
- const struct ovntrace_datapath *dp,
- const struct flow *uflow, uint8_t table_id,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow tcp_flow = *uflow;
-
- /* Update fields for TCP segment. */
- tcp_flow.dl_dst = uflow->dl_dst;
- tcp_flow.dl_src = uflow->dl_src;
- tcp_flow.nw_dst = uflow->nw_dst;
- tcp_flow.nw_src = uflow->nw_src;
- tcp_flow.nw_proto = IPPROTO_TCP;
- tcp_flow.nw_ttl = 255;
- tcp_flow.tp_src = uflow->tp_src;
- tcp_flow.tp_dst = uflow->tp_dst;
- tcp_flow.tcp_flags = htons(TCP_RST);
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "tcp_reset");
-
- trace_actions(on->nested, on->nested_len, dp, &tcp_flow,
- table_id, pipeline, &node->subs);
-}
-
-static void
-execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
- const struct ovntrace_datapath *dp,
- struct flow *uflow, struct ovs_list *super)
-{
- /* Get logical port number.*/
- struct mf_subfield port_sf = expr_resolve_field(&bind->port);
- ovs_assert(port_sf.n_bits == 32);
- uint32_t port_key = mf_get_subfield(&port_sf, uflow);
-
- /* Get IP address. */
- struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
- ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
- union mf_subvalue ip_sv;
- mf_read_subfield(&ip_sf, uflow, &ip_sv);
- struct in6_addr ip = (ip_sf.n_bits == 32
- ? in6_addr_mapped_ipv4(ip_sv.ipv4)
- : ip_sv.ipv6);
-
- const struct ovntrace_mac_binding *binding
- = ovntrace_mac_binding_find(dp, port_key, &ip);
-
- uflow->dl_dst = binding ? binding->mac : eth_addr_zero;
- if (binding) {
- ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
- "/* MAC binding to "ETH_ADDR_FMT". */",
- ETH_ADDR_ARGS(uflow->dl_dst));
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
- "/* No MAC binding. */");
- }
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
- "eth.dst = "ETH_ADDR_FMT,
- ETH_ADDR_ARGS(uflow->dl_dst));
-}
-
-static void
-execute_put_opts(const struct ovnact_put_opts *po,
- const char *name, struct flow *uflow,
- struct ovs_list *super)
-{
- /* Format the put_dhcp_opts action. */
- struct ds s = DS_EMPTY_INITIALIZER;
- for (const struct ovnact_gen_option *o = po->options;
- o < &po->options[po->n_options]; o++) {
- if (o != po->options) {
- ds_put_cstr(&s, ", ");
- }
- ds_put_format(&s, "%s = ", o->option->name);
- expr_constant_set_format(&o->value, &s);
- }
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY, "%s(%s)",
- name, ds_cstr(&s));
-
- struct mf_subfield dst = expr_resolve_field(&po->dst);
- if (!mf_is_register(dst.field->id)) {
- /* Format assignment. */
- ds_clear(&s);
- expr_field_format(&po->dst, &s);
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
- "%s = 1", ds_cstr(&s));
- }
- ds_destroy(&s);
-
- struct mf_subfield sf = expr_resolve_field(&po->dst);
- union mf_subvalue sv = { .u8_val = 1 };
- mf_write_subfield_flow(&sf, &sv, uflow);
-}
-
-static void
-execute_put_dhcp_opts(const struct ovnact_put_opts *pdo,
- const char *name, struct flow *uflow,
- struct ovs_list *super)
-{
- ovntrace_node_append(
- super, OVNTRACE_NODE_ERROR,
- "/* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */");
- execute_put_opts(pdo, name, uflow, super);
-}
-
-static void
-execute_put_nd_ra_opts(const struct ovnact_put_opts *pdo,
- const char *name, struct flow *uflow,
- struct ovs_list *super)
-{
- execute_put_opts(pdo, name, uflow, super);
-}
-
-static void
-execute_next(const struct ovnact_next *next,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- if (pipeline != next->pipeline) {
- ovs_assert(next->pipeline == OVNACT_P_INGRESS);
-
- uint16_t in_key = uflow->regs[MFF_LOG_INPORT - MFF_REG0];
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_PIPELINE, "ingress(dp=\"%s\", inport=\"%s\")",
- dp->friendly_name, ovntrace_port_key_to_name(dp, in_key));
- super = &node->subs;
- }
- trace__(dp, uflow, next->ltable, next->pipeline, super);
-}
-
-
-static void
-execute_dns_lookup(const struct ovnact_dns_lookup *dl, struct flow *uflow,
- struct ovs_list *super)
-{
- struct mf_subfield sf = expr_resolve_field(&dl->dst);
- union mf_subvalue sv = { .u8_val = 0 };
- mf_write_subfield_flow(&sf, &sv, uflow);
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** dns_lookup action not implemented");
-}
-
-static void
-execute_ct_next(const struct ovnact_ct_next *ct_next,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- /* Figure out ct_state. */
- uint32_t state;
- const char *comment;
- if (ct_state_idx < n_ct_states) {
- state = ct_states[ct_state_idx++];
- comment = "";
- } else {
- state = CS_ESTABLISHED | CS_TRACKED;
- comment = " /* default (use --ct to customize) */";
- }
-
- /* Make a sub-node for attaching the next table. */
- struct ds s = DS_EMPTY_INITIALIZER;
- format_flags(&s, ct_state_to_string, state, '|');
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "ct_next(ct_state=%s%s)",
- ds_cstr(&s), comment);
- ds_destroy(&s);
-
- /* Trace the actions in the next table. */
- struct flow ct_flow = *uflow;
- ct_flow.ct_state = state;
- trace__(dp, &ct_flow, ct_next->ltable, pipeline, &node->subs);
-
- /* Upon return, we will trace the actions following the ct action in the
- * original table. The pipeline forked, so we're using the original
- * flow, not ct_flow. */
-}
-
-static void
-execute_ct_nat(const struct ovnact_ct_nat *ct_nat,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- bool is_dst = ct_nat->ovnact.type == OVNACT_CT_DNAT;
- if (!is_dst && dp->has_local_l3gateway && !ct_nat->ip) {
- /* "ct_snat;" has no visible effect in a gateway router. */
- return;
- }
- const char *direction = is_dst ? "dst" : "src";
-
- /* Make a sub-node for attaching the next table,
- * and figure out the changes if any. */
- struct flow ct_flow = *uflow;
- struct ds s = DS_EMPTY_INITIALIZER;
- ds_put_format(&s, "ct_%cnat", direction[0]);
- if (ct_nat->ip) {
- ds_put_format(&s, "(ip4.%s="IP_FMT")", direction, IP_ARGS(ct_nat->ip));
- ovs_be32 *ip = is_dst ? &ct_flow.nw_dst : &ct_flow.nw_src;
- *ip = ct_nat->ip;
-
- uint8_t state = is_dst ? CS_DST_NAT : CS_SRC_NAT;
- ct_flow.ct_state |= state;
- } else {
- ds_put_format(&s, " /* assuming no un-%cnat entry, so no change */",
- direction[0]);
- }
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "%s", ds_cstr(&s));
- ds_destroy(&s);
-
- /* Trace the actions in the next table. */
- trace__(dp, &ct_flow, ct_nat->ltable, pipeline, &node->subs);
-
- /* Upon return, we will trace the actions following the ct action in the
- * original table. The pipeline forked, so we're using the original
- * flow, not ct_flow. */
-}
-
-static void
-execute_ct_lb(const struct ovnact_ct_lb *ct_lb,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- enum ovnact_pipeline pipeline, struct ovs_list *super)
-{
- struct flow ct_lb_flow = *uflow;
-
- int family = (ct_lb_flow.dl_type == htons(ETH_TYPE_IP) ? AF_INET
- : ct_lb_flow.dl_type == htons(ETH_TYPE_IPV6) ? AF_INET6
- : AF_UNSPEC);
- if (family != AF_UNSPEC) {
- const struct ovnact_ct_lb_dst *dst = NULL;
- if (ct_lb->n_dsts) {
- /* For ct_lb with addresses, choose one of the addresses. */
- int n = 0;
- for (int i = 0; i < ct_lb->n_dsts; i++) {
- const struct ovnact_ct_lb_dst *d = &ct_lb->dsts[i];
- if (d->family != family) {
- continue;
- }
-
- /* Check for the destination specified by --lb-dst, if any. */
- if (lb_dst.family == family
- && (family == AF_INET
- ? d->ipv4 == lb_dst.ipv4
- : ipv6_addr_equals(&d->ipv6, &lb_dst.ipv6))) {
- lb_dst.family = AF_UNSPEC;
- dst = d;
- break;
- }
-
- /* Select a random destination as a fallback. */
- if (!random_range(++n)) {
- dst = d;
- }
- }
-
- if (!dst) {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** no load balancing destination "
- "(use --lb-dst)");
- }
- } else if (lb_dst.family == family) {
- /* For ct_lb without addresses, use user-specified address. */
- dst = &lb_dst;
- }
-
- if (dst) {
- if (family == AF_INET6) {
- ct_lb_flow.ipv6_dst = dst->ipv6;
- } else {
- ct_lb_flow.nw_dst = dst->ipv4;
- }
- if (dst->port) {
- ct_lb_flow.tp_dst = htons(dst->port);
- }
- ct_lb_flow.ct_state |= CS_DST_NAT;
- }
- }
-
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TRANSFORMATION, "ct_lb");
- trace__(dp, &ct_lb_flow, ct_lb->ltable, pipeline, &node->subs);
-}
-
-static void
-execute_log(const struct ovnact_log *log, struct flow *uflow,
- struct ovs_list *super)
-{
- char *packet_str = flow_to_string(uflow, NULL);
- ovntrace_node_append(super, OVNTRACE_NODE_TRANSFORMATION,
- "LOG: ACL name=%s, verdict=%s, severity=%s, packet=\"%s\"",
- log->name ? log->name : "<unnamed>",
- log_verdict_to_string(log->verdict),
- log_severity_to_string(log->severity),
- packet_str);
- free(packet_str);
-}
-
-static void
-execute_ovnfield_load(const struct ovnact_load *load,
- struct ovs_list *super)
-{
- const struct ovn_field *f = ovn_field_from_name(load->dst.symbol->name);
- switch (f->id) {
- case OVN_ICMP4_FRAG_MTU: {
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
- "icmp4.frag_mtu = %u",
- ntohs(load->imm.value.be16_int));
- break;
- }
-
- case OVN_FIELD_N_IDS:
- default:
- OVS_NOT_REACHED();
- }
-}
-
-static void
-trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
- const struct ovntrace_datapath *dp, struct flow *uflow,
- uint8_t table_id, enum ovnact_pipeline pipeline,
- struct ovs_list *super)
-{
- if (!ovnacts_len) {
- ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "drop;");
- return;
- }
-
- struct ds s = DS_EMPTY_INITIALIZER;
- const struct ovnact *a;
- OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
- ds_clear(&s);
- ovnacts_format(a, sizeof *a * (ovnact_next(a) - a), &s);
- char *friendly = ovntrace_make_names_friendly(ds_cstr(&s));
- ovntrace_node_append(super, OVNTRACE_NODE_ACTION, "%s", friendly);
- free(friendly);
-
- switch (a->type) {
- case OVNACT_OUTPUT:
- execute_output(dp, uflow, pipeline, super);
- break;
-
- case OVNACT_NEXT:
- execute_next(ovnact_get_NEXT(a), dp, uflow, pipeline, super);
- break;
-
- case OVNACT_LOAD:
- execute_load(ovnact_get_LOAD(a), dp, uflow, super);
- break;
-
- case OVNACT_MOVE:
- execute_move(ovnact_get_MOVE(a), uflow, super);
- break;
-
- case OVNACT_EXCHANGE:
- execute_exchange(ovnact_get_EXCHANGE(a), uflow, super);
- break;
-
- case OVNACT_DEC_TTL:
- if (is_ip_any(uflow)) {
- if (uflow->nw_ttl) {
- uflow->nw_ttl--;
- ovntrace_node_append(super, OVNTRACE_NODE_MODIFY,
- "ip.ttl--");
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** TTL underflow");
- }
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** TTL decrement of non-IP packet");
- }
- break;
-
- case OVNACT_CT_NEXT:
- execute_ct_next(ovnact_get_CT_NEXT(a), dp, uflow, pipeline, super);
- break;
-
- case OVNACT_CT_COMMIT:
- /* Nothing to do. */
- break;
-
- case OVNACT_CT_DNAT:
- execute_ct_nat(ovnact_get_CT_DNAT(a), dp, uflow, pipeline, super);
- break;
-
- case OVNACT_CT_SNAT:
- execute_ct_nat(ovnact_get_CT_SNAT(a), dp, uflow, pipeline, super);
- break;
-
- case OVNACT_CT_LB:
- execute_ct_lb(ovnact_get_CT_LB(a), dp, uflow, pipeline, super);
- break;
-
- case OVNACT_CT_CLEAR:
- flow_clear_conntrack(uflow);
- break;
-
- case OVNACT_CLONE:
- execute_clone(ovnact_get_CLONE(a), dp, uflow, table_id, pipeline,
- super);
- break;
-
- case OVNACT_ARP:
- execute_arp(ovnact_get_ARP(a), dp, uflow, table_id, pipeline,
- super);
- break;
-
- case OVNACT_ND_NA:
- case OVNACT_ND_NA_ROUTER:
- execute_nd_na(ovnact_get_ND_NA(a), dp, uflow, table_id, pipeline,
- super);
- break;
-
- case OVNACT_ND_NS:
- execute_nd_ns(ovnact_get_ND_NS(a), dp, uflow, table_id, pipeline,
- super);
- break;
-
- case OVNACT_GET_ARP:
- execute_get_mac_bind(ovnact_get_GET_ARP(a), dp, uflow, super);
- break;
-
- case OVNACT_GET_ND:
- execute_get_mac_bind(ovnact_get_GET_ND(a), dp, uflow, super);
- break;
-
- case OVNACT_PUT_ARP:
- case OVNACT_PUT_ND:
- /* Nothing to do for tracing. */
- break;
-
- case OVNACT_PUT_DHCPV4_OPTS:
- execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
- "put_dhcp_opts", uflow, super);
- break;
-
- case OVNACT_PUT_DHCPV6_OPTS:
- execute_put_dhcp_opts(ovnact_get_PUT_DHCPV6_OPTS(a),
- "put_dhcpv6_opts", uflow, super);
- break;
-
- case OVNACT_PUT_ND_RA_OPTS:
- execute_put_nd_ra_opts(ovnact_get_PUT_DHCPV6_OPTS(a),
- "put_nd_ra_opts", uflow, super);
- break;
-
- case OVNACT_SET_QUEUE:
- /* The set_queue action is slippery from a logical perspective. It
- * has no visible effect as long as the packet remains on the same
- * chassis: it can bounce from one logical datapath to another
- * retaining the queue and even end up at a VM on the same chassis.
- * Without taking the physical arrangement into account, we can't
- * do anything with this action other than just to note that it
- * happened. If we ever add some physical knowledge to ovn-trace,
- * though, it would be easy enough to track the queue information
- * by adjusting uflow->skb_priority. */
- break;
-
- case OVNACT_DNS_LOOKUP:
- execute_dns_lookup(ovnact_get_DNS_LOOKUP(a), uflow, super);
- break;
-
- case OVNACT_LOG:
- execute_log(ovnact_get_LOG(a), uflow, super);
- break;
-
- case OVNACT_SET_METER:
- /* Nothing to do. */
- break;
-
- case OVNACT_ICMP4:
- execute_icmp4(ovnact_get_ICMP4(a), dp, uflow, table_id, pipeline,
- super);
- break;
-
- case OVNACT_ICMP4_ERROR:
- execute_icmp4(ovnact_get_ICMP4_ERROR(a), dp, uflow, table_id,
- pipeline, super);
- break;
-
- case OVNACT_ICMP6:
- execute_icmp6(ovnact_get_ICMP6(a), dp, uflow, table_id, pipeline,
- super);
- break;
-
- case OVNACT_IGMP:
- /* Nothing to do for tracing. */
- break;
-
- case OVNACT_TCP_RESET:
- execute_tcp_reset(ovnact_get_TCP_RESET(a), dp, uflow, table_id,
- pipeline, super);
- break;
-
- case OVNACT_OVNFIELD_LOAD:
- execute_ovnfield_load(ovnact_get_OVNFIELD_LOAD(a), super);
- break;
-
- case OVNACT_TRIGGER_EVENT:
- break;
-
- case OVNACT_CHECK_PKT_LARGER:
- break;
- }
- }
- ds_destroy(&s);
-}
-
-static bool
-may_omit_stage(const struct ovntrace_flow *f, uint8_t table_id)
-{
- return (f
- && f->match->type == EXPR_T_BOOLEAN && f->match->boolean
- && f->ovnacts_len == OVNACT_NEXT_SIZE
- && f->ovnacts->type == OVNACT_NEXT
- && ovnact_get_NEXT(f->ovnacts)->ltable == table_id + 1);
-}
-
-static void
-trace_openflow(const struct ovntrace_flow *f, struct ovs_list *super)
-{
- struct ofputil_flow_stats_request fsr = {
- .cookie = htonll(f->uuid.parts[0]),
- .cookie_mask = OVS_BE64_MAX,
- .out_port = OFPP_ANY,
- .out_group = OFPG_ANY,
- .table_id = OFPTT_ALL,
- };
-
- struct ofputil_flow_stats *fses;
- size_t n_fses;
- int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM,
- &fses, &n_fses);
- if (error) {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** error obtaining flow stats (%s)",
- ovs_strerror(error));
- VLOG_WARN("%s: error obtaining flow stats (%s)",
- ovs, ovs_strerror(error));
- return;
- }
-
- if (n_fses) {
- struct ds s = DS_EMPTY_INITIALIZER;
- for (size_t i = 0; i < n_fses; i++) {
- ds_clear(&s);
- ofputil_flow_stats_format(&s, &fses[i], NULL, NULL, true);
- ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
- "%s", ds_cstr(&s));
- }
- ds_destroy(&s);
- } else {
- ovntrace_node_append(super, OVNTRACE_NODE_ERROR,
- "*** no OpenFlow flows");
- }
-
- for (size_t i = 0; i < n_fses; i++) {
- free(CONST_CAST(struct ofpact *, fses[i].ofpacts));
- }
- free(fses);
-}
-
-static void
-trace__(const struct ovntrace_datapath *dp, struct flow *uflow,
- uint8_t table_id, enum ovnact_pipeline pipeline,
- struct ovs_list *super)
-{
- const struct ovntrace_flow *f;
- for (;;) {
- f = ovntrace_flow_lookup(dp, uflow, table_id, pipeline);
- if (!may_omit_stage(f, table_id)) {
- break;
- }
- table_id++;
- }
-
- struct ds s = DS_EMPTY_INITIALIZER;
- ds_put_format(&s, "%2d. ", table_id);
- if (f) {
- if (f->stage_name && f->source) {
- ds_put_format(&s, "%s (%s): ", f->stage_name, f->source);
- } else if (f->stage_name) {
- ds_put_format(&s, "%s: ", f->stage_name);
- } else if (f->source) {
- ds_put_format(&s, "(%s): ", f->source);
- }
- ds_put_format(&s, "%s, priority %d, uuid %08x",
- f->match_s, f->priority, f->uuid.parts[0]);
- } else {
- char *stage_name = ovntrace_stage_name(dp, table_id, pipeline);
- ds_put_format(&s, "%s%sno match (implicit drop)",
- stage_name ? stage_name : "",
- stage_name ? ": " : "");
- free(stage_name);
- }
- struct ovntrace_node *node = ovntrace_node_append(
- super, OVNTRACE_NODE_TABLE, "%s", ds_cstr(&s));
- ds_destroy(&s);
-
- if (f) {
- if (vconn) {
- trace_openflow(f, &node->subs);
- }
- trace_actions(f->ovnacts, f->ovnacts_len, dp, uflow, table_id,
- pipeline, &node->subs);
- }
-}
-
-static char *
-trace(const char *dp_s, const char *flow_s)
-{
- const struct ovntrace_datapath *dp = ovntrace_datapath_find_by_name(dp_s);
- if (!dp) {
- return xasprintf("unknown datapath \"%s\"\n", dp_s);
- }
-
- struct flow uflow;
- char *error = expr_parse_microflow(flow_s, &symtab, &address_sets,
- &port_groups, ovntrace_lookup_port,
- dp, &uflow);
- if (error) {
- char *s = xasprintf("error parsing flow: %s\n", error);
- free(error);
- return s;
- }
-
- uint32_t in_key = uflow.regs[MFF_LOG_INPORT - MFF_REG0];
- if (!in_key) {
- VLOG_WARN("microflow does not specify ingress port");
- }
- const struct ovntrace_port *inport = ovntrace_port_find_by_key(dp, in_key);
- const char *inport_name = inport ? inport->friendly_name : "(unnamed)";
-
- struct ds output = DS_EMPTY_INITIALIZER;
-
- ds_put_cstr(&output, "# ");
- flow_format(&output, &uflow, NULL);
- ds_put_char(&output, '\n');
-
- if (ovs) {
- int retval = vconn_open_block(ovs, 1 << OFP13_VERSION, 0, -1, &vconn);
- if (retval) {
- VLOG_WARN("%s: connection failed (%s)", ovs, ovs_strerror(retval));
- }
- }
-
- struct ovs_list root = OVS_LIST_INITIALIZER(&root);
- struct ovntrace_node *node = ovntrace_node_append(
- &root, OVNTRACE_NODE_PIPELINE, "ingress(dp=\"%s\", inport=\"%s\")",
- dp->friendly_name, inport_name);
- trace__(dp, &uflow, 0, OVNACT_P_INGRESS, &node->subs);
-
- bool multiple = (detailed + summary + minimal) > 1;
- if (detailed) {
- if (multiple) {
- ds_put_cstr(&output, "# Detailed trace.\n");
- }
- ovntrace_node_print_details(&output, &root, 0);
- }
-
- if (summary) {
- if (multiple) {
- ds_put_cstr(&output, "# Summary trace.\n");
- }
- struct ovs_list clone = OVS_LIST_INITIALIZER(&clone);
- ovntrace_node_clone(&root, &clone);
- ovntrace_node_prune_summary(&clone);
- ovntrace_node_print_summary(&output, &clone, 0);
- ovntrace_node_list_destroy(&clone);
- }
-
- if (minimal) {
- if (multiple) {
- ds_put_cstr(&output, "# Minimal trace.\n");
- }
- ovntrace_node_prune_hard(&root);
- ovntrace_node_print_summary(&output, &root, 0);
- }
-
- ovntrace_node_list_destroy(&root);
-
- vconn_close(vconn);
-
- return ds_steal_cstr(&output);
-}
-
-static void
-ovntrace_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
- const char *argv[] OVS_UNUSED, void *exiting_)
-{
- bool *exiting = exiting_;
- *exiting = true;
- unixctl_command_reply(conn, NULL);
-}
-
-static void
-ovntrace_trace(struct unixctl_conn *conn, int argc,
- const char *argv[], void *aux OVS_UNUSED)
-{
- detailed = summary = minimal = false;
- while (argc > 1 && argv[1][0] == '-') {
- if (!strcmp(argv[1], "--detailed")) {
- detailed = true;
- } else if (!strcmp(argv[1], "--summary")) {
- summary = true;
- } else if (!strcmp(argv[1], "--minimal")) {
- minimal = true;
- } else if (!strcmp(argv[1], "--all")) {
- detailed = summary = minimal = true;
- } else {
- unixctl_command_reply_error(conn, "unknown option");
- return;
- }
- argc--;
- argv++;
- }
- if (!detailed && !summary && !minimal) {
- detailed = true;
- }
-
- if (argc != 3) {
- unixctl_command_reply_error(
- conn, "exactly 2 non-option arguments are required");
- return;
- }
-
- char *output = trace(argv[1], argv[2]);
- unixctl_command_reply(conn, output);
- free(output);
-}
diff --git a/ovn/utilities/ovndb-servers.ocf b/ovn/utilities/ovndb-servers.ocf
deleted file mode 100755
index cd4742668..000000000
--- a/ovn/utilities/ovndb-servers.ocf
+++ /dev/null
@@ -1,642 +0,0 @@
-#!/bin/bash
-
-: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/lib/heartbeat}
-. ${OCF_FUNCTIONS_DIR}/ocf-shellfuncs
-: ${OVN_CTL_DEFAULT="/usr/share/openvswitch/scripts/ovn-ctl"}
-: ${NB_MASTER_PORT_DEFAULT="6641"}
-: ${NB_MASTER_PROTO_DEFAULT="tcp"}
-: ${SB_MASTER_PORT_DEFAULT="6642"}
-: ${SB_MASTER_PROTO_DEFAULT="tcp"}
-: ${MANAGE_NORTHD_DEFAULT="no"}
-: ${INACTIVE_PROBE_DEFAULT="5000"}
-: ${LISTEN_ON_MASTER_IP_ONLY_DEFAULT="yes"}
-: ${NB_SSL_KEY_DEFAULT="/etc/openvswitch/ovnnb-privkey.pem"}
-: ${NB_SSL_CERT_DEFAULT="/etc/openvswitch/ovnnb-cert.pem"}
-: ${NB_SSL_CACERT_DEFAULT="/etc/openvswitch/cacert.pem"}
-: ${SB_SSL_KEY_DEFAULT="/etc/openvswitch/ovnsb-privkey.pem"}
-: ${SB_SSL_CERT_DEFAULT="/etc/openvswitch/ovnsb-cert.pem"}
-: ${SB_SSL_CACERT_DEFAULT="/etc/openvswitch/cacert.pem"}
-
-CRM_MASTER="${HA_SBIN_DIR}/crm_master -l reboot"
-CRM_ATTR_REPL_INFO="${HA_SBIN_DIR}/crm_attribute --type crm_config --name OVN_REPL_INFO -s ovn_ovsdb_master_server"
-OVN_CTL=${OCF_RESKEY_ovn_ctl:-${OVN_CTL_DEFAULT}}
-MASTER_IP=${OCF_RESKEY_master_ip}
-NB_MASTER_PORT=${OCF_RESKEY_nb_master_port:-${NB_MASTER_PORT_DEFAULT}}
-NB_MASTER_PROTO=${OCF_RESKEY_nb_master_protocol:-${NB_MASTER_PROTO_DEFAULT}}
-SB_MASTER_PORT=${OCF_RESKEY_sb_master_port:-${SB_MASTER_PORT_DEFAULT}}
-SB_MASTER_PROTO=${OCF_RESKEY_sb_master_protocol:-${SB_MASTER_PROTO_DEFAULT}}
-MANAGE_NORTHD=${OCF_RESKEY_manage_northd:-${MANAGE_NORTHD_DEFAULT}}
-INACTIVE_PROBE=${OCF_RESKEY_inactive_probe_interval:-${INACTIVE_PROBE_DEFAULT}}
-NB_PRIVKEY=${OCF_RESKEY_ovn_nb_db_privkey:-${NB_SSL_KEY_DEFAULT}}
-NB_CERT=${OCF_RESKEY_ovn_nb_db_cert:-${NB_SSL_CERT_DEFAULT}}
-NB_CACERT=${OCF_RESKEY_ovn_nb_db_cacert:-${NB_SSL_CACERT_DEFAULT}}
-SB_PRIVKEY=${OCF_RESKEY_ovn_sb_db_privkey:-${SB_SSL_KEY_DEFAULT}}
-SB_CERT=${OCF_RESKEY_ovn_sb_db_cert:-${SB_SSL_CERT_DEFAULT}}
-SB_CACERT=${OCF_RESKEY_ovn_sb_db_cacert:-${SB_SSL_CACERT_DEFAULT}}
-
-
-# In order for pacemaker to work with LB, we can set LISTEN_ON_MASTER_IP_ONLY
-# to false and pass LB vip IP while creating pcs resource.
-LISTEN_ON_MASTER_IP_ONLY=${OCF_RESKEY_listen_on_master_ip_only:-${LISTEN_ON_MASTER_IP_ONLY_DEFAULT}}
-
-# Invalid IP address is an address that can never exist in the network, as
-# mentioned in rfc-5737. The ovsdb servers connects to this IP address till
-# a master is promoted and the IPAddr2 resource is started.
-INVALID_IP_ADDRESS=192.0.2.254
-
-host_name=$(ocf_attribute_target)
-if [ "x$host_name" = "x" ]; then
- # function ocf_attribute_target may not be available if the pacemaker
- # version is old. Fall back to ocf_local_nodename.
- host_name=$(ocf_local_nodename)
-fi
-: ${slave_score=5}
-: ${master_score=10}
-
-ovsdb_server_metadata() {
- cat <<END
-<?xml version="1.0"?>
-<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
-<resource-agent name="ovsdb-server">
- <version>1.0</version>
-
- <longdesc lang="en">
- This resource manages ovsdb-server.
- </longdesc>
-
- <shortdesc lang="en">
- Manages ovsdb-server.
- </shortdesc>
-
- <parameters>
-
- <parameter name="ovn_ctl" unique="1">
- <longdesc lang="en">
- Location to the ovn-ctl script file
- </longdesc>
- <shortdesc lang="en">ovn-ctl script</shortdesc>
- <content type="string" default="${OVN_CTL_DEFAULT}" />
- </parameter>
-
- <parameter name="master_ip" unique="1">
- <longdesc lang="en">
- The IP address resource which will be available on the master ovsdb server
- </longdesc>
- <shortdesc lang="en">master ip address</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="nb_master_port" unique="1">
- <longdesc lang="en">
- The port which the master Northbound database server is listening
- </longdesc>
- <shortdesc lang="en">master Northbound database port</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="nb_master_protocol" unique="1">
- <longdesc lang="en">
- The protocol which the master Northbound database server used, 'tcp' or 'ssl'.
- </longdesc>
- <shortdesc lang="en">master Northbound database protocol</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="sb_master_port" unique="1">
- <longdesc lang="en">
- The port which the master Southbound database server is listening
- </longdesc>
- <shortdesc lang="en">master Southbound database port</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="sb_master_protocol" unique="1">
- <longdesc lang="en">
- The protocol which the master Southbound database server used, 'tcp' or 'ssl'.
- </longdesc>
- <shortdesc lang="en">master Southbound database protocol</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="manage_northd" unique="1">
- <longdesc lang="en">
- If set to yes, manages ovn-northd service. ovn-northd will be started in
- the master node.
- </longdesc>
- <shortdesc lang="en">manage ovn-northd service</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="inactive_probe_interval" unique="1">
- <longdesc lang="en">
- Inactive probe interval to set for ovsdb-server.
- </longdesc>
- <shortdesc lang="en">Set inactive probe interval</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="listen_on_master_ip_only" unique="1">
- <longdesc lang="en">
- If set to yes, the OVNDBs will listen on master IP. Otherwise, it will
- listen on 0.0.0.0. Set to yes when using pacemaker managed vip resource
- as MASTER_IP; set to no when using external LB VIP.
- </longdesc>
- <shortdesc lang="en">Listen on master IP or 0.0.0.0</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="ovn_nb_db_privkey" unique="1">
- <longdesc lang="en">
- OVN NB DB private key absolute path for ssl setup.
- </longdesc>
- <shortdesc lang="en">OVN NB DB private key file</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="ovn_nb_db_cert" unique="1">
- <longdesc lang="en">
- OVN NB DB certificate absolute path for ssl setup.
- </longdesc>
- <shortdesc lang="en">OVN NB DB cert file</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="ovn_nb_db_cacert" unique="1">
- <longdesc lang="en">
- OVN NB DB CA certificate absolute path for ssl setup.
- </longdesc>
- <shortdesc lang="en">OVN NB DB cacert file</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="ovn_sb_db_privkey" unique="1">
- <longdesc lang="en">
- OVN SB DB private key absolute path for ssl setup.
- </longdesc>
- <shortdesc lang="en">OVN SB DB private key file</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="ovn_sb_db_cert" unique="1">
- <longdesc lang="en">
- OVN SB DB certificate absolute path for ssl setup.
- </longdesc>
- <shortdesc lang="en">OVN SB DB cert file</shortdesc>
- <content type="string" />
- </parameter>
-
- <parameter name="ovn_sb_db_cacert" unique="1">
- <longdesc lang="en">
- OVN SB DB CA certificate absolute path for ssl setup.
- </longdesc>
- <shortdesc lang="en">OVN SB DB cacert file</shortdesc>
- <content type="string" />
- </parameter>
-
- </parameters>
-
- <actions>
- <action name="notify" timeout="20s" />
- <action name="start" timeout="30s" />
- <action name="stop" timeout="20s" />
- <action name="promote" timeout="50s" />
- <action name="demote" timeout="50s" />
- <action name="monitor" timeout="20s" depth="0" interval="10s"
- role="Master" />
- <action name="monitor" timeout="20s" depth="0" interval="30s"
- role="Slave"/>
- <action name="meta-data" timeout="5s" />
- <action name="validate-all" timeout="20s" />
- </actions>
-</resource-agent>
-END
- exit $OCF_SUCCESS
-}
-
-ovsdb_server_notify() {
- # requires the notify=true meta resource attribute
- local type_op="${OCF_RESKEY_CRM_meta_notify_type}-${OCF_RESKEY_CRM_meta_notify_operation}"
-
- if [ "$type_op" != "post-promote" ]; then
- # We are only interested in specific events
- return $OCF_SUCCESS
- fi
-
- ocf_log debug "ovndb_server: notified of event $type_op"
- if [ "x$(ovsdb_server_last_known_master)" = "x${host_name}" ]; then
- # Record ourselves so that the agent has a better chance of doing
- # the right thing at startup
- ocf_log debug "ovndb_server: $host_name is the master"
- ${CRM_ATTR_REPL_INFO} -v "$host_name"
- if [ "$MANAGE_NORTHD" = "yes" ]; then
- # Startup ovn-northd service
- ${OVN_CTL} --ovn-manage-ovsdb=no start_northd
- fi
-
- # In order to over-ride inactivity_probe for LB use case, we need to
- # create connection entry to listen on 0.0.0.0 for master node.
- if [ "x${LISTEN_ON_MASTER_IP_ONLY}" = xno ]; then
- LISTON_ON_IP="0.0.0.0"
- else
- LISTON_ON_IP=${MASTER_IP}
- fi
- conn=`ovn-nbctl get NB_global . connections`
- if [ "$conn" == "[]" ]
- then
- ovn-nbctl -- --id=@conn_uuid create Connection \
-target="p${NB_MASTER_PROTO}\:${NB_MASTER_PORT}\:${LISTON_ON_IP}" \
-inactivity_probe=$INACTIVE_PROBE -- set NB_Global . connections=@conn_uuid
- fi
-
- conn=`ovn-sbctl get SB_global . connections`
- if [ "$conn" == "[]" ]
- then
- ovn-sbctl -- --id=@conn_uuid create Connection \
-target="p${SB_MASTER_PROTO}\:${SB_MASTER_PORT}\:${LISTON_ON_IP}" \
-inactivity_probe=$INACTIVE_PROBE -- set SB_Global . connections=@conn_uuid
- fi
-
- else
- if [ "$MANAGE_NORTHD" = "yes" ]; then
- # Stop ovn-northd service. Set --ovn-manage-ovsdb=no so that
- # ovn-ctl doesn't stop ovsdb-servers.
- ${OVN_CTL} --ovn-manage-ovsdb=no stop_northd
- fi
- # Synchronize with the new master
- ocf_log debug "ovndb_server: Connecting to the new master ${OCF_RESKEY_CRM_meta_notify_promote_uname}"
- ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \
- --db-nb-sync-from-port=${NB_MASTER_PORT} \
- --db-nb-sync-from-proto=${NB_MASTER_PROTO}
- ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \
- --db-sb-sync-from-port=${SB_MASTER_PORT} \
- --db-sb-sync-from-proto=${SB_MASTER_PROTO}
- fi
-}
-
-ovsdb_server_usage() {
- cat <<END
-usage: $0 {start|stop|status|monitor|notify|validate-all|meta-data}
-
-Expects to have a fully populated OCF RA-compliant environment set.
-END
- exit $1
-}
-
-ovsdb_server_find_active_master() {
- # Operation sequence is Demote -> Stop -> Start -> Promote
- # At the point this is run, the only active masters will be
- # previous masters minus any that were scheduled to be demoted
-
- for master in ${OCF_RESKEY_CRM_meta_notify_master_uname}; do
- found=0
- for old in ${OCF_RESKEY_CRM_meta_notify_demote_uname}; do
- if [ $master = $old ]; then
- found=1
- fi
- done
- if [ $found = 0 ]; then
- # Rely on master-max=1
- # Pacemaker will demote any additional ones it finds before starting new copies
- echo "$master"
- return
- fi
- done
-
- local expected_master=$($CRM_ATTR_REPL_INFO --query -q 2>/dev/null)
- case "x${OCF_RESKEY_CRM_meta_notify_start_uname}x" in
- *${expected_master}*) echo "${expected_master}";; # The previous master is expected to start
- esac
-}
-
-ovsdb_server_last_known_master()
-{
- if [ -z "$MASTER_HOST" ]; then
- MASTER_HOST="$(${CRM_ATTR_REPL_INFO} --query -q 2>/dev/null)"
- fi
- echo "$MASTER_HOST"
-}
-
-ovsdb_server_master_update() {
- case $1 in
- $OCF_SUCCESS)
- $CRM_MASTER -N $host_name -v ${slave_score};;
- $OCF_RUNNING_MASTER)
- $CRM_MASTER -N $host_name -v ${master_score};;
- #*) $CRM_MASTER -D;;
- esac
-}
-
-ovsdb_server_monitor() {
- ovsdb_server_check_status $@
- rc=$?
-
- ovsdb_server_master_update $rc
- return $rc
-}
-
-ovsdb_server_check_status() {
- local sb_status=`${OVN_CTL} status_ovnsb`
- local nb_status=`${OVN_CTL} status_ovnnb`
-
- if [[ $sb_status == "running/backup" && $nb_status == "running/backup" ]]; then
- return $OCF_SUCCESS
- fi
-
- check_northd="no"
- if [ "$MANAGE_NORTHD" == "yes" ] && [ "$1" != "ignore_northd" ]; then
- check_northd="yes"
- fi
-
- if [[ $sb_status == "running/active" && $nb_status == "running/active" ]]; then
- if [ "$check_northd" == "yes" ]; then
- # Verify if ovn-northd is running or not.
- ${OVN_CTL} status_northd
- if [ "$?" == "0" ] ; then
- return $OCF_RUNNING_MASTER
- fi
- else
- return $OCF_RUNNING_MASTER
- fi
- fi
-
- # TODO: What about service running but not in either state above?
- # Eg. a transient state where one db is "active" and the other
- # "backup"
-
- return $OCF_NOT_RUNNING
-}
-
-ovsdb_server_start() {
- ovsdb_server_check_status
- local status=$?
- # If not in stopped state, return
- if [ $status -ne $OCF_NOT_RUNNING ]; then
- return $status
- fi
-
- local present_master=$(ovsdb_server_find_active_master)
-
- set ${OVN_CTL}
-
- if [ "x${LISTEN_ON_MASTER_IP_ONLY}" = xno ]; then
- set $@ --db-nb-port=${NB_MASTER_PORT}
- set $@ --db-sb-port=${SB_MASTER_PORT}
-
- else
- set $@ --db-nb-addr=${MASTER_IP} --db-nb-port=${NB_MASTER_PORT}
- set $@ --db-sb-addr=${MASTER_IP} --db-sb-port=${SB_MASTER_PORT}
- fi
-
- if [ "x${NB_MASTER_PROTO}" = xssl ]; then
- set $@ --ovn-nb-db-ssl-key=${NB_PRIVKEY}
- set $@ --ovn-nb-db-ssl-cert=${NB_CERT}
- set $@ --ovn-nb-db-ssl-ca-cert=${NB_CACERT}
- fi
- if [ "x${SB_MASTER_PROTO}" = xssl ]; then
- set $@ --ovn-sb-db-ssl-key=${SB_PRIVKEY}
- set $@ --ovn-sb-db-ssl-cert=${SB_CERT}
- set $@ --ovn-sb-db-ssl-ca-cert=${SB_CACERT}
- fi
- if [ "x${present_master}" = x ]; then
- # No master detected, or the previous master is not among the
- # set starting.
- #
- # Force all copies to come up as slaves by pointing them into
- # space and let pacemaker pick one to promote:
- #
- if [ "x${NB_MASTER_PROTO}" = xtcp ]; then
- set $@ --db-nb-create-insecure-remote=yes
- fi
-
- if [ "x${SB_MASTER_PROTO}" = xtcp ]; then
- set $@ --db-sb-create-insecure-remote=yes
- fi
- set $@ --db-nb-sync-from-addr=${INVALID_IP_ADDRESS} --db-sb-sync-from-addr=${INVALID_IP_ADDRESS}
-
- elif [ ${present_master} != ${host_name} ]; then
- if [ "x${LISTEN_ON_MASTER_IP_ONLY}" = xyes ]; then
- if [ "x${NB_MASTER_PROTO}" = xtcp ]; then
- set $@ --db-nb-create-insecure-remote=yes
- fi
-
- if [ "x${SB_MASTER_PROTO}" = xtcp ]; then
- set $@ --db-sb-create-insecure-remote=yes
- fi
- fi
- # An existing master is active, connect to it
- set $@ --db-nb-sync-from-addr=${MASTER_IP} --db-sb-sync-from-addr=${MASTER_IP}
- set $@ --db-nb-sync-from-port=${NB_MASTER_PORT}
- set $@ --db-nb-sync-from-proto=${NB_MASTER_PROTO}
- set $@ --db-sb-sync-from-port=${SB_MASTER_PORT}
- set $@ --db-sb-sync-from-proto=${SB_MASTER_PROTO}
- if [ "x${LISTEN_ON_MASTER_IP_ONLY}" = xno ]; then
- set $@ --db-sb-use-remote-in-db="no"
- set $@ --db-nb-use-remote-in-db="no"
- fi
- fi
-
- $@ start_ovsdb
-
- while [ 1 = 1 ]; do
- # It is important that we don't return until we're in a functional
- # state. When checking the status of the ovsdb-server's ignore northd.
- # It is possible that when the resource is restarted ovsdb-server's
- # can be started as masters and ovn-northd would not have been started.
- # ovn-northd will be started once a node is promoted to master and
- # 'manage_northd' is set to yes.
- ovsdb_server_monitor ignore_northd
- rc=$?
- case $rc in
- $OCF_SUCCESS) return $rc;;
- $OCF_RUNNING_MASTER)
- # When a slave node is promoted as master, the action would be
- # STOP -> START -> PROMOTE.
- # When the start action is called, it is possible for the
- # ovsdb-server's to be started as active. This could happen
- # if the node owns the $MASTER_IP. At this point, pacemaker
- # has not promoted this node yet. Demote it and check for
- # status again.
- # Let pacemaker promote it in subsequent actions.
- # As per the OCF guidelines, only monitor action should return
- # OCF_RUNNING_MASTER.
- # http://www.linux-ha.org/doc/dev-guides/_literal_ocf_running_master_literal_8.html
- ${OVN_CTL} demote_ovnnb \
- --db-nb-sync-from-addr=${INVALID_IP_ADDRESS}
- ${OVN_CTL} demote_ovnsb \
- --db-sb-sync-from-addr=${INVALID_IP_ADDRESS}
- ;;
- $OCF_ERR_GENERIC) return $rc;;
- # Otherwise loop, waiting for the service to start, until
- # the cluster times the operation out
- esac
- ocf_log warn "ovndb_servers: After starting ovsdb, status is $rc. Checking the status again"
- done
-}
-
-ovsdb_server_stop() {
- if [ "$MANAGE_NORTHD" = "yes" ]; then
- # Stop ovn-northd service in case it was running. This is required
- # when the master is demoted. For other cases, it would be a no-op.
- # Set --ovn-manage-ovsdb=no so that ovn-ctl doesn't stop ovsdb-servers.
- ${OVN_CTL} --ovn-manage-ovsdb=no stop_northd
- fi
-
- ovsdb_server_check_status ignore_northd
- case $? in
- $OCF_NOT_RUNNING)
- # Even if one server is down, check_status returns NOT_RUNNING.
- # So before returning call stop_ovsdb to be sure.
- ${OVN_CTL} stop_ovsdb
- return ${OCF_SUCCESS};;
- esac
-
- ${OVN_CTL} stop_ovsdb
- ovsdb_server_master_update ${OCF_NOT_RUNNING}
-
- while [ 1 = 1 ]; do
- # It is important that we don't return until we're stopped
- ovsdb_server_check_status ignore_northd
- rc=$?
- case $rc in
- $OCF_SUCCESS)
- # Loop, waiting for the service to stop, until the
- # cluster times the operation out
- ocf_log warn "ovndb_servers: Even after stopping, the servers seems to be running"
- ;;
- $OCF_NOT_RUNNING)
- return $OCF_SUCCESS
- ;;
- *)
- return $rc
- ;;
- esac
- done
-
- return $OCF_ERR_GENERIC
-}
-
-ovsdb_server_promote() {
- local state
-
- ovsdb_server_check_status ignore_northd
- rc=$?
- case $rc in
- ${OCF_SUCCESS}) ;;
- ${OCF_RUNNING_MASTER}) ;;
- *)
- ovsdb_server_master_update $OCF_RUNNING_MASTER
- return ${rc}
- ;;
- esac
-
- # Restart ovs so that new master can listen on tcp port
- if [ "x${LISTEN_ON_MASTER_IP_ONLY}" = xno ]; then
- ${OVN_CTL} stop_ovsdb
- ovsdb_server_start
- fi
- ${OVN_CTL} promote_ovnnb
- ${OVN_CTL} promote_ovnsb
-
- if [ "$MANAGE_NORTHD" = "yes" ]; then
- # Startup ovn-northd service
- ${OVN_CTL} --ovn-manage-ovsdb=no start_northd
- fi
-
- ocf_log debug "ovndb_servers: Waiting for promotion $host_name as master to complete"
- ovsdb_server_check_status
- state=$?
- while [ "$state" != "$OCF_RUNNING_MASTER" ]; do
- sleep 1
- ovsdb_server_check_status
- state=$?
- done
- ocf_log debug "ovndb_servers: Promotion of $host_name as the master completed"
- # Record ourselves so that the agent has a better chance of doing
- # the right thing at startup
- ${CRM_ATTR_REPL_INFO} -v "$host_name"
- ovsdb_server_master_update $OCF_RUNNING_MASTER
- return $OCF_SUCCESS
-}
-
-ovsdb_server_demote() {
- # While demoting, check the status of ovn_northd.
- # In case ovn_northd is not running, we should return OCF_NOT_RUNNING.
- ovsdb_server_check_status
- if [ $? = $OCF_NOT_RUNNING ]; then
- return $OCF_NOT_RUNNING
- fi
-
- local present_master=$(ovsdb_server_find_active_master)
- local recorded_master=$($CRM_ATTR_REPL_INFO --query -q 2>/dev/null)
-
- ocf_log debug "ovndb_servers: Demoting $host_name, present master ${present_master}, recorded master ${recorded_master}"
- if [ "x${recorded_master}" = "x${host_name}" -a "x${present_master}" = x ]; then
- # We are the one and only master
- # This should be the "normal" case
- # The only way to be demoted is to call demote_ovn*
- #
- # The local database is only reset once we successfully
- # connect to the peer. So specify one that doesn't exist.
- #
- # Eventually a new master will be promoted and we'll resync
- # using the logic in ovsdb_server_notify()
- ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${INVALID_IP_ADDRESS}
- ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${INVALID_IP_ADDRESS}
-
- elif [ "x${present_master}" = "x${host_name}" ]; then
- # Safety check, should never be called
- #
- # Never allow sync'ing from ourselves, its a great way to
- # erase the local DB
- ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${INVALID_IP_ADDRESS}
- ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${INVALID_IP_ADDRESS}
-
- elif [ "x${present_master}" != x ]; then
- # There are too many masters and we're an extra one that is
- # being demoted. Sync to the surviving one
- ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \
- --db-nb-sync-from-port=${NB_MASTER_PORT} \
- --db-nb-sync-from-proto=${NB_MASTER_PROTO}
- ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \
- --db-sb-sync-from-port=${SB_MASTER_PORT} \
- --db-sb-sync-from-proto=${SB_MASTER_PROTO}
-
- else
- # For completeness, should never be called
- #
- # Something unexpected happened, perhaps CRM_ATTR_REPL_INFO is incorrect
- ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${INVALID_IP_ADDRESS}
- ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${INVALID_IP_ADDRESS}
- fi
-
- if [ "$MANAGE_NORTHD" = "yes" ]; then
- # Stop ovn-northd service
- ${OVN_CTL} --ovn-manage-ovsdb=no stop_northd
- fi
- ovsdb_server_master_update $OCF_SUCCESS
- return $OCF_SUCCESS
-}
-
-ovsdb_server_validate() {
- if [ ! -e ${OVN_CTL} ]; then
- return $OCF_ERR_INSTALLED
- fi
- return $OCF_SUCCESS
-}
-
-
-case $__OCF_ACTION in
-start) ovsdb_server_start;;
-stop) ovsdb_server_stop;;
-promote) ovsdb_server_promote;;
-demote) ovsdb_server_demote;;
-notify) ovsdb_server_notify;;
-meta-data) ovsdb_server_metadata;;
-validate-all) ovsdb_server_validate;;
-status|monitor) ovsdb_server_monitor;;
-usage|help) ovsdb_server_usage $OCF_SUCCESS;;
-*) ovsdb_server_usage $OCF_ERR_UNIMPLEMENTED ;;
-esac
-
-rc=$?
-exit $rc
diff --git a/ovsdb/ovsdb-tool.1.in b/ovsdb/ovsdb-tool.1.in
index ec85e14c4..6fdb4b5a5 100644
--- a/ovsdb/ovsdb-tool.1.in
+++ b/ovsdb/ovsdb-tool.1.in
@@ -91,18 +91,17 @@ both its schema and data.)
.
.IP "\fBcreate\-cluster\fI db contents local"
Use this command to initialize the first server in a high-availability
-cluster of 3 (or more) database servers, e.g. for an OVN northbound or
-southbound database in an environment that cannot tolerate a single
-point of failure. It creates clustered database file \fIdb\fR and
-configures the server to listen on \fIlocal\fR, which must take the
-form \fIprotocol\fB:\fIip\fB:\fIport\fR, where \fIprotocol\fR is
-\fBtcp\fR or \fBssl\fR, \fIip\fR is the server's IP (either an IPv4
-address or an IPv6 address enclosed in square brackets), and
-\fIport\fR is a TCP port number. Only one address is specified, for
-the first server in the cluster, ordinarily the one for the server
-running \fBcreate\-cluster\fR. The address is used for communication
-within the cluster, not for communicating with OVSDB clients, and must
-not use the same port used for the OVSDB protocol.
+cluster of 3 (or more) database servers, e.g. for a database in an
+environment that cannot tolerate a single point of failure. It creates
+clustered database file \fIdb\fR and configures the server to listen on
+\fIlocal\fR, which must take the form \fIprotocol\fB:\fIip\fB:\fIport\fR,
+where \fIprotocol\fR is \fBtcp\fR or \fBssl\fR, \fIip\fR is the server's
+IP (either an IPv4 address or an IPv6 address enclosed in square
+brackets), and \fIport\fR is a TCP port number. Only one address is
+specified, for the first server in the cluster, ordinarily the one for
+the server running \fBcreate\-cluster\fR. The address is used for
+communication within the cluster, not for communicating with OVSDB
+clients, and must not use the same port used for the OVSDB protocol.
.IP
The new database is initialized with \fIcontents\fR, which must name a
file that contains either an OVSDB schema in JSON format or a
diff --git a/rhel/automake.mk b/rhel/automake.mk
index 1c5bf153c..c75406e05 100644
--- a/rhel/automake.mk
+++ b/rhel/automake.mk
@@ -23,8 +23,6 @@ EXTRA_DIST += \
rhel/openvswitch.spec.in \
rhel/openvswitch-fedora.spec \
rhel/openvswitch-fedora.spec.in \
- rhel/ovn-fedora.spec \
- rhel/ovn-fedora.spec.in \
rhel/usr_share_openvswitch_scripts_ovs-systemd-reload \
rhel/usr_share_openvswitch_scripts_sysconfig.template \
rhel/usr_share_openvswitch_scripts_systemd_sysconfig.template \
@@ -34,12 +32,7 @@ EXTRA_DIST += \
rhel/usr_lib_systemd_system_ovsdb-server.service \
rhel/usr_lib_systemd_system_ovs-vswitchd.service.in \
rhel/usr_lib_systemd_system_ovs-delete-transient-ports.service \
- rhel/usr_lib_systemd_system_ovn-controller.service \
- rhel/usr_lib_systemd_system_ovn-controller-vtep.service \
- rhel/usr_lib_systemd_system_ovn-northd.service \
- rhel/usr_lib_systemd_system_openvswitch-ipsec.service \
- rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
- rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml
+ rhel/usr_lib_systemd_system_openvswitch-ipsec.service
DISTCLEANFILES += rhel/usr_lib_systemd_system_ovs-vswitchd.service
@@ -74,13 +67,6 @@ rpm-fedora: dist $(srcdir)/rhel/openvswitch-fedora.spec
-D "_topdir ${RPMBUILD_TOP}" \
-ba $(srcdir)/rhel/openvswitch-fedora.spec
-rpm-fedora-ovn: dist $(srcdir)/rhel/ovn-fedora.spec
- ${MKDIR_P} ${RPMBUILD_TOP}/SOURCES
- cp ${DIST_ARCHIVES} ${RPMBUILD_TOP}/SOURCES
- rpmbuild ${RPMBUILD_OPT} \
- -D "_topdir ${RPMBUILD_TOP}" \
- -ba $(srcdir)/rhel/ovn-fedora.spec
-
# Build kernel datapath RPM
rpm-fedora-kmod: dist $(srcdir)/rhel/openvswitch-kmod-fedora.spec
${MKDIR_P} ${RPMBUILD_TOP}/SOURCES
diff --git a/rhel/ovn-fedora.spec.in b/rhel/ovn-fedora.spec.in
deleted file mode 100644
index 2ecc629f2..000000000
--- a/rhel/ovn-fedora.spec.in
+++ /dev/null
@@ -1,432 +0,0 @@
-# Spec file for Open Virtual Network (OVN).
-
-# Copyright (C) 2018 Red Hat, Inc.
-#
-# Copying and distribution of this file, with or without modification,
-# are permitted in any medium without royalty provided the copyright
-# notice and this notice are preserved. This file is offered as-is,
-# without warranty of any kind.
-#
-# If tests have to be skipped while building, specify the '--without check'
-# option. For example:
-# rpmbuild -bb --without check rhel/ovn-fedora.spec
-#
-
-# If libcap-ng isn't available and there is no need for running OVS
-# as regular user, specify the '--without libcapng'
-%bcond_without libcapng
-
-# Enable Python 3 by specifying '--with build_python3'.
-# This is enabled by default for versions of the distribution that
-# have Python 3 by default (Fedora > 22).
-%bcond_with build_python3
-
-# Enable PIE, bz#955181
-%global _hardened_build 1
-
-# some distros (e.g: RHEL-7) don't define _rundir macro yet
-# Fedora 15 onwards uses /run as _rundir
-%if 0%{!?_rundir:1}
-%define _rundir /run
-%endif
-
-# define the python package prefix based on distribution version so that we can
-# simultaneously support RHEL-based and later Fedora versions in this spec file.
-%if 0%{?fedora} >= 25
-%define _py2 python2
-%endif
-
-%if 0%{?rhel} || 0%{?fedora} < 25
-%define _py2 python
-%endif
-
-Name: ovn
-Summary: Open Virtual Network support
-Group: System Environment/Daemons
-URL: http://www.openvswitch.org/
-Version: @VERSION@
-Obsoletes: openvswitch-ovn-common < %{?epoch:%{epoch}:}%{version}-%{release}
-Provides: openvswitch-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
-
-# Nearly all of openvswitch is ASL 2.0. The bugtool is LGPLv2+, and the
-# lib/sflow*.[ch] files are SISSL
-License: ASL 2.0 and LGPLv2+ and SISSL
-Release: 1%{?dist}
-Source: http://openvswitch.org/releases/openvswitch-%{version}.tar.gz
-
-BuildRequires: gcc gcc-c++
-BuildRequires: autoconf automake libtool
-BuildRequires: systemd-units openssl openssl-devel
-BuildRequires: %{_py2}-devel
-%if 0%{?fedora} > 22 || %{with build_python3}
-BuildRequires: python3-devel
-%endif
-BuildRequires: desktop-file-utils
-BuildRequires: groff graphviz
-BuildRequires: checkpolicy, selinux-policy-devel
-BuildRequires: /usr/bin/sphinx-build
-# make check dependencies
-BuildRequires: %{_py2}-twisted%{?rhel:-core} %{_py2}-zope-interface %{_py2}-six
-BuildRequires: procps-ng
-%if %{with libcapng}
-BuildRequires: libcap-ng libcap-ng-devel
-%endif
-BuildRequires: unbound unbound-devel
-
-Requires: openssl hostname iproute module-init-tools openvswitch
-
-Requires(post): systemd-units
-Requires(preun): systemd-units
-Requires(postun): systemd-units
-
-# to skip running checks, pass --without check
-%bcond_without check
-
-%description
-OVN, the Open Virtual Network, is a system to support virtual network
-abstraction. OVN complements the existing capabilities of OVS to add
-native support for virtual network abstractions, such as virtual L2 and L3
-overlays and security groups.
-
-%package central
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: ovn
-Requires: firewalld-filesystem
-Obsoletes: openvswitch-ovn-central
-Provides: openvswitch-ovn-central = %{?epoch:%{epoch}:}%{version}-%{release}
-
-%description central
-OVN DB servers and ovn-northd running on a central node.
-
-%package host
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: ovn
-Requires: firewalld-filesystem
-Obsoletes: openvswitch-ovn-host
-Provides: openvswitch-ovn-host = %{?epoch:%{epoch}:}%{version}-%{release}
-
-%description host
-OVN controller running on each host.
-
-%package vtep
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: ovn
-Obsoletes: openvswitch-ovn-vtep
-Provides: openvswitch-ovn-vtep = %{?epoch:%{epoch}:}%{version}-%{release}
-
-%description vtep
-OVN vtep controller
-
-%package docker
-Summary: Open Virtual Network support
-License: ASL 2.0
-Requires: ovn %{_py2}-openvswitch
-Obsoletes: openvswitch-ovn-docker
-Provides: openvswitch-ovn-docker = %{?epoch:%{epoch}:}%{version}-%{release}
-
-%description docker
-Docker network plugins for OVN.
-
-%prep
-%setup -n openvswitch-%{version}
-
-%build
-%configure \
-%if %{with libcapng}
- --enable-libcapng \
-%else
- --disable-libcapng \
-%endif
- --enable-ssl \
- --with-pkidir=%{_sharedstatedir}/openvswitch/pki \
-%if 0%{?fedora} > 22 || %{with build_python3}
- PYTHON3=%{__python3} \
- PYTHON=%{__python2}
-%else
- PYTHON=%{__python}
-%endif
-
-make %{?_smp_mflags}
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make install DESTDIR=$RPM_BUILD_ROOT
-
-for service in ovn-controller ovn-controller-vtep ovn-northd; do
- install -p -D -m 0644 \
- rhel/usr_lib_systemd_system_${service}.service \
- $RPM_BUILD_ROOT%{_unitdir}/${service}.service
-done
-
-rm -rf $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/
-
-install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/openvswitch
-
-install -d $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/
-install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
- $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ovn-central-firewall-service.xml
-install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
- $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ovn-host-firewall-service.xml
-
-install -d -m 0755 $RPM_BUILD_ROOT%{_prefix}/lib/ocf/resource.d/ovn
-ln -s %{_datadir}/openvswitch/scripts/ovndb-servers.ocf \
- $RPM_BUILD_ROOT%{_prefix}/lib/ocf/resource.d/ovn/ovndb-servers
-
-# remove OVS unpackages files
-rm -f $RPM_BUILD_ROOT%{_bindir}/ovs*
-rm -f $RPM_BUILD_ROOT%{_bindir}/vtep-ctl
-rm -f $RPM_BUILD_ROOT%{_sbindir}/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man1/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man5/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man5/vtep*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man7/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man8/ovs*
-rm -f $RPM_BUILD_ROOT%{_mandir}/man8/vtep*
-rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/ovs*
-rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/vswitch.ovsschema
-rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/vtep.ovsschema
-rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs*
-rm -rf $RPM_BUILD_ROOT%{_datadir}/openvswitch/bugtool-plugins
-rm -f $RPM_BUILD_ROOT%{_includedir}/openvswitch/*
-rm -f $RPM_BUILD_ROOT%{_includedir}/openflow/*
-rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
-rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
-rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
-rm -f $RPM_BUILD_ROOT%{_includedir}/openvswitch/*
-rm -f $RPM_BUILD_ROOT%{_includedir}/openflow/*
-rm -f $RPM_BUILD_ROOT%{_includedir}/ovn/*
-rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash
-rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash
-rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/openvswitch
-
-%check
-%if %{with check}
- touch resolv.conf
- export OVS_RESOLV_CONF=$(pwd)/resolv.conf
- if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :;
- else
- cat tests/testsuite.log
- exit 1
- fi
-%endif
-
-%clean
-rm -rf $RPM_BUILD_ROOT
-
-%pre central
-if [ $1 -eq 1 ] ; then
- # Package install.
- /bin/systemctl status ovn-northd.service >/dev/null
- ovn_status=$?
- rpm -ql openvswitch-ovn-central > /dev/null
- if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
- # ovn-northd service is running which means old openvswitch-ovn-central
- # is already installed and it will be cleaned up. So start ovn-northd
- # service when posttrans central is called.
- touch %{_localstatedir}/lib/rpm-state/ovn-northd
- fi
-fi
-
-%pre host
-if [ $1 -eq 1 ] ; then
- # Package install.
- /bin/systemctl status ovn-controller.service >/dev/null
- ovn_status=$?
- rpm -ql openvswitch-ovn-host > /dev/null
- if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
- # ovn-controller service is running which means old
- # openvswitch-ovn-host is installed and it will be cleaned up. So
- # start ovn-controller service when posttrans host is called.
- touch %{_localstatedir}/lib/rpm-state/ovn-controller
- fi
-fi
-
-%pre vtep
-if [ $1 -eq 1 ] ; then
- # Package install.
- /bin/systemctl status ovn-controller-vtep.service >/dev/null
- ovn_status=$?
- rpm -ql openvswitch-ovn-vtep > /dev/null
- if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
- # ovn-controller-vtep service is running which means old
- # openvswitch-ovn-vtep is installed and it will be cleaned up. So
- # start ovn-controller-vtep service when posttrans host is called.
- touch %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
- fi
-fi
-
-%preun central
-%if 0%{?systemd_preun:1}
- %systemd_preun ovn-northd.service
-%else
- if [ $1 -eq 0 ] ; then
- # Package removal, not upgrade
- /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || :
- /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || :
- fi
-%endif
-
-%preun host
-%if 0%{?systemd_preun:1}
- %systemd_preun ovn-controller.service
-%else
- if [ $1 -eq 0 ] ; then
- # Package removal, not upgrade
- /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || :
- /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || :
- fi
-%endif
-
-%preun vtep
-%if 0%{?systemd_preun:1}
- %systemd_preun ovn-controller-vtep.service
-%else
- if [ $1 -eq 0 ] ; then
- # Package removal, not upgrade
- /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || :
- /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || :
- fi
-%endif
-
-%post central
-%if 0%{?systemd_post:1}
- %systemd_post ovn-northd.service
-%else
- # Package install, not upgrade
- if [ $1 -eq 1 ]; then
- /bin/systemctl daemon-reload >dev/null || :
- fi
-%endif
-
-%post host
-%if 0%{?systemd_post:1}
- %systemd_post ovn-controller.service
-%else
- # Package install, not upgrade
- if [ $1 -eq 1 ]; then
- /bin/systemctl daemon-reload >dev/null || :
- fi
-%endif
-
-%post vtep
-%if 0%{?systemd_post:1}
- %systemd_post ovn-controller-vtep.service
-%else
- # Package install, not upgrade
- if [ $1 -eq 1 ]; then
- /bin/systemctl daemon-reload >dev/null || :
- fi
-%endif
-
-%postun
-
-%postun central
-%if 0%{?systemd_postun_with_restart:1}
- %systemd_postun_with_restart ovn-northd.service
-%else
- /bin/systemctl daemon-reload >/dev/null 2>&1 || :
- if [ "$1" -ge "1" ] ; then
- # Package upgrade, not uninstall
- /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || :
- fi
-%endif
-
-%postun host
-%if 0%{?systemd_postun_with_restart:1}
- %systemd_postun_with_restart ovn-controller.service
-%else
- /bin/systemctl daemon-reload >/dev/null 2>&1 || :
- if [ "$1" -ge "1" ] ; then
- # Package upgrade, not uninstall
- /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || :
- fi
-%endif
-
-%postun vtep
-%if 0%{?systemd_postun_with_restart:1}
- %systemd_postun_with_restart ovn-controller-vtep.service
-%else
- /bin/systemctl daemon-reload >/dev/null 2>&1 || :
- if [ "$1" -ge "1" ] ; then
- # Package upgrade, not uninstall
- /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || :
- fi
-%endif
-
-%posttrans central
-if [ $1 -eq 1 ]; then
- # Package install, not upgrade
- if [ -e %{_localstatedir}/lib/rpm-state/ovn-northd ]; then
- rm %{_localstatedir}/lib/rpm-state/ovn-northd
- /bin/systemctl start ovn-northd.service >/dev/null 2>&1 || :
- fi
-fi
-
-
-%posttrans host
-if [ $1 -eq 1 ]; then
- # Package install, not upgrade
- if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller ]; then
- rm %{_localstatedir}/lib/rpm-state/ovn-controller
- /bin/systemctl start ovn-controller.service >/dev/null 2>&1 || :
- fi
-fi
-
-%posttrans vtep
-if [ $1 -eq 1 ]; then
- # Package install, not upgrade
- if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller-vtep ]; then
- rm %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
- /bin/systemctl start ovn-controller-vtep.service >/dev/null 2>&1 || :
- fi
-fi
-
-%files
-%{_bindir}/ovn-nbctl
-%{_bindir}/ovn-sbctl
-%{_bindir}/ovn-trace
-%{_bindir}/ovn-detrace
-%{_datadir}/openvswitch/scripts/ovn-ctl
-%{_datadir}/openvswitch/scripts/ovndb-servers.ocf
-%{_datadir}/openvswitch/scripts/ovn-bugtool-nbctl-show
-%{_datadir}/openvswitch/scripts/ovn-bugtool-sbctl-lflow-list
-%{_datadir}/openvswitch/scripts/ovn-bugtool-sbctl-show
-%{_mandir}/man8/ovn-ctl.8*
-%{_mandir}/man8/ovn-nbctl.8*
-%{_mandir}/man8/ovn-trace.8*
-%{_mandir}/man1/ovn-detrace.1*
-%{_mandir}/man7/ovn-architecture.7*
-%{_mandir}/man8/ovn-sbctl.8*
-%{_mandir}/man5/ovn-nb.5*
-%{_mandir}/man5/ovn-sb.5*
-%{_prefix}/lib/ocf/resource.d/ovn/ovndb-servers
-
-%files docker
-%{_bindir}/ovn-docker-overlay-driver
-%{_bindir}/ovn-docker-underlay-driver
-
-%files central
-%{_bindir}/ovn-northd
-%{_mandir}/man8/ovn-northd.8*
-%config %{_datadir}/openvswitch/ovn-nb.ovsschema
-%config %{_datadir}/openvswitch/ovn-sb.ovsschema
-%{_unitdir}/ovn-northd.service
-%{_prefix}/lib/firewalld/services/ovn-central-firewall-service.xml
-
-%files host
-%{_bindir}/ovn-controller
-%{_mandir}/man8/ovn-controller.8*
-%{_unitdir}/ovn-controller.service
-%{_prefix}/lib/firewalld/services/ovn-host-firewall-service.xml
-
-%files vtep
-%{_bindir}/ovn-controller-vtep
-%{_mandir}/man8/ovn-controller-vtep.8*
-%{_unitdir}/ovn-controller-vtep.service
-
-%changelog
-* Thu Dec 20 2018 Numan Siddique <nusiddiq@redhat.com>
-- OVS/OVN split.
diff --git a/rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml b/rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml
deleted file mode 100644
index a005f325c..000000000
--- a/rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<service>
- <short>ovn-central-firewall-service</short>
- <description>Firewall service for ovn central</description>
- <port protocol="tcp" port="6641"/>
- <port protocol="tcp" port="6642"/>
-</service>
diff --git a/rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml b/rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml
deleted file mode 100644
index f606890c3..000000000
--- a/rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<service>
- <short>ovn-host-firewall-service</short>
- <description>Firewall service for ovn host</description>
- <port protocol="udp" port="6081"/>
-</service>
diff --git a/rhel/usr_lib_systemd_system_ovn-controller-vtep.service b/rhel/usr_lib_systemd_system_ovn-controller-vtep.service
deleted file mode 100644
index b1e239f57..000000000
--- a/rhel/usr_lib_systemd_system_ovn-controller-vtep.service
+++ /dev/null
@@ -1,50 +0,0 @@
-# See ovn-controller-vtep(8) for details about ovn-controller-vtep.
-#
-# You may override the following variables to customize ovn-controller-vtep
-# behavior:
-#
-# OVN_DB - Set this variable to the location of the ovsdb server that is
-# serving the OVN_Southbound database. See the manpage for
-# ovn-controller-vtep for more details on the format for the db
-# location.
-#
-# VTEP_DB - Set this variable to the location of the ovsdb server that is
-# serving the hardware_vtep database. See the manpage for
-# ovn-controller-vtep for more details on the format for the db
-# location.
-#
-# To override these variables, you may create a configuration file
-# in the /etc/systemd/system/ovn-controller-vtep.d/ directory. For example,
-# you could place the following contents in
-# /etc/systemd/system/ovn-controller-vtep.d/local.conf:
-#
-# [System]
-# Environment="OVN_DB=unix:/usr/local/var/run/openvswitch/db.sock" "VTEP_DB=unix:/usr/local/var/run/openvswitch/vtep.sock"
-#
-# Alternatively, you may specify environment variables in the file /etc/sysconfig/ovn-controller-vtep:
-#
-# OVN_DB="unix:/usr/local/var/run/openvswitch/db.sock"
-# VTEP_DB="unix:/usr/local/var/run/openvswitch/vtep.sock"
-
-[Unit]
-Description=OVN VTEP gateway controller daemon
-After=syslog.target
-Requires=openvswitch.service
-After=openvswitch.service
-
-[Service]
-Type=forking
-PIDFile=/var/run/openvswitch/ovn-controller-vtep.pid
-Restart=on-failure
-Environment=OVN_DB=unix:%t/openvswitch/ovnsb_db.sock
-Environment=VTEP_DB=unix:%t/openvswitch/db.sock
-EnvironmentFile=-/etc/sysconfig/ovn-controller-vtep
-EnvironmentFile=/run/openvswitch.useropts
-ExecStart=/usr/share/openvswitch/scripts/ovn-ctl \
- --db-sb-sock=${OVN_DB} --db-sock=${VTEP_DB} \
- --ovn-user=${OVS_USER_ID} \
- start_controller_vtep
-ExecStop=/usr/share/openvswitch/scripts/ovn-ctl stop_controller_vtep
-
-[Install]
-WantedBy=multi-user.target
diff --git a/rhel/usr_lib_systemd_system_ovn-controller.service b/rhel/usr_lib_systemd_system_ovn-controller.service
deleted file mode 100644
index 335cd5a52..000000000
--- a/rhel/usr_lib_systemd_system_ovn-controller.service
+++ /dev/null
@@ -1,34 +0,0 @@
-# See ovn-controller(8) for details about ovn-controller.
-#
-# To customize the ovn-controller service, you may create a configuration file
-# in the /etc/systemd/system/ovn-controller.d/ directory. For example, to specify
-# additional options to be passed to the "ovn-ctl start_controller" command, you
-# could place the following contents in
-# /etc/systemd/system/ovn-controller.d/local.conf:
-#
-# [System]
-# Environment="OVN_CONTROLLER_OPTS=--ovn-controller-log=-vconsole:emer -vsyslog:err -vfile:info"
-#
-# Alternatively, you may specify environment variables in the file /etc/sysconfig/ovn-controller:
-#
-# OVN_CONTROLLER_OPTS="--ovn-controller-log=-vconsole:emer -vsyslog:err -vfile:info"
-
-[Unit]
-Description=OVN controller daemon
-After=syslog.target
-Requires=openvswitch.service
-After=openvswitch.service
-
-[Service]
-Type=forking
-PIDFile=/var/run/openvswitch/ovn-controller.pid
-Restart=on-failure
-EnvironmentFile=-/etc/sysconfig/ovn-controller
-EnvironmentFile=/run/openvswitch.useropts
-ExecStart=/usr/share/openvswitch/scripts/ovn-ctl --no-monitor \
- --ovn-user=${OVS_USER_ID} \
- start_controller $OVN_CONTROLLER_OPTS
-ExecStop=/usr/share/openvswitch/scripts/ovn-ctl stop_controller
-
-[Install]
-WantedBy=multi-user.target
diff --git a/rhel/usr_lib_systemd_system_ovn-northd.service b/rhel/usr_lib_systemd_system_ovn-northd.service
deleted file mode 100644
index ea8c191e3..000000000
--- a/rhel/usr_lib_systemd_system_ovn-northd.service
+++ /dev/null
@@ -1,35 +0,0 @@
-# See ovn-northd(8) for details about ovn-northd.
-#
-# To customize the ovn-northd service, you may create a configuration file
-# in the /etc/systemd/system/ovn-northd.d/ directory. For example, to specify
-# additional options to be passed to the "ovn-ctl start_northd" command, you
-# could place the following contents in
-# /etc/systemd/system/ovn-northd.d/local.conf:
-#
-# [System]
-# Environment="OVN_NORTHD_OPTS=--db-nb-sock=/usr/local/var/run/openvswitch/ovnnb_db.sock --db-sb-sock=/usr/local/var/run/openvswitch/ovnsb_db.sock"
-#
-# Alternatively, you may specify environment variables in the file /etc/sysconfig/ovn-northd:
-#
-# OVN_NORTHD_OPTS="--db-nb-sock=/usr/local/var/run/openvswitch/ovnnb_db.sock --db-sb-sock=/usr/local/var/run/openvswitch/ovnsb_db.sock"
-
-[Unit]
-Description=OVN northd management daemon
-After=syslog.target
-Requires=openvswitch.service
-After=openvswitch.service
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-Environment=OVS_RUNDIR=%t/openvswitch OVS_DBDIR=/var/lib/openvswitch
-EnvironmentFile=-/etc/sysconfig/ovn-northd
-EnvironmentFile=/run/openvswitch.useropts
-ExecStartPre=-/usr/bin/chown -R ${OVS_USER_ID} ${OVS_DBDIR}
-ExecStart=/usr/share/openvswitch/scripts/ovn-ctl \
- --ovs-user=${OVS_USER_ID} --ovn-user=${OVS_USER_ID} \
- start_northd $OVN_NORTHD_OPTS
-ExecStop=/usr/share/openvswitch/scripts/ovn-ctl stop_northd
-
-[Install]
-WantedBy=multi-user.target
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 2e565d788..556f8681c 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -217,10 +217,6 @@ unset HTTPS_PROXY
unset FTP_PROXY
unset NO_PROXY
-# Avoid OVN environment variables leaking in from external environment.
-unset OVN_NB_DB
-unset OVN_SB_DB
-
# Prevent logging to syslog during tests.
OVS_SYSLOG_METHOD=null
export OVS_SYSLOG_METHOD
diff --git a/tests/automake.mk b/tests/automake.mk
index 908eb6666..0b4f29486 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -23,8 +23,7 @@ EXTRA_DIST += \
COMMON_MACROS_AT = \
tests/ovsdb-macros.at \
tests/ovs-macros.at \
- tests/ofproto-macros.at \
- tests/ovn-macros.at
+ tests/ofproto-macros.at
TESTSUITE_AT = \
tests/testsuite.at \
@@ -104,16 +103,9 @@ TESTSUITE_AT = \
tests/vlog.at \
tests/vtep-ctl.at \
tests/auto-attach.at \
- tests/ovn.at \
- tests/ovn-northd.at \
- tests/ovn-nbctl.at \
- tests/ovn-sbctl.at \
- tests/ovn-controller.at \
- tests/ovn-controller-vtep.at \
tests/mcast-snooping.at \
tests/packet-type-aware.at \
- tests/nsh.at \
- tests/ovn-performance.at
+ tests/nsh.at
EXTRA_DIST += $(FUZZ_REGRESSION_TESTS)
FUZZ_REGRESSION_TESTS = \
@@ -158,7 +150,6 @@ SYSTEM_KMOD_TESTSUITE_AT = \
SYSTEM_USERSPACE_TESTSUITE_AT = \
tests/system-userspace-testsuite.at \
- tests/system-ovn.at \
tests/system-userspace-macros.at \
tests/system-userspace-packet-type-aware.at
@@ -169,7 +160,6 @@ SYSTEM_AFXDP_TESTSUITE_AT = \
SYSTEM_TESTSUITE_AT = \
tests/system-common-macros.at \
- tests/system-ovn.at \
tests/system-layer3-tunnels.at \
tests/system-traffic.at \
tests/system-interface.at
@@ -197,7 +187,7 @@ SYSTEM_DPDK_TESTSUITE = $(srcdir)/tests/system-dpdk-testsuite
OVSDB_CLUSTER_TESTSUITE = $(srcdir)/tests/ovsdb-cluster-testsuite
DISTCLEANFILES += tests/atconfig tests/atlocal
-AUTOTEST_PATH = utilities:vswitchd:ovsdb:vtep:tests:$(PTHREAD_WIN32_DIR_DLL):$(SSL_DIR):ovn/controller-vtep:ovn/northd:ovn/utilities:ovn/controller
+AUTOTEST_PATH = utilities:vswitchd:ovsdb:vtep:tests:$(PTHREAD_WIN32_DIR_DLL):$(SSL_DIR):ovn/utilities
check-local:
set $(SHELL) '$(TESTSUITE)' -C tests AUTOTEST_PATH=$(AUTOTEST_PATH); \
@@ -238,9 +228,7 @@ check-lcov: all $(check_DATA) clean-lcov
# valgrind support
valgrind_wrappers = \
- tests/valgrind/ovn-controller \
tests/valgrind/ovn-nbctl \
- tests/valgrind/ovn-northd \
tests/valgrind/ovn-sbctl \
tests/valgrind/ovs-appctl \
tests/valgrind/ovs-ofctl \
@@ -447,7 +435,6 @@ tests_ovstest_SOURCES = \
tests/test-netflow.c \
tests/test-odp.c \
tests/test-ofpbuf.c \
- tests/test-ovn.c \
tests/test-packets.c \
tests/test-random.c \
tests/test-rcu.c \
@@ -475,7 +462,7 @@ tests_ovstest_SOURCES += \
tests/test-netlink-conntrack.c
endif
-tests_ovstest_LDADD = lib/libopenvswitch.la ovn/lib/libovn.la
+tests_ovstest_LDADD = lib/libopenvswitch.la
noinst_PROGRAMS += tests/test-stream
tests_test_stream_SOURCES = tests/test-stream.c
diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
index db0cd5108..04d4ed7e2 100644
--- a/tests/ofproto-macros.at
+++ b/tests/ofproto-macros.at
@@ -214,7 +214,7 @@ check_logs () {
# We most notably ignore 'Broken pipe' warnings. These often and
# intermittently appear in ovsdb-server.log, because *ctl commands
- # (e.g. ovs-vsctl, ovn-nbctl) exit right after committing a change to the
+ # (e.g. ovs-vsctl) exit right after committing a change to the
# database. However, in reaction, some daemon may immediately update the
# database, and this later update may cause database sending update back to
# *ctl command if *ctl has not exited yet. If *ctl command exits before
diff --git a/tests/oss-fuzz/automake.mk b/tests/oss-fuzz/automake.mk
index 5bf7d0d7c..2b116e7a5 100644
--- a/tests/oss-fuzz/automake.mk
+++ b/tests/oss-fuzz/automake.mk
@@ -2,7 +2,6 @@ OSS_FUZZ_TARGETS = \
tests/oss-fuzz/flow_extract_target \
tests/oss-fuzz/json_parser_target \
tests/oss-fuzz/ofp_print_target \
- tests/oss-fuzz/expr_parse_target \
tests/oss-fuzz/odp_target \
tests/oss-fuzz/miniflow_target \
tests/oss-fuzz/ofctl_parse_target
@@ -27,13 +26,6 @@ tests_oss_fuzz_ofp_print_target_SOURCES = \
tests_oss_fuzz_ofp_print_target_LDADD = lib/libopenvswitch.la
tests_oss_fuzz_ofp_print_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++
-tests_oss_fuzz_expr_parse_target_SOURCES = \
- tests/oss-fuzz/expr_parse_target.c \
- tests/oss-fuzz/fuzzer.h
-tests_oss_fuzz_expr_parse_target_LDADD = lib/libopenvswitch.la \
- ovn/lib/libovn.la
-tests_oss_fuzz_expr_parse_target_LDFLAGS = $(LIB_FUZZING_ENGINE) -lc++
-
tests_oss_fuzz_odp_target_SOURCES = \
tests/oss-fuzz/odp_target.c \
tests/oss-fuzz/fuzzer.h
@@ -56,11 +48,9 @@ EXTRA_DIST += \
tests/oss-fuzz/config/flow_extract_target.options \
tests/oss-fuzz/config/json_parser_target.options \
tests/oss-fuzz/config/ofp_print_target.options \
- tests/oss-fuzz/config/expr_parse_target.options \
tests/oss-fuzz/config/odp_target.options \
tests/oss-fuzz/config/miniflow_target.options \
tests/oss-fuzz/config/ofctl_parse_target.options \
tests/oss-fuzz/config/ovs.dict \
- tests/oss-fuzz/config/expr.dict \
tests/oss-fuzz/config/odp.dict \
tests/oss-fuzz/config/ofp-flow.dict
diff --git a/tests/oss-fuzz/config/expr.dict b/tests/oss-fuzz/config/expr.dict
deleted file mode 100644
index 03741ad7d..000000000
--- a/tests/oss-fuzz/config/expr.dict
+++ /dev/null
@@ -1,120 +0,0 @@
-" = "
-" = ("
-" = dns_lookup();"
-" { "
-" };"
-"!"
-"!="
-"$"
-"&&"
-"("
-"()"
-")"
-"),commit,table=,zone=NXM_NX_REG)"
-");"
-", "
-", meter=\"\""
-","
-",bucket=bucket_id=,weight:100,actions=ct(nat(dst="
-"--"
-".."
-"/"
-"/%"
-"0"
-":"
-"<"
-"<->"
-"<="
-"="
-"=="
-"=r"
-">"
-">="
-"["
-"\x00"
-"\x28"
-"]"
-"]:"
-"allow"
-"arp"
-"bool"
-"bswap "
-"bswap %0"
-"cc"
-"clone"
-"ct_clear"
-"ct_clear;"
-"ct_commit"
-"ct_commit("
-"ct_dnat"
-"ct_label"
-"ct_label="
-"ct_lb"
-"ct_mark"
-"ct_mark="
-"ct_mark=%#x"
-"ct_next"
-"ct_next;"
-"ct_snat"
-"decimal"
-"dhcpv6_stateful"
-"dhcpv6_stateless"
-"dns_lookup"
-"drop"
-"drop;"
-"egress"
-"error("
-"get_arp"
-"get_nd"
-"hexadecimal"
-"icmp4"
-"icmp6"
-"ingress"
-"ip.ttl"
-"ip.ttl--;"
-"ipv4"
-"ipv6"
-"log"
-"log("
-"mac"
-"meter"
-"name"
-"name=\"\", "
-"nd_na"
-"nd_na_router"
-"nd_ns"
-"next"
-"next();"
-"next(pipeline=, table=);"
-"next;"
-"output"
-"output;"
-"pipeline"
-"put_arp"
-"put_dhcp_opts"
-"put_dhcpv6_opts"
-"put_nd"
-"put_nd_ra_opts"
-"reject"
-"set_meter"
-"set_meter();"
-"set_meter(, );"
-"set_meter(,);"
-"set_queue"
-"set_queue();"
-"severity"
-"severity="
-"slaac"
-"static_routes"
-"str"
-"table"
-"tcp_reset"
-"type=select,selection_method=dp_hash"
-"uint16"
-"uint32"
-"uint8"
-"verdict"
-"verdict=, "
-"{"
-"||"
-"}"
diff --git a/tests/oss-fuzz/config/expr_parse_target.options b/tests/oss-fuzz/config/expr_parse_target.options
deleted file mode 100644
index fc254e84f..000000000
--- a/tests/oss-fuzz/config/expr_parse_target.options
+++ /dev/null
@@ -1,3 +0,0 @@
-[libfuzzer]
-dict = expr.dict
-close_fd_mask = 3
diff --git a/tests/oss-fuzz/expr_parse_target.c b/tests/oss-fuzz/expr_parse_target.c
deleted file mode 100644
index 170186d9d..000000000
--- a/tests/oss-fuzz/expr_parse_target.c
+++ /dev/null
@@ -1,464 +0,0 @@
-#include <config.h>
-#include "fuzzer.h"
-#include <errno.h>
-#include <getopt.h>
-#include <sys/wait.h>
-
-#include "command-line.h"
-#include "dp-packet.h"
-#include "fatal-signal.h"
-#include "flow.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/match.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofpbuf.h"
-#include "openvswitch/vlog.h"
-#include "ovn/actions.h"
-#include "ovn/expr.h"
-#include "ovn/lex.h"
-#include "ovn/logical-fields.h"
-#include "ovn/lib/ovn-l7.h"
-#include "ovn/lib/extend-table.h"
-#include "openvswitch/shash.h"
-#include "simap.h"
-#include "util.h"
-
-static void
-compare_token(const struct lex_token *a, const struct lex_token *b)
-{
- if (a->type != b->type) {
- fprintf(stderr, "type differs: %d -> %d\n", a->type, b->type);
- return;
- }
-
- if (!((a->s && b->s && !strcmp(a->s, b->s))
- || (!a->s && !b->s))) {
- fprintf(stderr, "string differs: %s -> %s\n",
- a->s ? a->s : "(null)",
- b->s ? b->s : "(null)");
- return;
- }
-
- if (a->type == LEX_T_INTEGER || a->type == LEX_T_MASKED_INTEGER) {
- if (memcmp(&a->value, &b->value, sizeof a->value)) {
- fprintf(stderr, "value differs\n");
- return;
- }
-
- if (a->type == LEX_T_MASKED_INTEGER
- && memcmp(&a->mask, &b->mask, sizeof a->mask)) {
- fprintf(stderr, "mask differs\n");
- return;
- }
-
- if (a->format != b->format
- && !(a->format == LEX_F_HEXADECIMAL
- && b->format == LEX_F_DECIMAL
- && a->value.integer == 0)) {
- fprintf(stderr, "format differs: %d -> %d\n",
- a->format, b->format);
- }
- }
-}
-
-static void
-test_lex(const char *input)
-{
- struct ds output;
-
- ds_init(&output);
- struct lexer lexer;
-
- lexer_init(&lexer, input);
- ds_clear(&output);
- while (lexer_get(&lexer) != LEX_T_END) {
- size_t len = output.length;
- lex_token_format(&lexer.token, &output);
-
- /* Check that the formatted version can really be parsed back
- * losslessly. */
- if (lexer.token.type != LEX_T_ERROR) {
- const char *s = ds_cstr(&output) + len;
- struct lexer l2;
-
- lexer_init(&l2, s);
- lexer_get(&l2);
- compare_token(&lexer.token, &l2.token);
- lexer_destroy(&l2);
- }
- ds_put_char(&output, ' ');
- }
- lexer_destroy(&lexer);
-
- ds_chomp(&output, ' ');
- puts(ds_cstr(&output));
- ds_destroy(&output);
-}
-
-static void
-create_symtab(struct shash *symtab)
-{
- ovn_init_symtab(symtab);
-
- /* For negative testing. */
- expr_symtab_add_field(symtab, "bad_prereq", MFF_XREG0, "xyzzy", false);
- expr_symtab_add_field(symtab, "self_recurse", MFF_XREG0,
- "self_recurse != 0", false);
- expr_symtab_add_field(symtab, "mutual_recurse_1", MFF_XREG0,
- "mutual_recurse_2 != 0", false);
- expr_symtab_add_field(symtab, "mutual_recurse_2", MFF_XREG0,
- "mutual_recurse_1 != 0", false);
- expr_symtab_add_string(symtab, "big_string", MFF_XREG0, NULL);
-}
-
-static void
-create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
- struct hmap *nd_ra_opts)
-{
- hmap_init(dhcp_opts);
- dhcp_opt_add(dhcp_opts, "offerip", 0, "ipv4");
- dhcp_opt_add(dhcp_opts, "netmask", 1, "ipv4");
- dhcp_opt_add(dhcp_opts, "router", 3, "ipv4");
- dhcp_opt_add(dhcp_opts, "dns_server", 6, "ipv4");
- dhcp_opt_add(dhcp_opts, "log_server", 7, "ipv4");
- dhcp_opt_add(dhcp_opts, "lpr_server", 9, "ipv4");
- dhcp_opt_add(dhcp_opts, "domain_name", 15, "str");
- dhcp_opt_add(dhcp_opts, "swap_server", 16, "ipv4");
- dhcp_opt_add(dhcp_opts, "policy_filter", 21, "ipv4");
- dhcp_opt_add(dhcp_opts, "router_solicitation", 32, "ipv4");
- dhcp_opt_add(dhcp_opts, "nis_server", 41, "ipv4");
- dhcp_opt_add(dhcp_opts, "ntp_server", 42, "ipv4");
- dhcp_opt_add(dhcp_opts, "server_id", 54, "ipv4");
- dhcp_opt_add(dhcp_opts, "tftp_server", 66, "ipv4");
- dhcp_opt_add(dhcp_opts, "classless_static_route", 121, "static_routes");
- dhcp_opt_add(dhcp_opts, "ip_forward_enable", 19, "bool");
- dhcp_opt_add(dhcp_opts, "router_discovery", 31, "bool");
- dhcp_opt_add(dhcp_opts, "ethernet_encap", 36, "bool");
- dhcp_opt_add(dhcp_opts, "default_ttl", 23, "uint8");
- dhcp_opt_add(dhcp_opts, "tcp_ttl", 37, "uint8");
- dhcp_opt_add(dhcp_opts, "mtu", 26, "uint16");
- dhcp_opt_add(dhcp_opts, "lease_time", 51, "uint32");
- dhcp_opt_add(dhcp_opts, "wpad", 252, "str");
-
- /* DHCPv6 options. */
- hmap_init(dhcpv6_opts);
- dhcp_opt_add(dhcpv6_opts, "server_id", 2, "mac");
- dhcp_opt_add(dhcpv6_opts, "ia_addr", 5, "ipv6");
- dhcp_opt_add(dhcpv6_opts, "dns_server", 23, "ipv6");
- dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str");
-
- /* IPv6 ND RA options. */
- hmap_init(nd_ra_opts);
- nd_ra_opts_init(nd_ra_opts);
-}
-
-static void
-create_addr_sets(struct shash *addr_sets)
-{
- shash_init(addr_sets);
-
- static const char *const addrs1[] = {
- "10.0.0.1", "10.0.0.2", "10.0.0.3",
- };
- static const char *const addrs2[] = {
- "::1", "::2", "::3",
- };
- static const char *const addrs3[] = {
- "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
- };
- static const char *const addrs4[] = { NULL };
-
- expr_const_sets_add(addr_sets, "set1", addrs1, 3, true);
- expr_const_sets_add(addr_sets, "set2", addrs2, 3, true);
- expr_const_sets_add(addr_sets, "set3", addrs3, 3, true);
- expr_const_sets_add(addr_sets, "set4", addrs4, 0, true);
-}
-
-static void
-create_port_groups(struct shash *port_groups)
-{
- shash_init(port_groups);
-
- static const char *const pg1[] = {
- "lsp1", "lsp2", "lsp3",
- };
- static const char *const pg2[] = { NULL };
-
- expr_const_sets_add(port_groups, "pg1", pg1, 3, false);
- expr_const_sets_add(port_groups, "pg_empty", pg2, 0, false);
-}
-
-static bool
-lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
-{
- const struct simap *ports = ports_;
- const struct simap_node *node = simap_find(ports, port_name);
- if (!node) {
- return false;
- }
- *portp = node->data;
- return true;
-}
-
-static bool
-is_chassis_resident_cb(const void *ports_, const char *port_name)
-{
- const struct simap *ports = ports_;
- const struct simap_node *node = simap_find(ports, port_name);
- if (node) {
- return true;
- }
- return false;
-}
-
-static void
-test_parse_actions(const char *input)
-{
- struct shash symtab;
- struct hmap dhcp_opts;
- struct hmap dhcpv6_opts;
- struct hmap nd_ra_opts;
- struct simap ports;
-
- create_symtab(&symtab);
- create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts);
-
- /* Initialize group ids. */
- struct ovn_extend_table group_table;
- ovn_extend_table_init(&group_table);
-
- /* Initialize meter ids for QoS. */
- struct ovn_extend_table meter_table;
- ovn_extend_table_init(&meter_table);
-
- simap_init(&ports);
- simap_put(&ports, "eth0", 5);
- simap_put(&ports, "eth1", 6);
- simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
-
- struct ofpbuf ovnacts;
- struct expr *prereqs;
- char *error;
-
- puts(input);
-
- ofpbuf_init(&ovnacts, 0);
-
- const struct ovnact_parse_params pp = {
- .symtab = &symtab,
- .dhcp_opts = &dhcp_opts,
- .dhcpv6_opts = &dhcpv6_opts,
- .nd_ra_opts = &nd_ra_opts,
- .n_tables = 24,
- .cur_ltable = 10,
- };
- error = ovnacts_parse_string(input, &pp, &ovnacts, &prereqs);
- if (!error) {
- /* Convert the parsed representation back to a string and print it,
- * if it's different from the input. */
- struct ds ovnacts_s = DS_EMPTY_INITIALIZER;
- ovnacts_format(ovnacts.data, ovnacts.size, &ovnacts_s);
- if (strcmp(input, ds_cstr(&ovnacts_s))) {
- printf(" formats as %s\n", ds_cstr(&ovnacts_s));
- }
-
- /* Encode the actions into OpenFlow and print. */
- const struct ovnact_encode_params ep = {
- .lookup_port = lookup_port_cb,
- .aux = &ports,
- .is_switch = true,
- .group_table = &group_table,
- .meter_table = &meter_table,
-
- .pipeline = OVNACT_P_INGRESS,
- .ingress_ptable = 8,
- .egress_ptable = 40,
- .output_ptable = 64,
- .mac_bind_ptable = 65,
- };
- struct ofpbuf ofpacts;
- ofpbuf_init(&ofpacts, 0);
- ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
- struct ds ofpacts_s = DS_EMPTY_INITIALIZER;
- struct ofpact_format_params fp = { .s = &ofpacts_s };
- ofpacts_format(ofpacts.data, ofpacts.size, &fp);
- printf(" encodes as %s\n", ds_cstr(&ofpacts_s));
- ds_destroy(&ofpacts_s);
- ofpbuf_uninit(&ofpacts);
-
- /* Print prerequisites if any. */
- if (prereqs) {
- struct ds prereqs_s = DS_EMPTY_INITIALIZER;
- expr_format(prereqs, &prereqs_s);
- printf(" has prereqs %s\n", ds_cstr(&prereqs_s));
- ds_destroy(&prereqs_s);
- }
-
- /* Now re-parse and re-format the string to verify that it's
- * round-trippable. */
- struct ofpbuf ovnacts2;
- struct expr *prereqs2;
- ofpbuf_init(&ovnacts2, 0);
- error = ovnacts_parse_string(ds_cstr(&ovnacts_s), &pp, &ovnacts2,
- &prereqs2);
- if (!error) {
- struct ds ovnacts2_s = DS_EMPTY_INITIALIZER;
- ovnacts_format(ovnacts2.data, ovnacts2.size, &ovnacts2_s);
- if (strcmp(ds_cstr(&ovnacts_s), ds_cstr(&ovnacts2_s))) {
- printf(" bad reformat: %s\n", ds_cstr(&ovnacts2_s));
- }
- ds_destroy(&ovnacts2_s);
- } else {
- printf(" reparse error: %s\n", error);
- free(error);
- }
- expr_destroy(prereqs2);
-
- ovnacts_free(ovnacts2.data, ovnacts2.size);
- ofpbuf_uninit(&ovnacts2);
- ds_destroy(&ovnacts_s);
- } else {
- printf(" %s\n", error);
- free(error);
- }
-
- expr_destroy(prereqs);
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
-
- simap_destroy(&ports);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
- dhcp_opts_destroy(&dhcp_opts);
- dhcp_opts_destroy(&dhcpv6_opts);
- nd_ra_opts_destroy(&nd_ra_opts);
- ovn_extend_table_destroy(&group_table);
- ovn_extend_table_destroy(&meter_table);
-}
-
-static void
-test_parse_expr(const char *input)
-{
- struct shash symtab;
- struct shash addr_sets;
- struct shash port_groups;
- struct simap ports;
- struct expr *expr;
- char *error;
-
- create_symtab(&symtab);
- create_addr_sets(&addr_sets);
- create_port_groups(&port_groups);
-
- simap_init(&ports);
- simap_put(&ports, "eth0", 5);
- simap_put(&ports, "eth1", 6);
- simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
- simap_put(&ports, "lsp1", 0x11);
- simap_put(&ports, "lsp2", 0x12);
- simap_put(&ports, "lsp3", 0x13);
-
- expr = expr_parse_string(input, &symtab, &addr_sets,
- &port_groups, NULL, &error);
- if (!error) {
- expr = expr_annotate(expr, &symtab, &error);
- }
- if (!error) {
- expr = expr_simplify(expr, is_chassis_resident_cb, &ports);
- expr = expr_normalize(expr);
- ovs_assert(expr_is_normalized(expr));
- }
- if (!error) {
- struct hmap matches;
-
- expr_to_matches(expr, lookup_port_cb, &ports, &matches);
- expr_matches_print(&matches, stdout);
- expr_matches_destroy(&matches);
- } else {
- puts(error);
- free(error);
- }
- expr_destroy(expr);
- simap_destroy(&ports);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
- expr_const_sets_destroy(&addr_sets);
- shash_destroy(&addr_sets);
- expr_const_sets_destroy(&port_groups);
- shash_destroy(&port_groups);
-}
-
-static bool
-lookup_atoi_cb(const void *aux OVS_UNUSED, const char *port_name,
- unsigned int *portp)
-{
- *portp = atoi(port_name);
- return true;
-}
-
-static void
-test_expr_to_packets(const char *input)
-{
- struct shash symtab;
- create_symtab(&symtab);
-
- struct flow uflow;
- char *error = expr_parse_microflow(input, &symtab, NULL, NULL,
- lookup_atoi_cb, NULL, &uflow);
- if (error) {
- puts(error);
- free(error);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
- return;
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- flow_compose(&packet, &uflow, NULL, 64);
-
- struct ds output = DS_EMPTY_INITIALIZER;
- const uint8_t *buf = dp_packet_data(&packet);
- for (int i = 0; i < dp_packet_size(&packet); i++) {
- uint8_t val = buf[i];
- ds_put_format(&output, "%02"PRIx8, val);
- }
- puts(ds_cstr(&output));
- ds_destroy(&output);
- dp_packet_uninit(&packet);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
-}
-
-int
-LLVMFuzzerTestOneInput(const uint8_t *input_, size_t size)
-{
- /* Bail out if we cannot construct at least a 1 char string. */
- const char *input = (const char *) input_;
- if (size < 2 || input[size - 1] != '\0' || strchr(input, '\n') ||
- strlen(input) != size - 1) {
- return 0;
- }
-
- /* Disable logging to avoid write to disk. */
- static bool isInit = false;
- if (!isInit) {
- vlog_set_verbosity("off");
- isInit = true;
- }
-
- /* Parse, annotate, simplify, normalize expr and convert to flows. */
- test_parse_expr(input);
-
- /* Parse actions. */
- test_parse_actions(input);
-
- /* Test OVN lexer. */
- test_lex(input);
-
- /* Expr to packets. */
- test_expr_to_packets(input);
-
- return 0;
-}
diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
deleted file mode 100644
index a3fe8cb88..000000000
--- a/tests/ovn-controller-vtep.at
+++ /dev/null
@@ -1,467 +0,0 @@
-AT_BANNER([ovn_controller_vtep])
-
-# OVN_CONTROLLER_VTEP_START
-#
-# Starts the test with a setup with vtep device. Each test case must first
-# call this macro.
-#
-# Uses vtep-ovs to simulate the vtep switch 'br-vtep' with two physical ports
-# 'p0', 'p1'.
-#
-# Configures ovn-nb with a logical switch 'br-test'.
-#
-#
-m4_define([OVN_CONTROLLER_VTEP_START],
- [
- AT_KEYWORDS([ovn])
- # this will cause skip when 'make check' using Windows setup.
- AT_SKIP_IF([test $HAVE_PYTHON = no])
-
- dnl Create databases (ovn-nb, ovn-sb, vtep).
- AT_CHECK([ovsdb-tool create vswitchd.db $abs_top_srcdir/vswitchd/vswitch.ovsschema])
- for daemon in ovn-nb ovn-sb vtep; do
- AT_CHECK([ovsdb-tool create $daemon.db $abs_top_srcdir/${daemon%%-*}/${daemon}.ovsschema])
- done
-
- dnl Start ovsdb-server.
- AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock vswitchd.db vtep.db], [0], [], [stderr])
- AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=ovsdb-nb-server.pid --log-file=ovsdb-nb-server.log --remote=punix:$OVS_RUNDIR/ovnnb_db.sock ovn-nb.db], [0], [], [stderr])
- AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=ovsdb-sb-server.pid --log-file=ovsdb-sb-server.log --remote=punix:$OVS_RUNDIR/ovnsb_db.sock ovn-sb.db ovn-sb.db], [0], [], [stderr])
- on_exit "kill `cat ovsdb-server.pid` `cat ovsdb-nb-server.pid` `cat ovsdb-sb-server.pid`"
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d
-/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
- AT_CAPTURE_FILE([ovsdb-server.log])
-
- dnl Start ovs-vswitchd.
- AT_CHECK([ovs-vswitchd --enable-dummy=system --disable-system --detach --no-chdir --pidfile --log-file -vvconn -vofproto_dpif], [0], [], [stderr])
- AT_CAPTURE_FILE([ovs-vswitchd.log])
- on_exit "kill `cat ovs-vswitchd.pid`"
- AT_CHECK([[sed < stderr '
-/ovs_numa|INFO|Discovered /d
-/vlog|INFO|opened log file/d
-/vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d
-/reconnect|INFO|/d
-/ofproto|INFO|using datapath ID/d
-/netlink_socket|INFO|netlink: could not enable listening to all nsid/d
-/ofproto|INFO|datapath ID changed to fedcba9876543210/d']])
- AT_CHECK([ovs-vsctl -- add-br br-vtep \
- -- set bridge br-vtep datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15]] fail-mode=secure \
- -- add-port br-vtep p0 -- set Interface p0 type=dummy ofport_request=1 \
- -- add-port br-vtep p1 -- set Interface p1 type=dummy ofport_request=2])
-
- dnl Start ovs-vtep.
- AT_CHECK([vtep-ctl add-ps br-vtep -- set Physical_Switch br-vtep tunnel_ips=1.2.3.4])
- AT_CHECK([ovs-vtep --log-file=ovs-vtep.log --pidfile=ovs-vtep.pid --detach --no-chdir br-vtep \], [0], [], [stderr])
- on_exit "kill `cat ovs-vtep.pid`"
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d']])
- # waits until ovs-vtep starts up.
- OVS_WAIT_UNTIL([test -n "`vtep-ctl show | grep Physical_Port`"])
-
- dnl Start ovn-northd.
- AT_CHECK([ovn-nbctl ls-add br-test])
- AT_CHECK([ovn-northd --detach --no-chdir --pidfile --log-file], [0], [], [stderr])
- on_exit "kill `cat ovn-northd.pid`"
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d']])
- AT_CAPTURE_FILE([ovn-northd.log])
-
- dnl Start ovn-controllger-vtep.
- AT_CHECK([ovn-controller-vtep --detach --no-chdir --pidfile --log-file --vtep-db=unix:$OVS_RUNDIR/db.sock --ovnsb-db=unix:$OVS_RUNDIR/ovnsb_db.sock], [0], [], [stderr])
- AT_CAPTURE_FILE([ovn-controller-vtep.log])
- on_exit "kill `cat ovn-controller-vtep.pid`"
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d
-/reconnect|INFO|/d']])
-])
-
-# OVN_CONTROLLER_VTEP_STOP
-#
-# So many exits... Yeah, we started a lot daemons~
-#
-m4_define([OVN_CONTROLLER_VTEP_STOP],
- [AT_CHECK([check_logs "$1"])
- OVS_APP_EXIT_AND_WAIT([ovs-vtep])
- OVS_APP_EXIT_AND_WAIT([ovn-northd])
- OVS_APP_EXIT_AND_WAIT([ovn-controller-vtep])
- OVS_APP_EXIT_AND_WAIT([ovsdb-server])
- OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])])
-
-# Adds logical port for a vtep gateway chassis in ovn-nb database.
-#
-# $1: logical switch name in ovn-nb database
-# $2: logical port name
-# $3: physical vtep gateway name
-# $4: logical switch name on vtep gateway chassis
-m4_define([OVN_NB_ADD_VTEP_PORT], [
-AT_CHECK([ovn-nbctl lsp-add $1 $2])
-
-AT_CHECK([ovn-nbctl lsp-set-type $2 vtep])
-AT_CHECK([ovn-nbctl lsp-set-options $2 vtep-physical-switch=$3 vtep-logical-switch=$4])
-])
-
-##############################################
-
-# tests chassis related updates.
-AT_SETUP([ovn-controller-vtep - chassis])
-OVN_CONTROLLER_VTEP_START
-
-# verifies the initial ovn-sb db configuration.
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl show | grep Chassis`"])
-AT_CHECK([ovn-sbctl show], [0], [dnl
-Chassis br-vtep
- Encap vxlan
- ip: "1.2.3.4"
- options: {csum="false"}
-])
-
-# deletes the chassis via ovn-sbctl and check that it is readded back
-# with the log.
-AT_CHECK([ovn-sbctl chassis-del br-vtep])
-OVS_WAIT_UNTIL([test -n "`grep WARN ovn-controller-vtep.log`"])
-AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log], [0], [dnl
-|WARN|Chassis for VTEP physical switch (br-vtep) disappears, maybe deleted by ovn-sbctl, adding it back
-])
-
-# changes the tunnel_ip on physical switch, watches the update of chassis's
-# encap.
-AT_CHECK([vtep-ctl set Physical_Switch br-vtep tunnel_ips=1.2.3.5])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl show | grep 1\.2\.3\.5`"])
-AT_CHECK([ovn-sbctl --columns=ip list Encap | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-"1.2.3.5"
-])
-
-# adds vlan_bindings to physical ports.
-AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0 -- bind-ls br-vtep p0 200 lswitch0 -- bind-ls br-vtep p1 300 lswitch0])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Chassis | grep -- lswitch0`"])
-AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f2 | tr -d ' ' ], [0], [dnl
-[[lswitch0]]
-])
-
-# adds another logical switch and new vlan_bindings.
-AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 300 lswitch1])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Chassis | grep -- lswitch1`"])
-AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-[[lswitch0,lswitch1]]
-])
-
-# unbinds one port from lswitch0, nothing should change.
-AT_CHECK([vtep-ctl unbind-ls br-vtep p0 200])
-OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=vlan_bindings list physical_port p0 | grep -- '200='`"])
-AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f2 | tr -d ' ' ], [0], [dnl
-[[lswitch0,lswitch1]]
-])
-
-# unbinds all ports from lswitch0.
-AT_CHECK([vtep-ctl unbind-ls br-vtep p0 100 -- unbind-ls br-vtep p1 300])
-OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch0`"])
-AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f2 | tr -d ' ' ], [0], [dnl
-[[lswitch1]]
-])
-
-# unbinds all ports from lswitch1.
-AT_CHECK([vtep-ctl unbind-ls br-vtep p0 300])
-OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch1`"])
-AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-[[]]
-])
-
-OVN_CONTROLLER_VTEP_STOP([/Chassis for VTEP physical switch (br-vtep) disappears/d])
-AT_CLEANUP
-
-
-# Tests binding updates.
-AT_SETUP([ovn-controller-vtep - binding 1])
-OVN_CONTROLLER_VTEP_START
-
-# adds logical switch 'lswitch0' and vlan_bindings.
-AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0 -- bind-ls br-vtep p1 300 lswitch0])
-# adds logical switch port in ovn-nb database, and sets the type and options.
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
-ovn-sbctl --timeout=10 wait-until Port_Binding br-vtep_lswitch0 chassis!='[[]]'
-# should see one binding, associated to chassis of 'br-vtep'.
-chassis_uuid=$(ovn-sbctl --columns=_uuid list Chassis br-vtep | cut -d ':' -f2 | tr -d ' ')
-AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding br-vtep_lswitch0 | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-${chassis_uuid}
-])
-
-# adds another logical switch 'lswitch1' and vlan_bindings.
-AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 200 lswitch1])
-# adds logical switch port in ovn-nb database for lswitch1.
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch1], [br-vtep], [lswitch1])
-ovn-sbctl --timeout=10 wait-until Port_Binding br-vtep_lswitch1 chassis!='[[]]'
-# This is allowed, but not recommended, to have two vlan_bindings (to different vtep logical switches)
-# from one vtep gateway physical port in one ovn-nb logical swithch.
-AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding | cut -d ':' -f2 | tr -d ' ' | sort], [0], [dnl
-
-${chassis_uuid}
-${chassis_uuid}
-])
-
-# adds another logical switch port in ovn-nb database for lswitch0.
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0_dup], [br-vtep], [lswitch0])
-ovn-sbctl --timeout=10 wait-until Port_Binding br-vtep_lswitch0_dup chassis!='[[]]'
-# it is not allowed to have more than one ovn-nb logical port for the same
-# vtep logical switch on a vtep gateway chassis, so should still see only
-# two port_binding entries bound.
-AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding | cut -d ':' -f2 | tr -d ' ' | sort | sort -d], [0], [dnl
-
-
-[[]]
-${chassis_uuid}
-${chassis_uuid}
-])
-# confirms the warning log.
-AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log | sed 's/([[-_0-9a-z]][[-_0-9a-z]]*)/()/g' | uniq], [0], [dnl
-|WARN|logical switch (), on vtep gateway chassis () has already been associated with logical port (), ignore logical port ()
-])
-
-# deletes physical ports from vtep.
-AT_CHECK([ovs-vsctl del-port p0 -- del-port p1])
-OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch`"])
-OVS_WAIT_UNTIL([test -z "`vtep-ctl list physical_port p0`"])
-OVS_WAIT_UNTIL([test -z "`vtep-ctl list physical_port p1`"])
-# should see empty chassis column in both binding entries.
-AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding | cut -d ':' -f2 | tr -d ' ' | sort], [0], [dnl
-
-
-[[]]
-[[]]
-[[]]
-])
-
-OVN_CONTROLLER_VTEP_STOP([/has already been associated with logical port/d])
-AT_CLEANUP
-
-
-# Tests corner case: Binding the vtep logical switch from two different
-# datapath.
-AT_SETUP([ovn-controller-vtep - binding 2])
-OVN_CONTROLLER_VTEP_START
-
-# adds logical switch 'lswitch0' and vlan_bindings.
-AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0])
-# adds logical switch port in ovn-nb database, and sets the type and options.
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
-ovn-sbctl --timeout=10 wait-until Port_Binding br-vtep_lswitch0 chassis!='[[]]'
-
-# adds another lswitch 'br-void' in ovn-nb database.
-AT_CHECK([ovn-nbctl ls-add br-void])
-# adds another vtep pswitch 'br-vtep-void' in vtep database.
-AT_CHECK([vtep-ctl add-ps br-vtep-void -- add-port br-vtep-void p0-void -- bind-ls br-vtep-void p0-void 100 lswitch0])
-# adds a conflicting logical port (both br-vtep_lswitch0 and br-vtep-void_lswitch0
-# are bound to the same logical switch, but they are on different datapath).
-OVN_NB_ADD_VTEP_PORT([br-void], [br-vtep-void_lswitch0], [br-vtep-void], [lswitch0])
-ovn-sbctl --timeout=10 wait-until Port_Binding br-vtep_lswitch0
-OVS_WAIT_UNTIL([test -n "`grep WARN ovn-controller-vtep.log`"])
-# confirms the warning log.
-AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log | sed 's/([[-_0-9a-z]][[-_0-9a-z]]*)/()/g;s/(with tunnel key [[0-9]][[0-9]]*)/()/g' | uniq], [0], [dnl
-|WARN|logical switch (), on vtep gateway chassis () has already been associated with logical datapath (), ignore logical port () which belongs to logical datapath ()
-])
-
-# then deletes 'br-void' and 'br-vtep-void', should see 'br-vtep_lswitch0'
-# bound correctly.
-AT_CHECK([ovn-nbctl ls-del br-void])
-# adds another vtep pswitch 'br-vtep-void' in vtep database.
-AT_CHECK([vtep-ctl del-ps br-vtep-void])
-OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Port_Binding | grep br-vtep-void_lswitch0`"])
-chassis_uuid=$(ovn-sbctl --columns=_uuid list Chassis br-vtep | cut -d ':' -f2 | tr -d ' ')
-AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding br-vtep_lswitch0 | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-${chassis_uuid}
-])
-
-OVN_CONTROLLER_VTEP_STOP([/has already been associated with logical datapath/d])
-AT_CLEANUP
-
-
-# Tests vtep module vtep logical switch tunnel key update.
-AT_SETUP([ovn-controller-vtep - vtep-lswitch])
-OVN_CONTROLLER_VTEP_START
-
-# creates the logical switch in vtep and adds the corresponding logical
-# port to 'br-test'.
-AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0])
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep -- br-vtep_lswitch0`"])
-
-# retrieves the expected tunnel key.
-datapath_uuid=$(ovn-sbctl --columns=datapath list Port_Binding br-vtep_lswitch0 | cut -d ':' -f2 | tr -d ' ')
-tunnel_key=$(ovn-sbctl --columns=tunnel_key list Datapath_Binding ${datapath_uuid} | cut -d ':' -f2 | tr -d ' ')
-OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=tunnel_key list Logical_Switch | grep 0`"])
-# checks the vtep logical switch tunnel key configuration.
-AT_CHECK_UNQUOTED([vtep-ctl --columns=tunnel_key list Logical_Switch | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-${tunnel_key}
-])
-
-# creates a second physical switch in vtep database, and binds its p0 vlan-100
-# to the same logical switch 'lswitch0'.
-AT_CHECK([vtep-ctl add-ps br-vtep-void -- add-port br-vtep-void p0 -- bind-ls br-vtep-void p0 100 lswitch0])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl --columns=name list Chassis | grep -- br-vtep-void`"])
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep-void_lswitch0], [br-vtep-void], [lswitch0])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep -- br-vtep-void_lswitch0`"])
-
-# checks the vtep logical switch tunnel key configuration.
-AT_CHECK_UNQUOTED([vtep-ctl --columns=tunnel_key list Logical_Switch | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-${tunnel_key}
-])
-
-# now, deletes br-vtep-void.
-AT_CHECK([vtep-ctl del-ps br-vtep-void])
-OVS_WAIT_UNTIL([test -z "`ovn-sbctl --columns=name list Chassis | grep -- br-vtep-void`"])
-# checks the vtep logical switch tunnel key configuration.
-AT_CHECK_UNQUOTED([vtep-ctl --columns=tunnel_key list Logical_Switch | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-${tunnel_key}
-])
-
-# changes the ovn-nb logical port type so that it is no longer
-# vtep port.
-AT_CHECK([ovn-nbctl lsp-set-type br-vtep_lswitch0 ""])
-OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=tunnel_key list Logical_Switch | grep 1`"])
-# now should see the tunnel key reset.
-AT_CHECK([vtep-ctl --columns=tunnel_key list Logical_Switch | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-0
-])
-
-OVN_CONTROLLER_VTEP_STOP
-AT_CLEANUP
-
-
-# Tests vtep module 'Ucast_Macs_Remote's.
-AT_SETUP([ovn-controller-vtep - vtep-macs 1])
-OVN_CONTROLLER_VTEP_START
-
-# creates a simple logical network with the vtep device and a fake hv chassis
-# 'ch0'.
-AT_CHECK([ovn-nbctl lsp-add br-test vif0])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif0 f0:ab:cd:ef:01:02])
-AT_CHECK([ovn-nbctl --timeout=10 --wait=sb sync])
-AT_CHECK([ovn-sbctl chassis-add ch0 vxlan 1.2.3.5])
-AT_CHECK([ovn-sbctl lsp-bind vif0 ch0])
-
-# creates the logical switch in vtep and adds the corresponding logical
-# port to 'br-test'.
-AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0])
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep br-vtep_lswitch0`"])
-
-# adds another lswitch 'br-void' in ovn-nb database.
-AT_CHECK([ovn-nbctl ls-add br-void])
-# adds fake hv chassis 'ch1'.
-AT_CHECK([ovn-nbctl lsp-add br-void vif1])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif1 f0:ab:cd:ef:01:02])
-AT_CHECK([ovn-nbctl --timeout=10 --wait=sb sync])
-AT_CHECK([ovn-sbctl chassis-add ch1 vxlan 1.2.3.6])
-AT_CHECK([ovn-sbctl lsp-bind vif1 ch1])
-
-# checks Ucast_Macs_Remote creation.
-OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Remote | grep _uuid`"])
-AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' '], [0], [dnl
-"f0:ab:cd:ef:01:02"
-])
-
-# checks physical locator creation.
-OVS_WAIT_UNTIL([test -n "`vtep-ctl list Physical_Locator | grep _uuid`"])
-AT_CHECK([vtep-ctl --columns=dst_ip list Physical_Locator | cut -d ':' -f2 | tr -d ' ' | grep -v 1.2.3.4 | sed '/^$/d'], [0], [dnl
-"1.2.3.5"
-])
-
-# checks tunnel creation by ovs-vtep.
-OVS_WAIT_UNTIL([test -n "`ovs-vsctl list Interface bfd1.2.3.5`"])
-AT_CHECK([ovs-vsctl --columns=options list Interface bfd1.2.3.5 | cut -d ':' -f2 | tr -d ' '], [0], [dnl
-{remote_ip="1.2.3.5"}
-])
-
-# adds another mac to logical switch port.
-AT_CHECK([ovn-nbctl lsp-set-addresses vif0 f0:ab:cd:ef:01:02 f0:ab:cd:ef:01:03])
-OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Remote | grep 03`"])
-AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl
-
-"f0:ab:cd:ef:01:02"
-"f0:ab:cd:ef:01:03"
-])
-
-# removes one mac to logical switch port.
-AT_CHECK([ovn-nbctl lsp-set-addresses vif0 f0:ab:cd:ef:01:03])
-OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=MAC list Ucast_Macs_Remote | grep 02`"])
-AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl
-"f0:ab:cd:ef:01:03"
-])
-
-# migrates mac to logical switch port vif1 on 'br-void'.
-AT_CHECK([ovn-nbctl lsp-set-addresses vif0])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif1 f0:ab:cd:ef:01:03])
-OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=MAC list Ucast_Macs_Remote | grep 03`"])
-AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl
-])
-
-OVN_CONTROLLER_VTEP_STOP
-AT_CLEANUP
-
-
-# Tests vtep module 'Ucast_Macs_Remote's (corner cases).
-AT_SETUP([ovn-controller-vtep - vtep-macs 2])
-OVN_CONTROLLER_VTEP_START
-
-# creates a simple logical network with the vtep device and a fake hv chassis
-# 'ch0'.
-AT_CHECK([ovn-nbctl lsp-add br-test vif0])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif0 f0:ab:cd:ef:01:02])
-AT_CHECK([ovn-nbctl --timeout=10 --wait=sb sync])
-AT_CHECK([ovn-sbctl chassis-add ch0 vxlan 1.2.3.5])
-AT_CHECK([ovn-sbctl lsp-bind vif0 ch0])
-
-# creates another vif in the same logical switch with duplicate mac.
-AT_CHECK([ovn-nbctl lsp-add br-test vif1])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif1 f0:ab:cd:ef:01:02])
-AT_CHECK([ovn-nbctl --timeout=10 --wait=sb sync])
-AT_CHECK([ovn-sbctl lsp-bind vif1 ch0])
-
-# creates the logical switch in vtep and adds the corresponding logical
-# port to 'br-test'.
-AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0])
-OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep br-vtep_lswitch0`"])
-
-# checks Ucast_Macs_Remote creation. Should still only be one entry, since duplicate
-# mac in the same logical switch is not allowed.
-OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Remote | grep _uuid`"])
-AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' '], [0], [dnl
-"f0:ab:cd:ef:01:02"
-])
-# confirms the warning log.
-OVS_WAIT_UNTIL([test -n "`grep WARN ovn-controller-vtep.log`"])
-AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log | sed 's/([[-_:0-9a-z]][[-_:0-9a-z]]*)/()/g' | uniq], [0], [dnl
-|WARN|MAC address () has already been known to be on logical port () in the same logical datapath, so just ignore this logical port ()
-])
-
-# deletes vif1.
-AT_CHECK([ovn-nbctl lsp-del vif1])
-
-# adds another lswitch 'br-void' in ovn-nb database.
-AT_CHECK([ovn-nbctl ls-add br-void])
-# adds fake hv chassis 'ch1' and vif1 with same mac address as vif0.
-AT_CHECK([ovn-nbctl lsp-add br-void vif1])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif1 f0:ab:cd:ef:01:02])
-AT_CHECK([ovn-nbctl --timeout=10 --wait=sb sync])
-AT_CHECK([ovn-sbctl chassis-add ch1 vxlan 1.2.3.6])
-AT_CHECK([ovn-sbctl lsp-bind vif1 ch1])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep vif1`"])
-
-# creates another logical switch in vtep and adds the corresponding logical
-# port to 'br-void'.
-AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 200 lswitch1])
-OVN_NB_ADD_VTEP_PORT([br-void], [br-void_lswitch1], [br-vtep], [lswitch1])
-OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep br-void_lswitch1`"])
-
-# checks Ucast_Macs_Remote creation. Should see two entries since it is allowed
-# to have duplicate macs in different logical switches.
-OVS_WAIT_UNTIL([test `vtep-ctl --columns=MAC list Ucast_Macs_Remote | grep 02 | wc -l` -gt 1])
-AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl
-
-"f0:ab:cd:ef:01:02"
-"f0:ab:cd:ef:01:02"
-])
-
-OVN_CONTROLLER_VTEP_STOP([/has already been known to be on logical port/d])
-AT_CLEANUP
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
deleted file mode 100644
index 343c2abed..000000000
--- a/tests/ovn-controller.at
+++ /dev/null
@@ -1,294 +0,0 @@
-AT_BANNER([ovn-controller])
-
-AT_SETUP([ovn-controller - ovn-bridge-mappings])
-AT_KEYWORDS([ovn])
-ovn_init_db ovn-sb
-net_add n1
-sim_add hv
-as hv
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0 \
- -- add-br br-eth1 \
- -- add-br br-eth2
-ovn_attach n1 br-phys 192.168.0.1
-
-# Waits until the OVS database contains exactly the specified patch ports.
-# Each argument should be of the form BRIDGE PORT PEER.
-check_patches () {
- # Generate code to check that the set of patch ports is exactly as
- # specified.
- echo 'ovs-vsctl -f csv -d bare --no-headings --columns=name find Interface type=patch | sort' > query
- for patch
- do
- echo $patch
- done | cut -d' ' -f 2 | sort > expout
-
- # Generate code to verify that the configuration of each patch
- # port is correct.
- for patch
- do
- set $patch; bridge=$1 port=$2 peer=$3
- echo >>query "ovs-vsctl iface-to-br $port -- get Interface $port type options"
- echo >>expout "$bridge
-patch
-{peer=$peer}"
- done
-
- # Run the query until we get the expected result (or until a timeout).
- #
- # (We use sed to drop all "s from output because ovs-vsctl quotes some
- # of the port names but not others.)
- AT_CAPTURE_FILE([query])
- AT_CAPTURE_FILE([expout])
- AT_CAPTURE_FILE([stdout])
- OVS_WAIT_UNTIL([. ./query | sed 's/"//g' > stdout #"
- diff -u stdout expout >/dev/null])
-}
-
-# Make sure that the configured bridge mappings in the Open_vSwitch db
-# is mirrored into the Chassis record in the OVN_Southbound db.
-check_bridge_mappings () {
- local_mappings=$1
- sysid=$(ovs-vsctl get Open_vSwitch . external_ids:system-id)
- OVS_WAIT_UNTIL([test x"${local_mappings}" = x$(ovn-sbctl get Chassis ${sysid} external_ids:ovn-bridge-mappings | sed -e 's/\"//g')])
-}
-
-# Initially there should be no patch ports.
-check_patches
-
-# Configure two ovn-bridge mappings, but no patch ports should be created yet
-AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0,physnet2:br-eth1])
-check_bridge_mappings "physnet1:br-eth0,physnet2:br-eth1"
-check_patches
-
-# Create a localnet port, but we should still have no patch ports, as they
-# won't be created until there's a localnet port on a logical switch with
-# another logical port bound to this chassis.
-ovn-sbctl \
- -- --id=@dp101 create Datapath_Binding tunnel_key=101 \
- -- create Port_Binding datapath=@dp101 logical_port=localnet1 tunnel_key=1 \
- type=localnet options:network_name=physnet1
-check_patches
-
-# Create a localnet port on a logical switch with a port bound to this chassis.
-# Now we should get some patch ports created.
-ovn-sbctl \
- -- --id=@dp102 create Datapath_Binding tunnel_key=102 \
- -- create Port_Binding datapath=@dp102 logical_port=localnet2 tunnel_key=1 \
- type=localnet options:network_name=physnet1 \
- -- create Port_Binding datapath=@dp102 logical_port=localvif2 tunnel_key=2
-ovs-vsctl add-port br-int localvif2 -- set Interface localvif2 external_ids:iface-id=localvif2
-check_patches \
- 'br-int patch-br-int-to-localnet2 patch-localnet2-to-br-int' \
- 'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
-
-# Add logical patch ports to connect new logical datapath.
-#
-# OVN no longer uses OVS patch ports to implement logical patch ports, so
-# the set of OVS patch ports doesn't change.
-AT_CHECK([ovn-sbctl \
- -- --id=@dp1 create Datapath_Binding tunnel_key=1 \
- -- --id=@dp2 create Datapath_Binding tunnel_key=2 \
- -- create Port_Binding datapath=@dp1 logical_port=foo tunnel_key=1 type=patch options:peer=bar \
- -- create Port_Binding datapath=@dp2 logical_port=bar tunnel_key=2 type=patch options:peer=foo \
- -- create Port_Binding datapath=@dp1 logical_port=dp1vif tunnel_key=3 \
-| uuidfilt], [0], [<0>
-<1>
-<2>
-<3>
-<4>
-])
-ovs-vsctl add-port br-int dp1vif -- set Interface dp1vif external_ids:iface-id=dp1vif
-check_patches \
- 'br-int patch-br-int-to-localnet2 patch-localnet2-to-br-int' \
- 'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
-
-# Delete the mapping and the ovn-bridge-mapping patch ports should go away.
-AT_CHECK([ovs-vsctl remove Open_vSwitch . external-ids ovn-bridge-mappings])
-check_bridge_mappings
-check_patches
-
-# Gracefully terminate daemons
-OVN_CLEANUP_SBOX([hv])
-OVN_CLEANUP_VSWITCH([main])
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-AT_CLEANUP
-
-# Checks that ovn-controller populates datapath-type and iface-types
-# correctly in the Chassis external-ids column.
-AT_SETUP([ovn-controller - Chassis external_ids])
-AT_KEYWORDS([ovn])
-ovn_init_db ovn-sb
-
-net_add n1
-sim_add hv
-as hv
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0 \
- -- add-br br-eth1 \
- -- add-br br-eth2
-ovn_attach n1 br-phys 192.168.0.1
-
-sysid=$(ovs-vsctl get Open_vSwitch . external_ids:system-id)
-
-# Make sure that the datapath_type set in the Bridge table
-# is mirrored into the Chassis record in the OVN_Southbound db.
-check_datapath_type () {
- datapath_type=$1
- chassis_datapath_type=$(ovn-sbctl get Chassis ${sysid} external_ids:datapath-type | sed -e 's/"//g') #"
- test "${datapath_type}" = "${chassis_datapath_type}"
-}
-
-OVS_WAIT_UNTIL([check_datapath_type ""])
-
-ovs-vsctl set Bridge br-int datapath-type=foo
-OVS_WAIT_UNTIL([check_datapath_type foo])
-
-# Change "ovn-bridge-mappings" value. It should not change the "datapath-type".
-ovs-vsctl set Open_vSwitch . external_ids:ovn-bridge-mappings=foo-mapping
-check_datapath_type foo
-
-ovs-vsctl set Bridge br-int datapath-type=bar
-OVS_WAIT_UNTIL([check_datapath_type bar])
-
-ovs-vsctl set Bridge br-int datapath-type=\"\"
-OVS_WAIT_UNTIL([check_datapath_type ""])
-
-# Set the datapath_type in external_ids:ovn-bridge-datapath-type.
-ovs-vsctl set Open_vSwitch . external_ids:ovn-bridge-datapath-type=foo
-OVS_WAIT_UNTIL([check_datapath_type foo])
-
-# Change the br-int's datapath type to bar.
-# It should be reset to foo since ovn-bridge-datapath-type is configured.
-ovs-vsctl set Bridge br-int datapath-type=bar
-OVS_WAIT_UNTIL([test foo=`ovs-vsctl get Bridge br-int datapath-type`])
-OVS_WAIT_UNTIL([check_datapath_type foo])
-
-ovs-vsctl set Open_vSwitch . external_ids:ovn-bridge-datapath-type=foobar
-OVS_WAIT_UNTIL([test foobar=`ovs-vsctl get Bridge br-int datapath-type`])
-OVS_WAIT_UNTIL([check_datapath_type foobar])
-
-expected_iface_types=$(ovs-vsctl get Open_vSwitch . iface_types | tr -d '[[]] ""')
-echo "expected_iface_types = ${expected_iface_types}"
-chassis_iface_types=$(ovn-sbctl get Chassis ${sysid} external_ids:iface-types | sed -e 's/\"//g')
-echo "chassis_iface_types = ${chassis_iface_types}"
-AT_CHECK([test "${expected_iface_types}" = "${chassis_iface_types}"])
-
-# Change the value of external_ids:iface-types using ovn-sbctl.
-# ovn-controller should again set it back to proper one.
-ovn-sbctl set Chassis ${sysid} external_ids:iface-types="foo"
-OVS_WAIT_UNTIL([
- chassis_iface_types=$(ovn-sbctl get Chassis ${sysid} external_ids:iface-types | sed -e 's/\"//g')
- echo "chassis_iface_types = ${chassis_iface_types}"
- test "${expected_iface_types}" = "${chassis_iface_types}"
-])
-
-# Change the value of external_ids:system-id and make sure it's mirrored
-# in the Chassis record in the OVN_Southbound database.
-sysid=${sysid}-foo
-ovs-vsctl set Open_vSwitch . external-ids:system-id="${sysid}"
-OVS_WAIT_UNTIL([
- chassis_id=$(ovn-sbctl get Chassis "${sysid}" name)
- test "${sysid}" = "${chassis_id}"
-])
-
-# Gracefully terminate daemons
-OVN_CLEANUP_SBOX([hv])
-OVN_CLEANUP_VSWITCH([main])
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-AT_CLEANUP
-
-# Checks that ovn-controller correctly maintains the mapping from the Encap
-# table in the Southbound database to OVS in the face of changes on both sides
-AT_SETUP([ovn-controller - change Encap properties])
-AT_KEYWORDS([ovn])
-ovn_init_db ovn-sb
-
-net_add n1
-sim_add hv
-as hv
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0 \
- -- add-br br-eth1 \
- -- add-br br-eth2
-ovn_attach n1 br-phys 192.168.0.1
-
-check_tunnel_property () {
- test "`ovs-vsctl get interface ovn-fakech-0 $1`" = "$2"
-}
-
-# Start off with a remote chassis supporting STT
-ovn-sbctl chassis-add fakechassis stt 192.168.0.2
-OVS_WAIT_UNTIL([check_tunnel_property type stt])
-
-# See if we switch to Geneve as the first choice when it is available
-# With multi-VTEP support we support tunnels with different IPs to the
-# same chassis, and hence use the IP to annotate the tunnel (along with
-# the chassis-id in ovn-chassis-id); if we supply a different IP here
-# we won't be able to co-relate this to the tunnel port that was created
-# in the previous step and, as a result, will end up creating another tunnel,
-# ie. we can't just lookup using "ovn-fakech-0". So, need to use the same IP
-# as above, i.e 192.168.0.2, here.
-encap_uuid=$(ovn-sbctl add chassis fakechassis encaps @encap -- --id=@encap create encap type=geneve ip="192.168.0.2")
-OVS_WAIT_UNTIL([check_tunnel_property type geneve])
-
-# Check that changes within an encap row are propagated
-ovn-sbctl set encap ${encap_uuid} ip=192.168.0.2
-OVS_WAIT_UNTIL([check_tunnel_property options:remote_ip "\"192.168.0.2\""])
-
-# Change the type on the OVS side and check than OVN fixes it
-ovs-vsctl set interface ovn-fakech-0 type=vxlan
-OVS_WAIT_UNTIL([check_tunnel_property type geneve])
-
-# Delete the port entirely and it should be resurrected
-ovs-vsctl del-port ovn-fakech-0
-OVS_WAIT_UNTIL([check_tunnel_property type geneve])
-
-# Gracefully terminate daemons
-OVN_CLEANUP_SBOX([hv])
-OVN_CLEANUP_VSWITCH([main])
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-AT_CLEANUP
-
-# Check ovn-controller connection status to Southbound database
-AT_SETUP([ovn-controller - check sbdb connection])
-AT_KEYWORDS([ovn])
-ovn_init_db ovn-sb
-
-net_add n1
-sim_add hv
-as hv
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0 \
- -- add-br br-eth1 \
- -- add-br br-eth2
-ovn_attach n1 br-phys 192.168.0.1
-
-check_sbdb_connection () {
- test "$(ovs-appctl -t ovn-controller connection-status)" = "$1"
-}
-
-OVS_WAIT_UNTIL([check_sbdb_connection connected])
-
-ovs-vsctl set open . external_ids:ovn-remote=tcp:192.168.0.10:6642
-OVS_WAIT_UNTIL([check_sbdb_connection 'not connected'])
-
-# reset the remote for clean-up
-ovs-vsctl set open . external_ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock
-# Gracefully terminate daemons
-OVN_CLEANUP_SBOX([hv])
-OVN_CLEANUP_VSWITCH([main])
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-AT_CLEANUP
diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at
deleted file mode 100644
index 7dba42c99..000000000
--- a/tests/ovn-macros.at
+++ /dev/null
@@ -1,180 +0,0 @@
-# OVN_CLEANUP_VSWITCH(sim)
-#
-# Gracefully terminate vswitch daemons in the
-# specified sandbox.
-m4_define([OVN_CLEANUP_VSWITCH],[
- as $1
- OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
- OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-])
-
-# OVN_CLEANUP_SBOX(sbox)
-#
-# Gracefully terminate OVN daemons in the specified
-# sandbox instance. The sandbox name "vtep" is treated
-# as a special case, and is assumed to have ovn-controller-vtep
-# and ovs-vtep daemons running instead of ovn-controller.
-m4_define([OVN_CLEANUP_SBOX],[
- as $1
- if test "$1" = "vtep"; then
- OVS_APP_EXIT_AND_WAIT([ovn-controller-vtep])
- OVS_APP_EXIT_AND_WAIT([ovs-vtep])
- else
- OVS_APP_EXIT_AND_WAIT([ovn-controller])
- fi
- OVN_CLEANUP_VSWITCH([$1])
-])
-
-# OVN_CLEANUP(sim [, sim ...])
-#
-# Gracefully terminate all OVN daemons, including those in the
-# specified sandbox instances.
-m4_define([OVN_CLEANUP],[
- m4_foreach([sbox], [$@], [
- OVN_CLEANUP_SBOX([sbox])
- ])
- as ovn-sb
- OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
- as ovn-nb
- OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
- as northd
- OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
- as northd-backup
- OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
- OVN_CLEANUP_VSWITCH([main])
-])
-
-m4_divert_push([PREPARE_TESTS])
-
-# ovn_init_db DATABASE
-#
-# Creates and initializes the given DATABASE (one of "ovn-sb" or "ovn-nb"),
-# starts its ovsdb-server instance, and sets the appropriate environment
-# variable (OVN_SB_DB or OVN_NB_DB) so that ovn-sbctl or ovn-nbctl uses the
-# database by default.
-#
-# Usually invoked from ovn_start.
-ovn_init_db () {
- echo "creating $1 database"
- local d=$ovs_base/$1
- mkdir "$d" || return 1
- : > "$d"/.$1.db.~lock~
- as $1 ovsdb-tool create "$d"/$1.db "$abs_top_srcdir"/ovn/$1.ovsschema
- as $1 start_daemon ovsdb-server --remote=punix:"$d"/$1.sock "$d"/$1.db
- local var=`echo $1_db | tr a-z- A-Z_`
- AS_VAR_SET([$var], [unix:$ovs_base/$1/$1.sock]); export $var
-}
-
-# ovn_start
-#
-# Creates and initializes ovn-sb and ovn-nb databases and starts their
-# ovsdb-server instance, sets appropriate environment variables so that
-# ovn-sbctl and ovn-nbctl use them by default, and starts ovn-northd running
-# against them.
-ovn_start () {
- ovn_init_db ovn-sb; ovn-sbctl init
- ovn_init_db ovn-nb; ovn-nbctl init
-
- echo "starting ovn-northd"
- mkdir "$ovs_base"/northd
- as northd start_daemon ovn-northd -v \
- --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock \
- --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
-
- echo "starting backup ovn-northd"
- mkdir "$ovs_base"/northd-backup
- as northd-backup start_daemon ovn-northd -v \
- --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock \
- --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
-}
-
-# Interconnection networks.
-#
-# When multiple sandboxed Open vSwitch instances exist, one will inevitably
-# want to connect them together. These commands allow for that. Conceptually,
-# an interconnection network is a switch for which these functions make it easy
-# to plug into other switches in other sandboxed Open vSwitch instances.
-# Interconnection networks are implemented as bridges in a switch named "main",
-# so to use interconnection networks please avoid working with that switch
-# directly.
-
-# net_add NETWORK
-#
-# Creates a new interconnection network named NETWORK.
-net_add () {
- test -d "$ovs_base"/main || sim_add main || return 1
- as main ovs-vsctl add-br "$1"
-}
-
-# net_attach NETWORK BRIDGE
-#
-# Adds a new port to BRIDGE in the default sandbox (as set with as()) and plugs
-# it into the NETWORK interconnection network. NETWORK must already have been
-# created by a previous invocation of net_add. The default sandbox must not be
-# "main".
-net_attach () {
- local net=$1 bridge=$2
-
- local port=${sandbox}_$bridge
- as main ovs-vsctl \
- -- add-port $net $port \
- -- set Interface $port options:pstream="punix:$ovs_base/main/$port.sock" options:rxq_pcap="$ovs_base/main/$port-rx.pcap" options:tx_pcap="$ovs_base/main/$port-tx.pcap" \
- || return 1
-
- ovs-vsctl \
- -- set Interface $bridge options:tx_pcap="$ovs_base/$sandbox/$bridge-tx.pcap" options:rxq_pcap="$ovs_base/$sandbox/$bridge-rx.pcap" \
- -- add-port $bridge ${bridge}_$net \
- -- set Interface ${bridge}_$net options:stream="unix:$ovs_base/main/$port.sock" options:rxq_pcap="$ovs_base/$sandbox/${bridge}_$net-rx.pcap" options:tx_pcap="$ovs_base/$sandbox/${bridge}_$net-tx.pcap" \
- || return 1
-}
-
-# ovn_attach NETWORK BRIDGE IP [MASKLEN]
-#
-# First, this command attaches BRIDGE to interconnection network NETWORK, just
-# like "net_attach NETWORK BRIDGE". Second, it configures (simulated) IP
-# address IP (with network mask length MASKLEN, which defaults to 24) on
-# BRIDGE. Finally, it configures the Open vSwitch database to work with OVN
-# and starts ovn-controller.
-ovn_attach() {
- local net=$1 bridge=$2 ip=$3 masklen=${4-24}
- net_attach $net $bridge || return 1
-
- mac=`ovs-vsctl get Interface $bridge mac_in_use | sed s/\"//g`
- arp_table="$arp_table $sandbox,$bridge,$ip,$mac"
- ovs-appctl netdev-dummy/ip4addr $bridge $ip/$masklen >/dev/null || return 1
- ovs-appctl ovs/route/add $ip/$masklen $bridge >/dev/null || return 1
- ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=$sandbox \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve,vxlan \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=$ip \
- -- add-br br-int \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \
- || return 1
- start_daemon ovn-controller || return 1
-}
-
-# OVN_POPULATE_ARP
-#
-# This pre-populates the ARP tables of all of the OVN instances that have been
-# started with ovn_attach(). That means that packets sent from one hypervisor
-# to another never get dropped or delayed by ARP resolution, which makes
-# testing easier.
-ovn_populate_arp__() {
- for e1 in $arp_table; do
- set `echo $e1 | sed 's/,/ /g'`; sb1=$1 br1=$2 ip=$3 mac=$4
- for e2 in $arp_table; do
- set `echo $e2 | sed 's/,/ /g'`; sb2=$1 br2=$2
- if test $sb1,$br1 != $sb2,$br2; then
- as $sb2 ovs-appctl tnl/neigh/set $br2 $ip $mac || return 1
- fi
- done
- done
-}
-m4_divert_pop([PREPARE_TESTS])
-
-m4_define([OVN_POPULATE_ARP], [AT_CHECK(ovn_populate_arp__, [0], [ignore])])
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
deleted file mode 100644
index d99d3af9b..000000000
--- a/tests/ovn-nbctl.at
+++ /dev/null
@@ -1,1660 +0,0 @@
-AT_BANNER([ovn-nbctl])
-
-OVS_START_SHELL_HELPERS
-# OVN_NBCTL_TEST_START
-m4_define([OVN_NBCTL_TEST_START],
- [AT_KEYWORDS([ovn])
- AT_CAPTURE_FILE([ovsdb-server.log])
- ovn_nbctl_test_start $1])
-ovn_nbctl_test_start() {
- dnl Create ovn-nb database.
- AT_CHECK([ovsdb-tool create ovn-nb.db $abs_top_srcdir/ovn/ovn-nb.ovsschema])
-
- dnl Start ovsdb-server.
- AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/ovnnb_db.sock ovn-nb.db], [0], [], [stderr])
- on_exit "kill `cat ovsdb-server.pid`"
- AS_CASE([$1],
- [daemon],
- [export OVN_NB_DAEMON=$(ovn-nbctl --pidfile --detach --no-chdir --log-file -vsocket_util:off)
- on_exit "kill `cat ovn-nbctl.pid`"],
- [direct], [],
- [*], [AT_FAIL_IF(:)])
- AT_CHECK([ovn-nbctl init])
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d
-/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
-}
-
-# OVN_NBCTL_TEST_STOP
-m4_define([OVN_NBCTL_TEST_STOP], [ovn_nbctl_test_stop])
-ovn_nbctl_test_stop() {
- AT_CHECK([check_logs "$1"])
- OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-}
-OVS_END_SHELL_HELPERS
-
-# OVN_NBCTL_TEST(NAME, TITLE, COMMANDS)
-m4_define([OVN_NBCTL_TEST],
- [OVS_START_SHELL_HELPERS
- $1() {
- $3
- }
- OVS_END_SHELL_HELPERS
-
- AT_SETUP([ovn-nbctl - $2 - direct])
- OVN_NBCTL_TEST_START direct
- $1
- OVN_NBCTL_TEST_STOP
- AT_CLEANUP
-
- AT_SETUP([ovn-nbctl - $2 - daemon])
- OVN_NBCTL_TEST_START daemon
- $1
- OVN_NBCTL_TEST_STOP
- AT_CLEANUP])
-
-OVN_NBCTL_TEST([ovn_nbctl_basic_switch], [basic switch commands], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl ls-list | uuidfilt], [0], [dnl
-<0> (ls0)
-])
-
-AT_CHECK([ovn-nbctl ls-add ls1])
-AT_CHECK([ovn-nbctl ls-list | uuidfilt], [0], [dnl
-<0> (ls0)
-<1> (ls1)
-])
-
-AT_CHECK([ovn-nbctl ls-del ls0])
-AT_CHECK([ovn-nbctl ls-list | uuidfilt], [0], [dnl
-<0> (ls1)
-])
-
-AT_CHECK([ovn-nbctl show ls0])
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl show ls0 | uuidfilt], [0],
- [switch <0> (ls0)
-])
-AT_CHECK([ovn-nbctl ls-add ls0], [1], [],
- [ovn-nbctl: ls0: a switch with this name already exists
-])
-AT_CHECK([ovn-nbctl --may-exist ls-add ls0])
-AT_CHECK([ovn-nbctl show ls0 | uuidfilt], [0],
- [switch <0> (ls0)
-])
-AT_CHECK([ovn-nbctl --add-duplicate ls-add ls0])
-AT_CHECK([ovn-nbctl --may-exist --add-duplicate ls-add ls0], [1], [],
- [ovn-nbctl: --may-exist and --add-duplicate may not be used together
-])
-AT_CHECK([ovn-nbctl ls-del ls0], [1], [],
- [ovn-nbctl: Multiple logical switches named 'ls0'. Use a UUID.
-])
-
-AT_CHECK([ovn-nbctl ls-del ls2], [1], [],
- [ovn-nbctl: ls2: switch name not found
-])
-AT_CHECK([ovn-nbctl --if-exists ls-del ls2])
-
-AT_CHECK([ovn-nbctl ls-add])
-AT_CHECK([ovn-nbctl ls-add])
-AT_CHECK([ovn-nbctl --add-duplicate ls-add], [1], [],
- [ovn-nbctl: --add-duplicate requires specifying a name
-])
-AT_CHECK([ovn-nbctl --may-exist ls-add], [1], [],
- [ovn-nbctl: --may-exist requires specifying a name
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_basic_lsp], [basic logical switch port commands], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0], [1], [],
- [ovn-nbctl: lp0: a port with this name already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lsp-add ls0 lp0])
-AT_CHECK([ovn-nbctl lsp-list ls0 | uuidfilt], [0], [dnl
-<0> (lp0)
-])
-
-AT_CHECK([ovn-nbctl lsp-add ls0 lp1])
-AT_CHECK([ovn-nbctl lsp-list ls0 | uuidfilt], [0], [dnl
-<0> (lp0)
-<1> (lp1)
-])
-
-AT_CHECK([ovn-nbctl ls-add ls1])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp1], [1], [],
- [ovn-nbctl: lp1: a port with this name already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lsp-add ls1 lp1], [1], [],
- [ovn-nbctl: lp1: port already exists but in switch ls0
-])
-AT_CHECK([ovn-nbctl --may-exist lsp-add ls0 lp1 lp0 5], [1], [],
- [ovn-nbctl: lp1: port already exists but has no parent
-])
-
-AT_CHECK([ovn-nbctl lsp-del lp1])
-AT_CHECK([ovn-nbctl lsp-list ls0 | uuidfilt], [0], [dnl
-<0> (lp0)
-])
-
-AT_CHECK([ovn-nbctl lsp-add ls0 lp2 lp3 5])
-AT_CHECK([ovn-nbctl --may-exist lsp-add ls0 lp2 lp4 5], [1], [],
- [ovn-nbctl: lp2: port already exists with different parent lp3
-])
-AT_CHECK([ovn-nbctl --may-exist lsp-add ls0 lp2 lp3 10], [1], [],
- [ovn-nbctl: lp2: port already exists with different tag_request 5
-])
-AT_CHECK([ovn-nbctl clear Logical_Switch_Port lp2 tag_request])
-AT_CHECK([ovn-nbctl --may-exist lsp-add ls0 lp2 lp3 5], [1], [],
- [ovn-nbctl: lp2: port already exists but has no tag_request
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lsp_get_ls], [lsp get ls], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
-
-AT_CHECK([ovn-nbctl lsp-get-ls lp0 | uuidfilt], [0], [dnl
-<0> (ls0)
-])
-
-AT_CHECK([ovn-nbctl lsp-get-ls lp1], [1], [],
- [ovn-nbctl: lp1: port name not found
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lport_addresses], [lport addresses], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
-AT_CHECK([ovn-nbctl lsp-get-addresses lp0], [0], [dnl
-])
-
-AT_CHECK([ovn-nbctl lsp-set-addresses lp0 00:11:22:33:44:55 unknown])
-AT_CHECK([ovn-nbctl lsp-get-addresses lp0], [0], [dnl
-00:11:22:33:44:55
-unknown
-])
-
-AT_CHECK([ovn-nbctl lsp-set-addresses lp0])
-AT_CHECK([ovn-nbctl lsp-get-addresses lp0], [0], [dnl
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_port_security], [port security], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
-AT_CHECK([ovn-nbctl lsp-get-addresses lp0], [0], [dnl
-])
-
-AT_CHECK([ovn-nbctl lsp-set-port-security lp0 aa:bb:cc:dd:ee:ff 00:11:22:33:44:55])
-AT_CHECK([ovn-nbctl lsp-get-port-security lp0], [0], [dnl
-00:11:22:33:44:55
-aa:bb:cc:dd:ee:ff
-])
-
-AT_CHECK([ovn-nbctl lsp-set-port-security lp0])
-AT_CHECK([ovn-nbctl lsp-get-port-security lp0], [0], [dnl
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_acls], [ACLs], [
-ovn_nbctl_test_acl() {
- AT_CHECK([ovn-nbctl $2 --log acl-add $1 from-lport 600 udp drop])
- AT_CHECK([ovn-nbctl $2 --log --name=test --severity=info acl-add $1 to-lport 500 udp drop])
- AT_CHECK([ovn-nbctl $2 acl-add $1 from-lport 400 tcp drop])
- AT_CHECK([ovn-nbctl $2 acl-add $1 to-lport 300 tcp drop])
- AT_CHECK([ovn-nbctl $2 acl-add $1 from-lport 200 ip drop])
- AT_CHECK([ovn-nbctl $2 acl-add $1 to-lport 100 ip drop])
- dnl Add duplicated ACL
- AT_CHECK([ovn-nbctl $2 acl-add $1 to-lport 100 ip drop], [1], [], [stderr])
- AT_CHECK([grep 'already existed' stderr], [0], [ignore])
- AT_CHECK([ovn-nbctl $2 --may-exist acl-add $1 to-lport 100 ip drop])
-
- AT_CHECK([ovn-nbctl $2 acl-list $1], [0], [dnl
-from-lport 600 (udp) drop log()
-from-lport 400 (tcp) drop
-from-lport 200 (ip) drop
- to-lport 500 (udp) drop log(name=test,severity=info)
- to-lport 300 (tcp) drop
- to-lport 100 (ip) drop
-])
-
- dnl Delete in one direction.
- AT_CHECK([ovn-nbctl $2 acl-del $1 to-lport])
- AT_CHECK([ovn-nbctl $2 acl-list $1], [0], [dnl
-from-lport 600 (udp) drop log()
-from-lport 400 (tcp) drop
-from-lport 200 (ip) drop
-])
-
- dnl Delete all ACLs.
- AT_CHECK([ovn-nbctl $2 acl-del $1])
- AT_CHECK([ovn-nbctl $2 acl-list $1], [0], [dnl
-])
-
- AT_CHECK([ovn-nbctl $2 acl-add $1 from-lport 600 udp drop])
- AT_CHECK([ovn-nbctl $2 acl-add $1 from-lport 400 tcp drop])
- AT_CHECK([ovn-nbctl $2 acl-add $1 from-lport 200 ip drop])
-
- dnl Delete a single flow.
- AT_CHECK([ovn-nbctl $2 acl-del $1 from-lport 400 tcp])
- AT_CHECK([ovn-nbctl $2 acl-list $1], [0], [dnl
-from-lport 600 (udp) drop
-from-lport 200 (ip) drop
-])
-}
-
-AT_CHECK([ovn-nbctl ls-add ls0])
-ovn_nbctl_test_acl ls0
-AT_CHECK([ovn-nbctl ls-add ls1])
-ovn_nbctl_test_acl ls1 --type=switch
-AT_CHECK([ovn-nbctl create port_group name=pg0], [0], [ignore])
-ovn_nbctl_test_acl pg0 --type=port-group
-
-dnl Test when port group doesn't exist
-AT_CHECK([ovn-nbctl --type=port-group acl-add pg1 to-lport 100 ip drop], [1], [], [dnl
-ovn-nbctl: pg1: port group name not found
-])
-
-dnl Test when same name exists in logical switches and portgroups
-AT_CHECK([ovn-nbctl create port_group name=ls0], [0], [ignore])
-AT_CHECK([ovn-nbctl acl-add ls0 to-lport 100 ip drop], [1], [], [stderr])
-AT_CHECK([grep 'exists in both' stderr], [0], [ignore])
-AT_CHECK([ovn-nbctl --type=port-group acl-add ls0 to-lport 100 ip drop], [0], [ignore])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_qos], [QoS], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 tcp dscp=63])
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 500 udp rate=100 burst=1000])
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 400 tcp dscp=0 rate=300 burst=3000])
-AT_CHECK([ovn-nbctl qos-add ls0 to-lport 300 tcp dscp=48])
-AT_CHECK([ovn-nbctl qos-add ls0 to-lport 200 ip rate=101])
-AT_CHECK([ovn-nbctl qos-add ls0 to-lport 100 ip4 dscp=13 rate=301 burst=30000])
-
-dnl Add duplicated qos
-AT_CHECK([ovn-nbctl qos-add ls0 to-lport 100 ip4 dscp=11 rate=302 burst=30002], [1], [], [stderr])
-AT_CHECK([grep 'already existed' stderr], [0], [ignore])
-AT_CHECK([ovn-nbctl --may-exist qos-add ls0 to-lport 100 ip4 dscp=11 rate=302 burst=30002])
-
-AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
-from-lport 600 (tcp) dscp=63
-from-lport 500 (udp) rate=100 burst=1000
-from-lport 400 (tcp) rate=300 burst=3000 dscp=0
- to-lport 300 (tcp) dscp=48
- to-lport 200 (ip) rate=101
- to-lport 100 (ip4) rate=301 burst=30000 dscp=13
-])
-
-dnl Delete in one direction.
-AT_CHECK([ovn-nbctl qos-del ls0 to-lport])
-AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
-from-lport 600 (tcp) dscp=63
-from-lport 500 (udp) rate=100 burst=1000
-from-lport 400 (tcp) rate=300 burst=3000 dscp=0
-])
-
-dnl Delete all qos_rules.
-AT_CHECK([ovn-nbctl qos-del ls0])
-AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
-])
-
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip rate=1000101])
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 400 tcp dscp=44])
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 200 ip burst=1000102 rate=301 dscp=19])
-
-dnl Delete a single flow.
-AT_CHECK([ovn-nbctl qos-del ls0 from-lport 400 tcp])
-AT_CHECK([ovn-nbctl qos-list ls0], [0], [dnl
-from-lport 600 (ip) rate=1000101
-from-lport 200 (ip) rate=301 burst=1000102 dscp=19
-])
-
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip rate=100010111111], [1], [],
-[ovn-nbctl: 100010111111: rate must be in the range 1...4294967295
-])
-
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip burst=100010111112 rate=100010], [1], [],
-[ovn-nbctl: 100010111112: burst must be in the range 1...4294967295
-])
-
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip dscp=-1], [1], [],
-[ovn-nbctl: -1: dscp must be in the range 0...63
-])
-
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip dscpa=-1], [1], [],
-[ovn-nbctl: dscpa=-1: supported arguments are "dscp=", "rate=", and "burst="
-])
-
-AT_CHECK([ovn-nbctl qos-add ls0 from-lport 600 ip burst=123], [1], [],
-[ovn-nbctl: Either "rate" and/or "dscp" must be specified
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_meters], [meters], [
-AT_CHECK([ovn-nbctl meter-add meter1 drop 10 kbps])
-AT_CHECK([ovn-nbctl meter-add meter2 drop 3 kbps 2])
-AT_CHECK([ovn-nbctl meter-add meter3 drop 100 kbps 200])
-AT_CHECK([ovn-nbctl meter-add meter4 drop 10 pktps 30])
-
-dnl Add duplicate meter name
-AT_CHECK([ovn-nbctl meter-add meter1 drop 10 kbps], [1], [], [stderr])
-AT_CHECK([grep 'already exists' stderr], [0], [ignore])
-
-dnl Add reserved meter name
-AT_CHECK([ovn-nbctl meter-add __meter1 drop 10 kbps], [1], [], [stderr])
-AT_CHECK([grep 'reserved' stderr], [0], [ignore])
-
-dnl Add meter with invalid rates
-AT_CHECK([ovn-nbctl meter-add meter5 drop 100010111111 kbps], [1], [],
-[ovn-nbctl: rate must be in the range 1...4294967295
-])
-
-dnl Add meter with invalid rates
-AT_CHECK([ovn-nbctl meter-add meter5 drop 100010111111 foo], [1], [],
-[ovn-nbctl: rate must be in the range 1...4294967295
-])
-
-AT_CHECK([ovn-nbctl meter-add meter5 drop 0 kbps], [1], [],
-[ovn-nbctl: rate must be in the range 1...4294967295
-])
-
-dnl Add meter with invalid burst
-AT_CHECK([ovn-nbctl meter-add meter5 drop 10 100010111111 kbps], [1], [],
-[ovn-nbctl: unit must be "kbps" or "pktps"
-])
-
-AT_CHECK([ovn-nbctl meter-list], [0], [dnl
-meter1: bands:
- drop: 10 kbps
-meter2: bands:
- drop: 3 kbps, 2 kb burst
-meter3: bands:
- drop: 100 kbps, 200 kb burst
-meter4: bands:
- drop: 10 pktps, 30 packet burst
-])
-
-dnl Delete a single meter.
-AT_CHECK([ovn-nbctl meter-del meter2])
-AT_CHECK([ovn-nbctl meter-list], [0], [dnl
-meter1: bands:
- drop: 10 kbps
-meter3: bands:
- drop: 100 kbps, 200 kb burst
-meter4: bands:
- drop: 10 pktps, 30 packet burst
-])
-
-dnl Delete all meters.
-AT_CHECK([ovn-nbctl meter-del])
-AT_CHECK([ovn-nbctl meter-list], [0], [dnl
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_nats], [NATs], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [],
-[ovn-nbctl: snatt: type must be one of "dnat", "snat" and "dnat_and_snat".
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2a 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0.2a: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2/24 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0.2/24: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2:80 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0.2:80: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2a], [1], [],
-[ovn-nbctl: 192.168.1.2a: should be an IPv4 address or network.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1], [1], [],
-[ovn-nbctl: 192.168.1: should be an IPv4 address or network.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2:80], [1], [],
-[ovn-nbctl: 192.168.1.2:80: should be an IPv4 address or network.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2/a], [1], [],
-[ovn-nbctl: 192.168.1.2/a: should be an IPv4 address or network.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2a], [1], [],
-[ovn-nbctl: 192.168.1.2a: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1], [1], [],
-[ovn-nbctl: 192.168.1: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2:80], [1], [],
-[ovn-nbctl: 192.168.1.2:80: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2/24], [1], [],
-[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2/24], [1], [],
-[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0], [1], [],
-[ovn-nbctl: lr-nat-add with logical_port must also specify external_mac.
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02:03], [1], [],
-[ovn-nbctl: logical_port and external_mac are only valid when type is "dnat_and_snat".
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02:03], [1], [],
-[ovn-nbctl: logical_port and external_mac are only valid when type is "dnat_and_snat".
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02:03], [1], [],
-[ovn-nbctl: lp0: port name not found
-])
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00:00:01:02], [1], [],
-[ovn-nbctl: invalid mac address 00:00:00:01:02.
-])
-
-dnl Add snat and dnat
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:01:02:03])
-AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-dnat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.2 192.168.1.3 00:00:00:01:02:03 lp0
-snat 30.0.0.1 192.168.1.0/24
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.10/24], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.0/24], [1], [],
-[ovn-nbctl: a NAT with this type (snat) and logical_ip (192.168.1.0/24) already exists
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.2: a NAT with this external_ip and logical_ip already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.3], [1], [],
-[ovn-nbctl: a NAT with this type (dnat) and external_ip (30.0.0.1) already exists
-])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2], [1], [],
-[ovn-nbctl: 30.0.0.1, 192.168.1.2: a NAT with this external_ip and logical_ip already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.3], [1], [],
-[ovn-nbctl: a NAT with this type (dnat_and_snat) and external_ip (30.0.0.1) already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:04:05:06])
-AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-dnat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.2 192.168.1.3 00:00:00:04:05:06 lp0
-snat 30.0.0.1 192.168.1.0/24
-])
-AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3])
-AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-dnat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.2 192.168.1.3
-snat 30.0.0.1 192.168.1.0/24
-])
-
-dnl Deletes the NATs
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.3], [1], [],
-[ovn-nbctl: no matching NAT with the type (dnat_and_snat) and external_ip (30.0.0.3)
-])
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat 30.0.0.2], [1], [],
-[ovn-nbctl: no matching NAT with the type (dnat) and external_ip (30.0.0.2)
-])
-AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 192.168.10.0/24], [1], [],
-[ovn-nbctl: no matching NAT with the type (snat) and logical_ip (192.168.10.0/24)
-])
-AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 192.168.10.0/24])
-
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.1])
-AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-dnat 30.0.0.1 192.168.1.2
-dnat_and_snat 30.0.0.2 192.168.1.3
-snat 30.0.0.1 192.168.1.0/24
-])
-
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat])
-AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
-TYPE EXTERNAL_IP LOGICAL_IP EXTERNAL_MAC LOGICAL_PORT
-dnat_and_snat 30.0.0.2 192.168.1.3
-snat 30.0.0.1 192.168.1.0/24
-])
-
-AT_CHECK([ovn-nbctl lr-nat-del lr0])
-AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [])
-AT_CHECK([ovn-nbctl lr-nat-del lr0])
-AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lbs], [LBs], [
-dnl Add two LBs.
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10:80a 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: 30.0.0.10:80a: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10:a80 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: 30.0.0.10:a80: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20 tcp], [1], [],
-[ovn-nbctl: 192.168.10.20: should be an IP address and a port number with : as a separator.
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.1a 192.168.10.10:80,192.168.10.20:80], [1], [],
-[ovn-nbctl: 30.0.0.1a: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0 192.168.10.10:80,192.168.10.20:80], [1], [],
-[ovn-nbctl: 30.0.0: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.10,192.168.10.20:80], [1], [],
-[ovn-nbctl: 192.168.10.20:80: should be an IP address.
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.10:a80], [1], [],
-[ovn-nbctl: 192.168.10.10:a80: should be an IP address.
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.10:], [1], [],
-[ovn-nbctl: 192.168.10.10:: should be an IP address.
-])
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 30.0.0.10 192.168.10.1a], [1], [],
-[ovn-nbctl: 192.168.10.1a: should be an IP address.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10: 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10 tcp], [1], [],
-[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:900 tcp], [1], [],
-[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
-])
-
-dnl Add ips to lb
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 ,,,192.168.10.10:80,,,,,])
-AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 ,,,192.168.10.10:80,,,,192.168.10.20:80,,,,])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80
-<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-])
-AT_CHECK([ovn-nbctl lb-del lb0])
-AT_CHECK([ovn-nbctl lb-del lb1])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80])
-AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 tcp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-])
-
-dnl Update the VIP of the lb1.
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080
-])
-
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 udp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080
-])
-
-dnl Config lb1 with another VIP.
-AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.20:80 192.168.10.10:80 udp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080
- udp 30.0.0.20:80 192.168.10.10:80
-])
-
-AT_CHECK([ovn-nbctl lb-del lb1 30.0.0.20:80])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080
-])
-
-dnl Add LBs whose vip is just an IP address.
-AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.30 192.168.10.10])
-AT_CHECK([ovn-nbctl lb-add lb3 30.0.0.30 192.168.10.10])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080
-<2> lb2 tcp/udp 30.0.0.30 192.168.10.10
-<3> lb3 tcp/udp 30.0.0.30 192.168.10.10
-])
-AT_CHECK([ovn-nbctl lb-del lb2 30.0.0.30])
-AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30])
-
-AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp])
-AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080
-<2> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80
-<3> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80
-])
-
-dnl If there are multiple load balancers with the same name, use a UUID to update/delete.
-AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-
-AT_CHECK([ovn-nbctl lb-del lb2], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:8080,192.168.10.20:8080 udp])
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:8080 192.168.10.10:8080,192.168.10.20:8080 udp])
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:9090 192.168.10.10:8080,192.168.10.20:8080 udp])
-AT_CHECK([ovn-nbctl lb-del lb0 30.0.0.10:80])
-AT_CHECK([ovn-nbctl lb-del lb1])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80
-<1> lb2 tcp 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80
-])
-
-dnl Add load balancer to logical switch.
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80])
-AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 udp])
-AT_CHECK([ovn-nbctl lb-add lb3 30.0.0.10 192.168.10.10,192.168.10.20])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl --may-exist ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
-
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20
-])
-
-AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20
-])
-
-AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1])
-AT_CHECK([ovn-nbctl ls-lb-del ls0 lb3])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [])
-AT_CHECK([ovn-nbctl --if-exists ls-lb-del ls0 lb1])
-
-dnl Remove all load balancers from logical switch.
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
-AT_CHECK([ovn-nbctl ls-lb-del ls0])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [])
-
-dnl Add load balancer to logical router.
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
-AT_CHECK([ovn-nbctl --may-exist lr-lb-add lr0 lb1])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
-
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<2> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20
-])
-
-AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb1 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
-<1> lb3 tcp/udp 30.0.0.10 192.168.10.10,192.168.10.20
-])
-
-AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1])
-AT_CHECK([ovn-nbctl lr-lb-del lr0 lb3])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [])
-AT_CHECK([ovn-nbctl --if-exists lr-lb-del lr0 lb1])
-
-dnl Remove all load balancers from logical router.
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
-AT_CHECK([ovn-nbctl lr-lb-del lr0])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [])
-
-dnl Remove load balancers after adding them to a logical router/switch.
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl lb-del lb0])
-AT_CHECK([ovn-nbctl lb-del lb1])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lbs_ipv6], [LBs IPv6], [
-dnl A bunch of commands that should fail
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 [[ae0f::10]]:80a [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
-[ovn-nbctl: [[ae0f::10]]:80a: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 [[ae0f::10]]:a80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
-[ovn-nbctl: [[ae0f::10]]:a80: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,fd0f::20 tcp], [1], [],
-[ovn-nbctl: fd0f::20: should be an IP address and a port number with : as a separator.
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10fff [[fd0f::10]]:80,fd0f::20 tcp], [1], [],
-[ovn-nbctl: ae0f::10fff: should be an IP address (or an IP address and a port number with : as a separator).
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10 [[fd0f::10]]:80,[[fd0f::20]]:80], [1], [],
-[ovn-nbctl: [[fd0f::10]]:80: should be an IP address.
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10 fd0f::10,[[fd0f::20]]:80], [1], [],
-[ovn-nbctl: [[fd0f::20]]:80: should be an IP address.
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10 [[fd0f::10]]:a80], [1], [],
-[ovn-nbctl: [[fd0f::10]]:a80: should be an IP address.
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10 [[fd0f::10]]:], [1], [],
-[ovn-nbctl: [[fd0f::10]]:: should be an IP address.
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 ae0f::10 fd0f::1001a], [1], [],
-[ovn-nbctl: fd0f::1001a: should be an IP address.
-])
-
-
-AT_CHECK([ovn-nbctl -vsocket_util:off lb-add lb0 [[ae0f::10]]: [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
-[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
-])
-
-
-AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10 tcp], [1], [],
-[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
-])
-
-
-AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:900 tcp], [1], [],
-[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [],
-[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [],
-[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 192.168.10.10:80], [1], [],
-[ovn-nbctl: 192.168.10.10:80: IP address family is different from VIP [[ae0f::10]]:80.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 ae0f::10], [1], [],
-[ovn-nbctl: ae0f::10: IP address family is different from VIP 30.0.0.10.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 [[ae0f::10]]:80], [1], [],
-[ovn-nbctl: [[ae0f::10]]:80: IP address family is different from VIP 30.0.0.10:80.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10])
-AT_CHECK([ovn-nbctl lb-add lb0 ae0f:0000:0000:0000:0000:0000:0000:0010 fd0f::20],
-[1], [], [ovn-nbctl: lb0: a load balancer with this vip (ae0f::10) already exists
-])
-
-AT_CHECK([ovn-nbctl lb-del lb0])
-
-dnl Add ips to lb
-AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,,])
-AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,[[fd0f::20]]:80,,,,])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80
-<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-])
-AT_CHECK([ovn-nbctl lb-del lb0])
-AT_CHECK([ovn-nbctl lb-del lb1])
-
-
-AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80])
-AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-])
-
-dnl Update the VIP of the lb1.
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080
-])
-
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 udp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080
-])
-
-dnl Config lb1 with another VIP.
-AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::20]]:80 [[fd0f::10]]:80 udp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080
- udp [[ae0f::20]]:80 [[fd0f::10]]:80
-])
-
-AT_CHECK([ovn-nbctl lb-del lb1 [[ae0f::20]]:80])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080
-])
-
-dnl Add LBs whose vip is just an IP address.
-AT_CHECK([ovn-nbctl lb-add lb2 ae0f::30 fd0f::10])
-AT_CHECK([ovn-nbctl lb-add lb3 ae0f::30 fd0f::10])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080
-<2> lb2 tcp/udp ae0f::30 fd0f::10
-<3> lb3 tcp/udp ae0f::30 fd0f::10
-])
-AT_CHECK([ovn-nbctl lb-del lb2 ae0f::30])
-AT_CHECK([ovn-nbctl lb-del lb3 ae0f::30])
-
-AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp])
-AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080
-<2> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80
-<3> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80
-])
-
-dnl If there are multiple load balancers with the same name, use a UUID to update/delete.
-AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-
-AT_CHECK([ovn-nbctl lb-del lb2], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp])
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:8080 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp])
-AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:9090 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp])
-AT_CHECK([ovn-nbctl lb-del lb0 [[ae0f::10]]:80])
-AT_CHECK([ovn-nbctl lb-del lb1])
-AT_CHECK([ovn-nbctl lb-list | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb2 tcp [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80
-])
-
-dnl Add load balancer to logical switch.
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80])
-AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 udp])
-AT_CHECK([ovn-nbctl lb-add lb3 ae0f::10 fd0f::10,fd0f::20])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl --may-exist ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
-
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<2> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20
-])
-
-AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20
-])
-
-AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1])
-AT_CHECK([ovn-nbctl ls-lb-del ls0 lb3])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [])
-AT_CHECK([ovn-nbctl --if-exists ls-lb-del ls0 lb1])
-
-dnl Remove all load balancers from logical switch.
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
-AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
-AT_CHECK([ovn-nbctl ls-lb-del ls0])
-AT_CHECK([ovn-nbctl ls-lb-list ls0 | uuidfilt], [0], [])
-
-dnl Add load balancer to logical router.
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
-AT_CHECK([ovn-nbctl --may-exist lr-lb-add lr0 lb1])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [],
-[ovn-nbctl: Multiple load balancers named 'lb2'. Use a UUID.
-])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
-
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb0 tcp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<2> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20
-])
-
-AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [dnl
-UUID LB PROTO VIP IPs
-<0> lb1 udp [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80
-<1> lb3 tcp/udp ae0f::10 fd0f::10,fd0f::20
-])
-
-AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1])
-AT_CHECK([ovn-nbctl lr-lb-del lr0 lb3])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [])
-AT_CHECK([ovn-nbctl --if-exists lr-lb-del lr0 lb1])
-
-dnl Remove all load balancers from logical router.
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
-AT_CHECK([ovn-nbctl lr-lb-del lr0])
-AT_CHECK([ovn-nbctl lr-lb-list lr0 | uuidfilt], [0], [])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_basic_lr], [basic logical router commands], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lr-list | uuidfilt], [0], [dnl
-<0> (lr0)
-])
-
-AT_CHECK([ovn-nbctl lr-add lr1])
-AT_CHECK([ovn-nbctl lr-list | uuidfilt], [0], [dnl
-<0> (lr0)
-<1> (lr1)
-])
-
-AT_CHECK([ovn-nbctl lr-del lr0])
-AT_CHECK([ovn-nbctl lr-list | uuidfilt], [0], [dnl
-<0> (lr1)
-])
-
-AT_CHECK([ovn-nbctl show lr0])
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl show lr0 | uuidfilt], [0],
- [router <0> (lr0)
-])
-AT_CHECK([ovn-nbctl lr-add lr0], [1], [],
- [ovn-nbctl: lr0: a router with this name already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lr-add lr0])
-AT_CHECK([ovn-nbctl show lr0 | uuidfilt], [0],
- [router <0> (lr0)
-])
-AT_CHECK([ovn-nbctl --add-duplicate lr-add lr0])
-AT_CHECK([ovn-nbctl --may-exist --add-duplicate lr-add lr0], [1], [],
- [ovn-nbctl: --may-exist and --add-duplicate may not be used together
-])
-AT_CHECK([ovn-nbctl lr-del lr0], [1], [],
- [ovn-nbctl: Multiple logical routers named 'lr0'. Use a UUID.
-])
-
-AT_CHECK([ovn-nbctl lr-del lr2], [1], [],
- [ovn-nbctl: lr2: router name not found
-])
-AT_CHECK([ovn-nbctl --if-exists lr-del lr2])
-
-AT_CHECK([ovn-nbctl lr-add])
-AT_CHECK([ovn-nbctl lr-add])
-AT_CHECK([ovn-nbctl --add-duplicate lr-add], [1], [],
- [ovn-nbctl: --add-duplicate requires specifying a name
-])
-AT_CHECK([ovn-nbctl --may-exist lr-add], [1], [],
- [ovn-nbctl: --may-exist requires specifying a name
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_basic_lrp], [basic logical router port commands], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp0: invalid mac address 00:00:00:01:02
-])
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03:04 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp0: invalid mac address 00:00:00:01:02:03:04
-])
-
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24])
-
-AT_CHECK([ovn-nbctl show lr0 | uuidfilt], [0], [dnl
-router <0> (lr0)
- port lrp0
- mac: "00:00:00:01:02:03"
- networks: [["192.168.1.1/24"]]
-])
-
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp0: a port with this name already exists
-])
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24])
-AT_CHECK([ovn-nbctl lrp-list lr0 | uuidfilt], [0], [dnl
-<0> (lrp0)
-])
-
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp1 00:00:00:01:02:03 192.168.1.1/24 peer=lrp1-peer])
-AT_CHECK([ovn-nbctl lrp-list lr0 | uuidfilt], [0], [dnl
-<0> (lrp0)
-<1> (lrp1)
-])
-
-AT_CHECK([ovn-nbctl lr-add lr1])
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp1 00:00:00:01:02:03 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp1: a port with this name already exists
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr1 lrp1 00:00:00:01:02:03 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp1: port already exists but in router lr0
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:04:05:06 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp1: port already exists with mac 00:00:00:01:02:03
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:01:02:03 192.168.1.1/24], [1], [],
- [ovn-nbctl: lrp1: port already exists with mismatching peer
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:01:02:03 10.0.0.1/24 peer=lrp1-peer], [1], [],
- [ovn-nbctl: lrp1: port already exists with different network
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:01:02:03 192.168.1.1/24 peer=lrp1-peer])
-
-AT_CHECK([ovn-nbctl lrp-del lrp1])
-AT_CHECK([ovn-nbctl lrp-list lr0 | uuidfilt], [0], [dnl
-<0> (lrp0)
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:01:02:03 192.168.1.1/24 10.0.0.1/24 peer=lrp1-peer])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:01:02:03 192.168.1.1/24 172.16.0.1/24 peer=lrp1-peer], [1], [],
- [ovn-nbctl: lrp1: port already exists with different network
-])
-
-AT_CHECK([ovn-nbctl --may-exist lrp-add lr0 lrp1 00:00:00:01:02:03 10.0.0.1/24 192.168.1.1/24 peer=lrp1-peer])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lrp_gw_chassi], [logical router port gateway chassis], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24])
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [])
-
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lp0 chassis1], [1], [],
-[ovn-nbctl: lp0: port name not found
-])
-
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lp0], [1], [],
-[ovn-nbctl: lp0: port name not found
-])
-
-AT_CHECK([ovn-nbctl lrp-del-gateway-chassis lp0 chassis1], [1], [],
-[ovn-nbctl: lp0: port name not found
-])
-
-AT_CHECK([ovn-nbctl lrp-del-gateway-chassis lrp0 chassis1], [1], [],
-[ovn-nbctl: chassis chassis1 is not added to logical port lrp0
-])
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis1])
-
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [dnl
-lrp0-chassis1 0
-])
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis1 10])
-
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [dnl
-lrp0-chassis1 10
-])
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis1 20])
-
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [dnl
-lrp0-chassis1 20
-])
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis2 5])
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [dnl
-lrp0-chassis1 20
-lrp0-chassis2 5
-])
-
-AT_CHECK([ovn-nbctl lrp-del-gateway-chassis lrp0 chassis1])
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [dnl
-lrp0-chassis2 5
-])
-
-AT_CHECK([ovn-nbctl lrp-del-gateway-chassis lrp0 chassis2])
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0])
-
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis1 1])
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis2 10])
-AT_CHECK([ovn-nbctl lrp-set-gateway-chassis lrp0 chassis3 5])
-AT_CHECK([ovn-nbctl lrp-get-gateway-chassis lrp0], [0], [dnl
-lrp0-chassis2 10
-lrp0-chassis3 5
-lrp0-chassis1 1
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lrp_enable], [logical router port enable and disable], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24])
-AT_CHECK([ovn-nbctl lrp-get-enabled lrp0], [0], [enabled
-])
-
-AT_CHECK([ovn-nbctl lrp-set-enabled lrp0 disabled])
-AT_CHECK([ovn-nbctl lrp-get-enabled lrp0], [0], [disabled
-])
-
-AT_CHECK([ovn-nbctl lrp-set-enabled lrp0 enabled])
-AT_CHECK([ovn-nbctl lrp-get-enabled lrp0], [0], [enabled
-])
-
-AT_CHECK([ovn-nbctl lrp-set-enabled lrp0 xyzzy], [1], [],
- [ovn-nbctl: xyzzy: state must be "enabled" or "disabled"
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_routes], [routes], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-
-dnl Check IPv4 routes
-AT_CHECK([ovn-nbctl lr-route-add lr0 0.0.0.0/0 192.168.0.1])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.1.0/24 11.0.1.1 lp0])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.1/24 11.0.0.2])
-
-dnl Add overlapping route with 10.0.0.1/24
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24 11.0.0.1], [1], [],
- [ovn-nbctl: duplicate prefix: 10.0.0.0/24
-])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111a/24 11.0.0.1], [1], [],
- [ovn-nbctl: bad prefix argument: 10.0.0.111a/24
-])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24a 11.0.0.1], [1], [],
- [ovn-nbctl: bad prefix argument: 10.0.0.111/24a
-])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24 11.0.0.1a], [1], [],
- [ovn-nbctl: bad next hop argument: 11.0.0.1a
-])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.111/24 11.0.0.1/24], [1], [],
- [ovn-nbctl: bad IPv4 nexthop argument: 11.0.0.1/24
-])
-AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1/64], [1], [],
- [ovn-nbctl: bad IPv6 nexthop argument: 2001:0db8:0:f103::1/64
-])
-
-AT_CHECK([ovn-nbctl --may-exist lr-route-add lr0 10.0.0.111/24 11.0.0.1])
-AT_CHECK([ovn-nbctl --policy=src-ip lr-route-add lr0 9.16.1.0/24 11.0.0.1])
-
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-IPv4 Routes
- 10.0.0.0/24 11.0.0.1 dst-ip
- 10.0.1.0/24 11.0.1.1 dst-ip lp0
- 9.16.1.0/24 11.0.0.1 src-ip
- 0.0.0.0/0 192.168.0.1 dst-ip
-])
-
-AT_CHECK([ovn-nbctl --may-exist lr-route-add lr0 10.0.0.111/24 11.0.0.1 lp1])
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-IPv4 Routes
- 10.0.0.0/24 11.0.0.1 dst-ip lp1
- 10.0.1.0/24 11.0.1.1 dst-ip lp0
- 9.16.1.0/24 11.0.0.1 src-ip
- 0.0.0.0/0 192.168.0.1 dst-ip
-])
-
-dnl Delete non-existent prefix
-AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.2.1/24], [1], [],
- [ovn-nbctl: no matching prefix: 10.0.2.0/24
-])
-AT_CHECK([ovn-nbctl --if-exists lr-route-del lr0 10.0.2.1/24])
-
-AT_CHECK([ovn-nbctl lr-route-del lr0 10.0.1.1/24])
-AT_CHECK([ovn-nbctl lr-route-del lr0 9.16.1.0/24])
-
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-IPv4 Routes
- 10.0.0.0/24 11.0.0.1 dst-ip lp1
- 0.0.0.0/0 192.168.0.1 dst-ip
-])
-
-AT_CHECK([ovn-nbctl lr-route-del lr0])
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-])
-
-dnl Check IPv6 routes
-AT_CHECK([ovn-nbctl lr-route-add lr0 0:0:0:0:0:0:0:0/0 2001:0db8:0:f101::1])
-AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0])
-AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1])
-
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-IPv6 Routes
- 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
- 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
- ::/0 2001:db8:0:f101::1 dst-ip
-])
-
-AT_CHECK([ovn-nbctl lr-route-del lr0 2001:0db8:0::/64])
-
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-IPv6 Routes
- 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
- ::/0 2001:db8:0:f101::1 dst-ip
-])
-
-AT_CHECK([ovn-nbctl lr-route-del lr0])
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-])
-
-dnl Check IPv4 and IPv6 routes
-AT_CHECK([ovn-nbctl lr-route-add lr0 0.0.0.0/0 192.168.0.1])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.1.1/24 11.0.1.1 lp0])
-AT_CHECK([ovn-nbctl lr-route-add lr0 10.0.0.1/24 11.0.0.1])
-AT_CHECK([ovn-nbctl lr-route-add lr0 0:0:0:0:0:0:0:0/0 2001:0db8:0:f101::1])
-AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:0::/64 2001:0db8:0:f102::1 lp0])
-AT_CHECK([ovn-nbctl lr-route-add lr0 2001:0db8:1::/64 2001:0db8:0:f103::1])
-
-AT_CHECK([ovn-nbctl lr-route-list lr0], [0], [dnl
-IPv4 Routes
- 10.0.0.0/24 11.0.0.1 dst-ip
- 10.0.1.0/24 11.0.1.1 dst-ip lp0
- 0.0.0.0/0 192.168.0.1 dst-ip
-
-IPv6 Routes
- 2001:db8::/64 2001:db8:0:f102::1 dst-ip lp0
- 2001:db8:1::/64 2001:db8:0:f103::1 dst-ip
- ::/0 2001:db8:0:f101::1 dst-ip
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_policies], [policies], [
-AT_CHECK([ovn-nbctl lr-add lr0])
-
-dnl Add policies with allow and drop actions
-AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop])
-AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.2.0/24" allow])
-AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip4.src == 2.1.1.0/24" allow])
-AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip4.src == 2.1.2.0/24" drop])
-AT_CHECK([ovn-nbctl lr-policy-add lr0 101 "ip6.src == 2002::/64" drop])
-
-dnl Add duplicated policy
-AT_CHECK([ovn-nbctl lr-policy-add lr0 100 "ip4.src == 1.1.1.0/24" drop], [1], [],
- [ovn-nbctl: Same routing policy already existed on the logical router lr0.
-])
-
-dnl Add duplicated policy
-AT_CHECK([ovn-nbctl lr-policy-add lr0 103 "ip4.src == 1.1.1.0/24" deny], [1], [],
- [ovn-nbctl: deny: action must be one of "allow", "drop", and "reroute"
-])
-
-dnl Delete by priority and match string
-AT_CHECK([ovn-nbctl lr-policy-del lr0 100 "ip4.src == 1.1.1.0/24"])
-AT_CHECK([ovn-nbctl lr-policy-list lr0], [0], [dnl
-Routing Policies
- 101 ip4.src == 2.1.1.0/24 allow
- 101 ip4.src == 2.1.2.0/24 drop
- 101 ip6.src == 2002::/64 drop
- 100 ip4.src == 1.1.2.0/24 allow
-])
-
-dnl Delete all policies for given priority
-AT_CHECK([ovn-nbctl lr-policy-del lr0 101])
-AT_CHECK([ovn-nbctl lr-policy-list lr0], [0], [dnl
-Routing Policies
- 100 ip4.src == 1.1.2.0/24 allow
-])
-
-dnl Add policy with reroute action
-AT_CHECK([ovn-nbctl lr-policy-add lr0 102 "ip4.src == 3.1.2.0/24" reroute 3.3.3.3])
-
-dnl Add policy with invalid reroute ip
-AT_CHECK([ovn-nbctl lr-policy-add lr0 103 "ip4.src == 3.1.2.0/24" reroute 3.3.3.x], [1], [],
- [ovn-nbctl: bad next hop argument: 3.3.3.x
-])
-
-dnl Add policy with reroute action
-AT_CHECK([ovn-nbctl lr-policy-add lr0 104 "ip6.src == 2001::/64" reroute 2002::5])
-
-dnl Add policy with invalid reroute ip
-AT_CHECK([ovn-nbctl lr-policy-add lr0 105 "ip6.src == 2001::/64" reroute 2002::x], [1], [],
- [ovn-nbctl: bad next hop argument: 2002::x
-])
-
-])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_lsp_types], [lsp types], [
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 lp0])
-
-dnl switchport type defaults to empty
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-
-])
-
-dnl The following are the valid entries for
-dnl switchport type
-AT_CHECK([ovn-nbctl lsp-set-type lp0 l2gateway])
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-l2gateway
-])
-
-AT_CHECK([ovn-nbctl lsp-set-type lp0 router])
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-router
-])
-
-AT_CHECK([ovn-nbctl lsp-set-type lp0 localnet])
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-localnet
-])
-
-AT_CHECK([ovn-nbctl lsp-set-type lp0 localport])
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-localport
-])
-
-AT_CHECK([ovn-nbctl lsp-set-type lp0 vtep])
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-vtep
-])
-
-dnl All of these are valid southbound port types but
-dnl should be rejected for northbound logical switch
-dnl ports.
-AT_CHECK([ovn-nbctl lsp-set-type lp0 l3gateway], [1], [], [dnl
-ovn-nbctl: Logical switch port type 'l3gateway' is unrecognized. Not setting type.
-])
-AT_CHECK([ovn-nbctl lsp-set-type lp0 patch], [1], [], [dnl
-ovn-nbctl: Logical switch port type 'patch' is unrecognized. Not setting type.
-])
-AT_CHECK([ovn-nbctl lsp-set-type lp0 chassisredirect], [1], [], [dnl
-ovn-nbctl: Logical switch port type 'chassisredirect' is unrecognized. Not setting type.
-])
-
-dnl switch port type should still be "vtep" since previous
-dnl commands failed.
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-vtep
-])
-
-dnl Attempt a nonsense type
-AT_CHECK([ovn-nbctl lsp-set-type lp0 eggs], [1], [], [dnl
-ovn-nbctl: Logical switch port type 'eggs' is unrecognized. Not setting type.
-])
-
-dnl Empty string should work too
-AT_CHECK([ovn-nbctl lsp-set-type lp0 ""])
-AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
-
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_connection], [connection], [
-AT_CHECK([ovn-nbctl --inactivity-probe=30000 set-connection ptcp:6641:127.0.0.1 punix:$OVS_RUNDIR/ovnnb_db.sock])
-AT_CHECK([ovn-nbctl list connection | grep inactivity_probe], [0], [dnl
-inactivity_probe : 30000
-inactivity_probe : 30000
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_dry_run_mode], [dry run mode], [
-dnl Check that dry run has no permanent effect.
-AT_CHECK([ovn-nbctl --dry-run ls-add ls0 -- ls-list | uuidfilt], [0], [dnl
-<0> (ls0)
-])
-AT_CHECK([ovn-nbctl ls-list | uuidfilt], [0], [dnl
-])
-
-dnl Check that dry-run mode is not sticky.
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl ls-list | uuidfilt], [0], [dnl
-<0> (ls0)
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_oneline_output], [oneline output], [
-AT_CHECK([ovn-nbctl ls-add ls0 -- ls-add ls1])
-
-dnl Expect one line for one command.
-AT_CHECK([ovn-nbctl --oneline ls-list | uuidfilt], [0], [dnl
-<0> (ls0)\n<1> (ls1)
-])
-
-dnl Expect lines for two commands.
-AT_CHECK([ovn-nbctl --oneline ls-list -- ls-list | uuidfilt], [0], [dnl
-<0> (ls0)\n<1> (ls1)
-<0> (ls0)\n<1> (ls1)
-])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_error_paths], [commands parser error paths], [
-dnl FIXME: Duplicate options are allowed when passed with global options.
-dnl For example: ovn-nbctl --if-exists --if-exists list Logical_Switch
-
-dnl Duplicate option
-AT_CHECK([ovn-nbctl -- --if-exists --if-exists list Logical_Switch], [1], [], [stderr])
-AT_CHECK([grep 'option specified multiple times' stderr], [0], [ignore])
-
-dnl Missing command
-AT_CHECK([ovn-nbctl], [1], [], [stderr])
-AT_CHECK([grep 'missing command name' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl --if-exists], [1], [], [stderr])
-AT_CHECK([grep 'missing command name' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl --], [1], [], [stderr])
-AT_CHECK([grep 'missing command name' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- --if-exists], [1], [], [stderr])
-AT_CHECK([grep 'missing command name' stderr], [0], [ignore])
-
-dnl Unknown command
-AT_CHECK([ovn-nbctl foo], [1], [], [stderr])
-AT_CHECK([grep 'unknown command' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- foo], [1], [], [stderr])
-AT_CHECK([grep 'unknown command' stderr], [0], [ignore])
-
-dnl Unknown option
-AT_CHECK([ovn-nbctl --foo list Logical_Switch], [1], [], [stderr])
-AT_CHECK([grep 'unrecognized option' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- --foo list Logical_Switch], [1], [], [stderr])
-AT_CHECK([grep 'command has no .* option' stderr], [0], [ignore])
-
-dnl Missing option argument
-AT_CHECK([ovn-nbctl --columns], [1], [], [stderr])
-AT_CHECK([grep 'option .* requires an argument' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- --columns list Logical_Switch], [1], [], [stderr])
-AT_CHECK([grep 'missing argument to .* option' stderr], [0], [ignore])
-
-dnl Unexpected option argument
-AT_CHECK([ovn-nbctl --if-exists=foo list Logical_Switch], [1], [], [stderr])
-AT_CHECK([egrep 'option .* doesn'\''t allow an argument|option .* requires an argument' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- --if-exists=foo list Logical_Switch], [1], [], [stderr])
-AT_CHECK([grep 'option on .* does not accept an argument' stderr], [0], [ignore])
-
-dnl Not enough arguments
-AT_CHECK([ovn-nbctl list], [1], [], [stderr])
-AT_CHECK([grep 'command requires at least .* arguments' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- list], [1], [], [stderr])
-AT_CHECK([grep 'command requires at least .* arguments' stderr], [0], [ignore])
-
-dnl Too many arguments
-AT_CHECK([ovn-nbctl show foo bar], [1], [], [stderr])
-AT_CHECK([grep 'command takes at most .* arguments' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- show foo bar], [1], [], [stderr])
-AT_CHECK([grep 'command takes at most .* arguments' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl show foo --bar], [1], [], [stderr])
-AT_CHECK([grep 'command takes at most .* arguments' stderr], [0], [ignore])
-
-AT_CHECK([ovn-nbctl -- show foo --bar], [1], [], [stderr])
-AT_CHECK([grep 'command takes at most .* arguments' stderr], [0], [ignore])])
-
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_port_groups], [port groups], [
-dnl Check that port group can be looked up by name
-AT_CHECK([ovn-nbctl create Port_Group name=pg0], [0], [ignore])
-AT_CHECK([ovn-nbctl get Port_Group pg0 name], [0], [dnl
-pg0
-])])
-
-OVN_NBCTL_TEST([ovn_nbctl_extra_newlines], [extra newlines], [
-dnl This test addresses a specific issue seen when running ovn-nbctl in
-dnl daemon mode. All we have to do is ensure that each time we list database
-dnl information, there is not an extra newline at the beginning of the output.
-AT_CHECK([ovn-nbctl ls-add sw1], [0], [ignore])
-AT_CHECK([ovn-nbctl --columns=name list logical_switch sw1], [0], [dnl
-name : sw1
-])
-AT_CHECK([ovn-nbctl --columns=name list logical_switch sw1], [0], [dnl
-name : sw1
-])])
-
-OVN_NBCTL_TEST([ovn_nbctl_table_formatting], [table formatting], [
-dnl This test addresses a specific issue seen when running ovn-nbctl in
-dnl daemon mode. We need to ensure that table formatting options are honored
-dnl when listing database information.
-AT_CHECK([ovn-nbctl ls-add sw1], [0], [ignore])
-AT_CHECK([ovn-nbctl --bare --columns=name list logical_switch sw1], [0], [dnl
-sw1
-])])
-dnl ---------------------------------------------------------------------
-
-OVN_NBCTL_TEST([ovn_nbctl_port_group_commands], [port group commands], [
-AT_CHECK([ovn-nbctl pg-add pg1], [0], [ignore])
-AT_CHECK([ovn-nbctl --bare --columns=name list port_group pg1], [0],
-[pg1
-])
-
-AT_CHECK([ovn-nbctl pg-del pg1], [0], [ignore])
-AT_CHECK([ovn-nbctl list port_group], [0], [])
-
-AT_CHECK([ovn-nbctl ls-add sw1], [0], [ignore])
-AT_CHECK([ovn-nbctl lsp-add sw1 sw1-p1], [0], [ignore])
-SW1P1=$(ovn-nbctl --bare --columns=_uuid list logical_switch_port sw1-p1)
-AT_CHECK([ovn-nbctl lsp-add sw1 sw1-p2], [0], [ignore])
-SW1P2=$(ovn-nbctl --bare --columns=_uuid list logical_switch_port sw1-p2)
-
-AT_CHECK([ovn-nbctl pg-add pg1 sw1-p1], [0], [ignore])
-AT_CHECK([ovn-nbctl --bare --columns=name list port_group pg1], [0],[dnl
-pg1
-])
-AT_CHECK_UNQUOTED([ovn-nbctl --bare --columns=ports list port_group pg1], [0], [dnl
-$SW1P1
-])
-
-AT_CHECK([ovn-nbctl pg-set-ports pg1 sw1-p2], [0], [ignore])
-AT_CHECK_UNQUOTED([ovn-nbctl --bare --columns=ports list port_group pg1], [0], [dnl
-$SW1P2
-])
-
-AT_CHECK([ovn-nbctl pg-del pg1], [0], [ignore])
-AT_CHECK([ovn-nbctl list port_group], [0], [])
-])
-
-AT_SETUP([ovn-nbctl - daemon retry connection])
-OVN_NBCTL_TEST_START daemon
-AT_CHECK([kill `cat ovsdb-server.pid`])
-AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/ovnnb_db.sock ovn-nb.db], [0], [], [stderr])
-AT_CHECK([ovn-nbctl show], [0], [ignore])
-OVN_NBCTL_TEST_STOP /Terminated/d
-AT_CLEANUP
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
deleted file mode 100644
index 62e58fd0e..000000000
--- a/tests/ovn-northd.at
+++ /dev/null
@@ -1,900 +0,0 @@
-AT_BANNER([OVN northd])
-AT_SETUP([ovn -- check from NBDB to SBDB])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl create Logical_Router name=R1
-ovn-sbctl chassis-add gw1 geneve 127.0.0.1
-ovn-sbctl chassis-add gw2 geneve 1.2.4.8
-
-# Connect alice to R1 as distributed router gateway port on hv2
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24
-
-ovn-nbctl --wait=sb \
- --id=@gc0 create Gateway_Chassis name=alice_gw1 \
- chassis_name=gw1 \
- priority=20 -- \
- --id=@gc1 create Gateway_Chassis name=alice_gw2 \
- chassis_name=gw2 \
- priority=10 -- \
- set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'
-
-nb_gwc1_uuid=`ovn-nbctl --bare --columns _uuid find Gateway_Chassis name="alice_gw1"`
-
-# With the new ha_chassis_group table added, there should be no rows in
-# gateway_chassis table in SB DB.
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
-])
-
-# There should be one ha_chassis_group with the name "alice"
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
-ha_chassis_group name="alice"`
-
-AT_CHECK([test $ha_chassi_grp_name = alice])
-
-ha_chgrp_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group name=alice`
-
-AT_CHECK([ovn-sbctl --bare --columns ha_chassis_group find port_binding \
-logical_port="cr-alice" | grep $ha_chgrp_uuid | wc -l], [0], [1
-])
-
-# There should be one ha_chassis_group with the name "alice"
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
-ha_chassis_group name="alice"`
-
-AT_CHECK([test $ha_chassi_grp_name = alice])
-
-ha_chgrp_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group name=alice`
-
-AT_CHECK([ovn-sbctl --bare --columns ha_chassis_group find port_binding \
-logical_port="cr-alice" | grep $ha_chgrp_uuid | wc -l], [0], [1
-])
-
-ha_ch=`ovn-sbctl --bare --columns ha_chassis find ha_chassis_group`
-# Trim the spaces.
-ha_ch=`echo $ha_ch | sed 's/ //g'`
-
-ha_ch_list=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list="$ha_ch_list $i"
-done
-
-# Trim the spaces.
-ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
-
-AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
-
-# Delete chassis - gw2 in SB DB.
-# ovn-northd should not recreate ha_chassis rows
-# repeatedly when gw2 is deleted.
-ovn-sbctl chassis-del gw2
-
-ha_ch_list_1=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list_1="$ha_ch_list_1 $i"
-done
-
-# Trim the spaces.
-ha_ch_list_1=`echo $ha_ch_list_1 | sed 's/ //g'`
-
-ha_ch_list_2=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list_2="$ha_ch_list_2 $i"
-done
-
-# Trim the spaces.
-ha_ch_list_2=`echo $ha_ch_list_2 | sed 's/ //g'`
-
-AT_CHECK([test "$ha_ch_list_1" = "$ha_ch_list_2"])
-
-# Add back the gw2 chassis
-ovn-sbctl chassis-add gw2 geneve 1.2.4.8
-
-# delete the 2nd Gateway_Chassis on NBDB for alice port
-gw_ch=`ovn-sbctl --bare --columns gateway_chassis find port_binding \
-logical_port="cr-alice"`
-AT_CHECK([test "$gw_ch" = ""])
-
-ha_ch=`ovn-sbctl --bare --columns ha_chassis find ha_chassis_group`
-ha_ch=`echo $ha_ch | sed 's/ //g'`
-# Trim the spaces.
-echo "ha ch in grp = $ha_ch"
-
-ha_ch_list=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list="$ha_ch_list $i"
-done
-
-# Trim the spaces.
-ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
-
-AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
-
-# delete the 2nd Gateway_Chassis on NBDB for alice port
-ovn-nbctl --wait=sb set Logical_Router_Port alice gateway_chassis=${nb_gwc1_uuid}
-
-# There should be only 1 row in ha_chassis SB DB table.
-AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis | wc -l], [0], [1
-])
-
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
-])
-
-# There should be only 1 row in ha_chassis SB DB table.
-AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis | wc -l], [0], [1
-])
-
-# delete all the gateway_chassis on NBDB for alice port
-
-ovn-nbctl --wait=sb clear Logical_Router_Port alice gateway_chassis
-
-# expect that the ha_chassis doesn't exist anymore
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
-])
-
-AT_CHECK([ovn-sbctl list ha_chassis | wc -l], [0], [0
-])
-
-AT_CHECK([ovn-sbctl list ha_chassis_group | wc -l], [0], [0
-])
-
-# expect that the ha_chassis doesn't exist anymore
-AT_CHECK([ovn-sbctl list ha_chassis | wc -l], [0], [0
-])
-AT_CHECK([ovn-sbctl list ha_chassis_group | wc -l], [0], [0
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check Gateway_Chassis propagation from NBDB to SBDB backwards compatibility])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl create Logical_Router name=R1
-ovn-sbctl chassis-add gw1 geneve 127.0.0.1
-ovn-sbctl chassis-add gw2 geneve 1.2.4.8
-
-ovn-nbctl --wait=sb lrp-add R1 bob 00:00:02:01:02:03 172.16.1.1/24 \
- -- set Logical_Router_Port bob options:redirect-chassis="gw1"
-
-
-# It should be converted to ha_chassis_group entries in SBDB, and
-# still redirect-chassis is kept for backwards compatibility
-
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
-])
-
-AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis | wc -l], [0], [1
-])
-
-AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis_group | wc -l], [0], [1
-])
-
-# There should be one ha_chassis_group with the name "bob_gw1"
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
-ha_chassis_group name="bob_gw1"`
-
-AT_CHECK([test $ha_chassi_grp_name = bob_gw1])
-
-ha_chgrp_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group name=bob_gw1`
-
-AT_CHECK([ovn-sbctl --bare --columns ha_chassis_group find port_binding \
-logical_port="cr-bob" | grep $ha_chgrp_uuid | wc -l], [0], [1
-])
-
-ovn-nbctl --wait=sb remove Logical_Router_Port bob options redirect-chassis
-
-# expect that the ha_chassis/ha_chassis_group doesn't exist anymore
-
-AT_CHECK([ovn-sbctl find Gateway_Chassis name=bob_gw1], [0], [])
-AT_CHECK([ovn-sbctl list ha_chassis | wc -l], [0], [0
-])
-
-AT_CHECK([ovn-sbctl list ha_chassis_group | wc -l], [0], [0
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check up state of VIF LSP])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add S1
-ovn-nbctl --wait=sb lsp-add S1 S1-vm1
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-vm1` = xdown])
-
-ovn-sbctl chassis-add hv1 geneve 127.0.0.1
-ovn-sbctl lsp-bind S1-vm1 hv1
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-vm1` = xup])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check up state of router LSP linked to a distributed LR])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl lr-add R1
-ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
-
-ovn-nbctl ls-add S1
-ovn-nbctl lsp-add S1 S1-R1
-ovn-nbctl lsp-set-type S1-R1 router
-ovn-nbctl lsp-set-addresses S1-R1 02:ac:10:01:00:01
-ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-R1` = xup])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check up state of router LSP linked to a gateway LR])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-sbctl chassis-add gw1 geneve 127.0.0.1
-
-ovn-nbctl create Logical_Router name=R1 options:chassis=gw1
-ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
-
-ovn-nbctl ls-add S1
-ovn-nbctl lsp-add S1 S1-R1
-ovn-nbctl lsp-set-type S1-R1 router
-ovn-nbctl lsp-set-addresses S1-R1 02:ac:10:01:00:01
-ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
-
-ovn-sbctl lsp-bind S1-R1 gw1
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-R1` = xup])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check up state of router LSP linked to an LRP with set Gateway Chassis])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-sbctl chassis-add gw1 geneve 127.0.0.1
-
-ovn-nbctl lr-add R1
-ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
-ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
-
-ovn-nbctl ls-add S1
-ovn-nbctl lsp-add S1 S1-R1
-ovn-nbctl lsp-set-type S1-R1 router
-ovn-nbctl lsp-set-addresses S1-R1 router
-ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-R1` = xup])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check IPv6 RA config propagation to SBDB])
-ovn_start
-
-ovn-nbctl lr-add ro
-ovn-nbctl lrp-add ro ro-sw 00:00:00:00:00:01 aef0:0:0:0:0:0:0:1/64
-ovn-nbctl ls-add sw
-ovn-nbctl lsp-add sw sw-ro
-ovn-nbctl lsp-set-type sw-ro router
-ovn-nbctl lsp-set-options sw-ro router-port=ro-sw
-ovn-nbctl lsp-set-addresses sw-ro 00:00:00:00:00:01
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:send_periodic=true
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=slaac
-ovn-nbctl --wait=sb set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1280
-
-uuid=$(ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=ro-sw)
-
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_send_periodic],
-[0], ["true"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_address_mode],
-[0], [slaac
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_max_interval],
-[0], ["600"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_min_interval],
-[0], ["200"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_mtu],
-[0], ["1280"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_src_eth],
-[0], ["00:00:00:00:00:01"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_src_addr],
-[0], ["fe80::200:ff:fe00:1"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_prefixes],
-[0], ["aef0::/64"
-])
-
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=300
-ovn-nbctl --wait=sb set Logical_Router_Port ro-sw ipv6_ra_configs:min_interval=600
-
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_max_interval],
-[0], ["300"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_min_interval],
-[0], ["225"
-])
-
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=300
-ovn-nbctl --wait=sb set Logical_Router_Port ro-sw ipv6_ra_configs:min_interval=250
-
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_max_interval],
-[0], ["300"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_min_interval],
-[0], ["225"
-])
-
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=0
-ovn-nbctl --wait=sb set Logical_Router_Port ro-sw ipv6_ra_configs:min_interval=0
-
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_max_interval],
-[0], ["4"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_min_interval],
-[0], ["3"
-])
-
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=3600
-ovn-nbctl --wait=sb set Logical_Router_Port ro-sw ipv6_ra_configs:min_interval=2400
-
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_max_interval],
-[0], ["1800"
-])
-AT_CHECK([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_min_interval],
-[0], ["1350"
-])
-
-ovn-nbctl --wait=sb set Logical_Router_port ro-sw ipv6_ra_configs:send_periodic=false
-
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_send_periodic],
-[1], [], [ovn-sbctl: no key "ipv6_ra_send_periodic" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_max_interval],
-[1], [], [ovn-sbctl: no key "ipv6_ra_max_interval" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_min_interval],
-[1], [], [ovn-sbctl: no key "ipv6_ra_min_interval" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_mtu],
-[1], [], [ovn-sbctl: no key "ipv6_ra_mtu" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_address_mode],
-[1], [], [ovn-sbctl: no key "ipv6_ra_address_mode" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_src_eth],
-[1], [], [ovn-sbctl: no key "ipv6_ra_src_eth" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_src_addr],
-[1], [], [ovn-sbctl: no key "ipv6_ra_src_addr" in Port_Binding record "${uuid}" column options
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get Port_Binding ${uuid} options:ipv6_ra_prefixes],
-[1], [], [ovn-sbctl: no key "ipv6_ra_prefixes" in Port_Binding record "${uuid}" column options
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- test unixctl])
-ovn_init_db ovn-sb; ovn-sbctl init
-ovn_init_db ovn-nb; ovn-nbctl init
-
-# test unixctl option
-mkdir "$ovs_base"/northd
-as northd start_daemon ovn-northd --unixctl="$ovs_base"/northd/ovn-northd.ctl --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
-ovn-nbctl ls-add sw
-ovn-nbctl --wait=sb lsp-add sw p1
-# northd created with unixctl option successfully created port_binding entry
-AT_CHECK([ovn-sbctl --bare --columns datapath find port_binding logical_port="p1" | wc -l], [0], [1
-])
-AT_CHECK([ovn-nbctl --wait=sb lsp-del p1])
-
-# ovs-appctl exit with unixctl option
-OVS_APP_EXIT_AND_WAIT_BY_TARGET(["$ovs_base"/northd/ovn-northd.ctl], ["$ovs_base"/northd/ovn-northd.pid])
-
-# Check no port_binding entry for new port as ovn-northd is not running
-ovn-nbctl lsp-add sw p2
-ovn-nbctl --timeout=10 --wait=sb sync
-AT_CHECK([ovn-sbctl --bare --columns datapath find port_binding logical_port="p2" | wc -l], [0], [0
-])
-
-# test default unixctl path
-as northd start_daemon ovn-northd --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
-ovn-nbctl --wait=sb lsp-add sw p3
-# northd created with default unixctl path successfully created port_binding entry
-AT_CHECK([ovn-sbctl --bare --columns datapath find port_binding logical_port="p3" | wc -l], [0], [1
-])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- check HA_Chassis_Group propagation from NBDB to SBDB])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl --wait=sb ha-chassis-group-add hagrp1
-
-# ovn-northd should not create HA chassis group and HA chassis rows
-# unless the HA chassis group in OVN NB DB is associated to
-# a logical router port or logical port of type external.
-AT_CHECK([ovn-sbctl --bare --columns name find ha_chassis_group name="hagrp1" \
-| wc -l], [0], [0
-])
-
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 30
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch2 20
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch3 10
-
-# There should be no HA_Chassis rows in SB DB.
-AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
-| grep -v '-' | wc -l ], [0], [0
-])
-
-# Add chassis ch1.
-ovn-sbctl chassis-add ch1 geneve 127.0.0.2
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list chassis | grep ch1 | wc -l`])
-
-# There should be no HA_Chassis rows
-AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
-| grep -v '-' | wc -l ], [0], [0
-])
-
-# Create a logical router port and attach ha chassis group.
-ovn-nbctl lr-add lr0
-ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
-
-hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group name=hagrp1`
-ovn-nbctl set logical_router_port lr0-public ha_chassis_group=$hagrp1_uuid
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Make sure that ovn-northd doesn't recreate the ha_chassis
-# records if the chassis record is missing in SB DB.
-
-ha_ch_list_1=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list_1="$ha_ch_list_1 $i"
-done
-
-# Trim the spaces.
-ha_ch_list_1=`echo $ha_ch_list_1 | sed 's/ //g'`
-
-ha_ch_list_2=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list_2="$ha_ch_list_2 $i"
-done
-
-# Trim the spaces.
-ha_ch_list_2=`echo $ha_ch_list_2 | sed 's/ //g'`
-
-AT_CHECK([test "$ha_ch_list_1" = "$ha_ch_list_2"])
-
-# 2 HA chassis should be created with 'chassis' column empty because
-# we have not added hv1 and hv2 chassis to the SB DB.
-AT_CHECK([test 2 = `ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
-| grep -v '-' | wc -l`])
-
-# We should have 1 ha chassis with 'chassis' column set for hv1
-AT_CHECK([test 1 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | awk '{print $3}' \
-| grep '-' | wc -l`])
-
-# Create another logical router port and associate to the same ha_chasis_group
-ovn-nbctl lr-add lr1
-ovn-nbctl lrp-add lr1 lr1-public 00:00:20:20:12:14 182.168.0.100/24
-
-ovn-nbctl set logical_router_port lr1-public ha_chassis_group=$hagrp1_uuid
-
-# We should still have 1 HA chassis group and 3 HA chassis in SB DB.
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Change the priority of ch1 - ha chassis in NB DB. It should get
-# reflected in SB DB.
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 100
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns priority find \
-ha_chassis | grep 100 | wc -l`])
-
-# Delete ch1 HA chassis in NB DB.
-ovn-nbctl --wait=sb ha-chassis-group-remove-chassis hagrp1 ch1
-
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Add back the ha chassis
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 40
-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Delete lr0-public. We should still have 1 HA chassis group and
-# 3 HA chassis in SB DB.
-ovn-nbctl --wait=sb lrp-del lr0-public
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Delete lr1-public. There should be no HA chassis group in SB DB.
-ovn-nbctl --wait=sb lrp-del lr1-public
-
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | grep chassis | wc -l`])
-
-# Add lr0-public again
-ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
-ovn-nbctl set logical_router_port lr0-public ha_chassis_group=$hagrp1_uuid
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Create a Gateway chassis. ovn-northd should ignore this.
-ovn-nbctl lrp-set-gateway-chassis lr0-public ch-1 20
-
-# There should be only 1 HA chassis group in SB DB with the
-# name hagrp1.
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group | wc -l`])
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Now delete HA chassis group. ovn-northd should create HA chassis group
-# with the Gateway chassis name
-ovn-nbctl clear logical_router_port lr0-public ha_chassis_group
-ovn-nbctl ha-chassis-group-del hagrp1
-
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="lr0-public" | wc -l`])
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid \
-find ha_chassis | wc -l`])
-
-ovn-nbctl lrp-set-gateway-chassis lr0-public ch2 10
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="lr0-public" | wc -l`])
-
-ovn-sbctl --bare --columns _uuid find ha_chassis
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Test if 'ref_chassis' column is properly set or not in
-# SB DB ha_chassis_group.
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 sw0-p1
-
-ovn-sbctl chassis-add ch2 geneve 127.0.0.3
-ovn-sbctl chassis-add ch3 geneve 127.0.0.4
-ovn-sbctl chassis-add comp1 geneve 127.0.0.5
-ovn-sbctl chassis-add comp2 geneve 127.0.0.6
-
-ovn-nbctl lrp-add lr0 lr0-sw0 00:00:20:20:12:14 10.0.0.1/24
-ovn-nbctl lsp-add sw0 sw0-lr0
-ovn-nbctl lsp-set-type sw0-lr0 router
-ovn-nbctl lsp-set-addresses sw0-lr0 router
-ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
-
-ovn-sbctl lsp-bind sw0-p1 comp1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p1` = xup])
-
-comp1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp1"`
-comp2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp2"`
-ch2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp1"`
-
-echo "comp1_ch_uuid = $comp1_ch_uuid"
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$comp1_ch_uuid" = "$ref_ch_list"])
-
-# unbind sw0-p1
-ovn-sbctl lsp-unbind sw0-p1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p1` = xdown])
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "" = "$ref_ch_list"])
-
-# Bind sw0-p1 in comp2
-ovn-sbctl lsp-bind sw0-p1 comp2
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$comp2_ch_uuid" = "$ref_ch_list"])
-
-ovn-nbctl ls-add sw1
-ovn-nbctl lsp-add sw1 sw1-p1
-ovn-nbctl lr-add lr1
-ovn-nbctl lrp-add lr1 lr1-sw1 00:00:20:20:12:15 20.0.0.1/24
-ovn-nbctl lsp-add sw1 sw1-lr1
-ovn-nbctl lsp-set-type sw1-lr1 router
-ovn-nbctl lsp-set-addresses sw1-lr1 router
-ovn-nbctl lsp-set-options sw1-lr1 router-port=lr1-sw1
-
-# Bind sw1-p1 in comp1.
-ovn-sbctl lsp-bind sw1-p1 comp1
-# Wait until sw1-p1 is up
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw1-p1` = xup])
-
-# sw1-p1 is not connected to lr0. So comp1 should not be in 'ref_chassis'
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$comp2_ch_uuid" = "$ref_ch_list"])
-
-# Now attach sw0 to lr1
-ovn-nbctl lrp-add lr1 lr1-sw0 00:00:20:20:12:16 10.0.0.10/24
-ovn-nbctl lsp-add sw0 sw0-lr1
-ovn-nbctl lsp-set-type sw0-lr1 router
-ovn-nbctl lsp-set-addresses sw0-lr1 router
-ovn-nbctl lsp-set-options sw0-lr1 router-port=lr1-sw0
-
-# Both comp1 and comp2 should be in 'ref_chassis' as sw1 is indirectly
-# connected to lr0
-exp_ref_ch_list=''
-for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
-do
- if test $i = $comp1_ch_uuid; then
- exp_ref_ch_list="${exp_ref_ch_list}$i"
- elif test $i = $comp2_ch_uuid; then
- exp_ref_ch_list="${exp_ref_ch_list}$i"
- fi
-done
-
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-# Unind sw1-p1. comp2 should not be in the ref_chassis.
-ovn-sbctl lsp-unbind sw1-p1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw1-p1` = xdown])
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$comp2_ch_uuid" = "$ref_ch_list"])
-
-# Create sw2 and attach it to lr2
-ovn-nbctl ls-add sw2
-ovn-nbctl lsp-add sw2 sw2-p1
-ovn-nbctl lr-add lr2
-ovn-nbctl lrp-add lr2 lr2-sw2 00:00:20:20:12:17 30.0.0.1/24
-ovn-nbctl lsp-add sw2 sw2-lr2
-ovn-nbctl lsp-set-type sw2-lr2 router
-ovn-nbctl lsp-set-addresses sw2-lr2 router
-ovn-nbctl lsp-set-options sw2-lr2 router-port=lr2-sw2
-
-# Bind sw2-p1 to comp1
-ovn-sbctl lsp-bind sw2-p1 comp1
-# Wait until sw2-p1 is up
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw2-p1` = xup])
-
-# sw2-p1 is not connected to lr0. So comp1 should not be in 'ref_chassis'
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$comp2_ch_uuid" = "$ref_ch_list"])
-
-# Now attach sw1 to lr2. With this sw2-p1 is indirectly connected to lr0.
-ovn-nbctl lrp-add lr2 lr2-sw1 00:00:20:20:12:18 20.0.0.10/24
-ovn-nbctl lsp-add sw1 sw1-lr2
-ovn-nbctl lsp-set-type sw1-lr2 router
-ovn-nbctl lsp-set-addresses sw1-lr2 router
-ovn-nbctl lsp-set-options sw1-lr2 router-port=lr2-sw1
-
-# sw2-p1 is indirectly connected to lr0. So comp1 (and comp2) should be in
-# 'ref_chassis'
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-# Create sw0-p2 and bind it to comp1
-ovn-nbctl lsp-add sw0 sw0-p2
-ovn-sbctl lsp-bind sw0-p2 comp1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p2` = xup])
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-# unbind sw0-p2
-ovn-sbctl lsp-unbind sw0-p2
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p2` = xdown])
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-# Delete lr1-sw0. comp1 should be deleted from ref_chassis as there is no link
-# from sw1 and sw2 to lr0.
-ovn-nbctl lrp-del lr1-sw0
-
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$comp2_ch_uuid" = "$ref_ch_list"])
-
-# Set redirect-chassis option to lr0-public. It should be ignored.
-ovn-nbctl set logical_router_port lr0-public options:redirect-chassis=ch1
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group | wc -l`])
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="lr0-public" | wc -l`])
-
-ovn-sbctl --bare --columns _uuid find ha_chassis
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Delete the gateway chassis. HA chassis group should be created in SB DB
-# for the redirect-chassis option.
-ovn-nbctl clear logical_router_port lr0-public gateway_chassis
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group | wc -l`])
-
-ovn-sbctl list ha_chassis_group
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="lr0-public_ch1" | wc -l`])
-
-ovn-sbctl --bare --columns _uuid find ha_chassis
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list ha_chassis | grep chassis |
-grep -v chassis-name | wc -l`])
-
-# Clear the redirect-chassis option.
-ovn-nbctl clear logical_router_port lr0-public options
-
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
-
-# Delete old sw0.
-ovn-nbctl ls-del sw0
-
-# Create external logical ports and associate ha_chassis_group
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 sw0-pext1
-ovn-nbctl lsp-add sw0 sw0-pext2
-ovn-nbctl lsp-add sw0 sw0-p1
-
-ovn-nbctl lsp-set-addresses sw0-pext1 "00:00:00:00:00:03 10.0.0.3"
-ovn-nbctl lsp-set-addresses sw0-pext2 "00:00:00:00:00:03 10.0.0.4"
-ovn-nbctl lsp-set-addresses sw0-p1 "00:00:00:00:00:03 10.0.0.5"
-
-ovn-nbctl --wait=sb ha-chassis-group-add hagrp1
-
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 30
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch2 20
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch3 10
-
-# ovn-northd should not create HA chassis group and HA chassis rows
-# unless the HA chassis group in OVN NB DB is associated to
-# a logical router port or logical port of type external.
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
-
-hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group \
-name=hagrp1`
-
-# The type of the lsp - sw0-pext1 is still not set to external.
-# So ha_chassis_group should be ignored.
-ovn-nbctl set logical_switch_port sw0-pext1 ha_chassis_group=$hagrp1_uuid
-
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | grep chassis | wc -l`])
-
-# Set the type of sw0-pext1 to external
-ovn-nbctl lsp-set-type sw0-pext1 external
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-sb_hagrp1_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group \
-name=hagrp1`
-
-AT_CHECK([test "$sb_hagrp1_uuid" = `ovn-sbctl --bare --columns \
-ha_chassis_group find port_binding logical_port=sw0-pext1`])
-
-# Set the type of sw0-pext2 to external and associate ha_chassis_group
-ovn-nbctl lsp-set-type sw0-pext2 external
-ovn-nbctl set logical_switch_port sw0-pext2 ha_chassis_group=$hagrp1_uuid
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis |
-grep -v chassis-name | wc -l`])
-AT_CHECK([test "$sb_hagrp1_uuid" = `ovn-sbctl --bare --columns \
-ha_chassis_group find port_binding logical_port=sw0-pext1`])
-
-OVS_WAIT_UNTIL([test "$sb_hagrp1_uuid" = `ovn-sbctl --bare --columns \
-ha_chassis_group find port_binding logical_port=sw0-pext2`])
-
-# sw0-p1 is a normal port. So ha_chassis_group should not be set
-# in port_binding.
-ovn-nbctl --wait=sb set logical_switch_port sw0-p1 \
-ha_chassis_group=$hagrp1_uuid
-
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=sw0-p1) = x], [0], [])
-
-# Clear ha_chassis_group for sw0-pext1
-ovn-nbctl --wait=sb clear logical_switch_port sw0-pext1 ha_chassis_group
-
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=sw0-pext1) = x], [0], [])
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
-ha_chassis_group name="hagrp1" | wc -l`])
-
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-# Clear ha_chassis_group for sw0-pext2
-ovn-nbctl --wait=sb clear logical_switch_port sw0-pext2 ha_chassis_group
-
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=sw0-pext2) = x], [0], [])
-
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-AT_CLEANUP
diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
deleted file mode 100644
index a8a15f8fe..000000000
--- a/tests/ovn-performance.at
+++ /dev/null
@@ -1,424 +0,0 @@
-#
-# Tests targeting performance of OVN components.
-#
-
-m4_divert_push([PREPARE_TESTS])
-
-# vec_cmp VALUE_VEC OP-VALUE_VEC
-#
-# Compares each value from VALUE_VEC to the operator-value pair from the
-# OP-VALUE_VEC.
-#
-# VALUE_VEC must be a list of values separated by a character from $IFS.
-# OP-VALUE_VEC must be a list of operator-value expressions separated by a
-# character from $IFS. Operator-value expressions cannot contain any characters
-# from $IFS like spaces. '=' is treated as an equality operator ('==') for
-# conciseness.
-#
-# Returns the result of each comparison as a list of boolean values (0 or 1)
-# separated by a new-line character.
-vec_cmp() {
- local a b i j
-
- i=0
- for a in $1; do
- j=0
- for b in $2; do
- if test $i -eq $j; then
- # Replace assignment '=' with equality comparison '=='
- case "$b" in
- =[[0-9]]*) b="=$b" ;;
- esac
-
- echo $(($a $b))
- break
- fi
- j=$((j + 1))
- done
- i=$((i + 1))
- done
-}
-
-# vec_sub VEC_A VEC_B
-#
-# Subtracts two vectors:
-#
-# VEC_A = [a1, a2, ...]
-# VEC_B = [b1, b2, ...]
-# OUT = [(a1 - b1), (a2 - b2), ...]
-#
-# VEC_A and VEC_B must be lists of values separated by a character from $IFS.
-vec_sub() {
- local a b i j
-
- i=0
- for a in $1; do
- j=0
- for b in $2; do
- if test $i -eq $j; then
- echo $((a - b))
- break
- fi
- j=$((j + 1))
- done
- i=$((i + 1))
- done
-}
-
-# vec_fold VEC OP
-#
-# Reduces a vector to a single value by applying the binary operator OP (i.e.,
-# one that requires two arguments) cumulatively to all vector elements from left
-# to right:
-#
-# VEC = [e1, e2, e3 ...]
-# OUT = (...((e1 OP e2) OP e3) OP ...)
-#
-# VEC must be a list of values separated by a character from $IFS.
-vec_fold() {
- local first op prod
-
- first=1
- op=$2
- for a in $1; do
- if test $first -eq 1; then
- prod=$a
- first=0
- else
- prod=$((prod $op a))
- fi
- done
- echo $prod
-}
-
-# read_counters SANDBOXES TARGET COUNTER
-#
-# Prints out the coverage COUNTER for the TARGET in each of the SANDBOXES.
-#
-# SANDBOXES must be a list of strings separated by a character from $IFS.
-read_counters() {
- local sims="$1" target="$2" counter="$3"
-
- for sim in $sims; do
- as $sim ovs-appctl -t "$target" coverage/read-counter "$counter" || return 1
- done
-}
-
-# counter_delta_ SANDBOXES TARGET COUNTER COMMAND
-#
-# Runs the COMMAND and reports the COUNTER change registered during the command
-# run for the given TARGET in each of the SANDBOXES.
-counter_delta_() {
- local sims="$1" target="$2" counter="$3" cmd="$4"
- local before after
-
- before=$(read_counters "$sims" "$target" "$counter") || return 1
- eval "$cmd" >/dev/null || return 1
- after=$(read_counters "$sims" "$target" "$counter") || return 1
-
- vec_sub "$after" "$before"
-}
-
-# counter_delta SANDBOXES TARGET COUNTER COMMAND
-#
-# Same as counter_delta_ but also prints the COUNTER values together with the
-# COMMAND to standard error.
-counter_delta() {
- local cmd="$4"
- local v
-
- v=$(counter_delta_ "$@") || return 1
-
- # Dump the counters and the command for troubleshooting
- echo "$v" | tr '\n' '\t' >&2
- echo "$cmd" >&2
-
- echo "$v"
-}
-
-# vec_cmp_counter_delta SANDBOXES TARGET COUNTER CONDS COMMAND
-#
-# Check if COUNTER change in the TARGET app in each of the SANDBOXES after
-# running the COMMAND meets the conditions listed as operator-value pairs in
-# CONDS.
-vec_cmp_counter_delta() {
- local v
-
- v=$(counter_delta "$1" "$2" "$3" "$5") || return 1
- v=$(vec_cmp "$v" "$4") || return 1
- v=$(vec_fold "$v" "&&") || return 1
-
- echo "$v"
-}
-
-# cmp_counter_delta SANDBOXES TARGET COUNTER COND COMMAND
-#
-# Check if COUNTER change in the TARGET app in each of the SANDBOXES after
-# running the COMMAND meets the COND condition given as a operator-value pair.
-cmp_counter_delta() {
- local conds=""
-
- # Use the same condition for each sandbox
- for _ in $1; do
- conds="$conds $4"
- done
-
- vec_cmp_counter_delta "$1" "$2" "$3" "$conds" "$5"
-}
-
-m4_divert_pop([PREPARE_TESTS])
-
-# CHECK_COUNTER_DELTA_IS_ZERO SANDBOXES TARGET COUNTER COMMAND
-#
-# Runs the COMMAND and checks if the COUNTER value for the TARGET in all of
-# the SANDBOXES did not change.
-m4_define([CHECK_COUNTER_DELTA_IS_ZERO],[
- rv=$(cmp_counter_delta "$1" "$2" "$3" "=0" "$4")
- rc=$?
- AT_CHECK([test $rc -eq 0 -a $rv -eq 1])
-])
-
-# CHECK_COUNTER_DELTA_IS_NOT_ZERO SANDBOXES TARGET COUNTER COMMAND
-#
-# Runs the COMMAND and checks if the COUNTER value for the TARGET in
-# all of the SANDBOXES has changed.
-m4_define([CHECK_COUNTER_DELTA_IS_NOT_ZERO],[
- rv=$(cmp_counter_delta "$1" "$2" "$3" ">0" "$4")
- rc=$?
- AT_CHECK([test $rc -eq 0 -a $rv -eq 1])
-])
-
-# CHECK_COUNTER_DELTA_COND SANDBOXES TARGET COUNTER CONDS COMMAND
-#
-# Runs the COMMAND and checks if the COUNTER value for the TARGET in all of the
-# SANDBOXES satisfies the conditions listed in CONDS.
-m4_define([CHECK_COUNTER_DELTA_COND],[
- rv=$(vec_cmp_counter_delta "$1" "$2" "$3" "$4" "$5")
- rc=$?
- AT_CHECK([test $rc -eq 0 -a $rv -eq 1])
-])
-
-# OVN_CONTROLLER_EXPECT_HIT SANDBOXES COUNTER COMMAND
-#
-# Checks if the COUNTER value has changed for any of the ovn-controller
-# processes in the SANDBOXES when the COMMAND was run.
-m4_define([OVN_CONTROLLER_EXPECT_HIT],[
- CHECK_COUNTER_DELTA_IS_NOT_ZERO([$1], [ovn-controller], [$2], [$3])
-])
-
-# OVN_CONTROLLER_EXPECT_NO_HIT SANDBOXES COUNTER COMMAND
-#
-# Checks if the COUNTER value has not changed for any of the ovn-controller
-# processes in the SANDBOXES when the COMMAND was run.
-m4_define([OVN_CONTROLLER_EXPECT_NO_HIT],[
- CHECK_COUNTER_DELTA_IS_ZERO([$1], [ovn-controller], [$2], [$3])
-])
-
-# OVN_CONTROLLER_EXPECT_HIT_COND SANDBOXES COUNTER CONDS COMMAND
-#
-# Checks if the change of the COUNTER value, when the COMMAND was run, of the
-# ovn-controller process in each of the SANDBOXES meets the conditions in
-# CONDS. CONDS must be a list of operator-value pairs, for example "[>0 =0]",
-# following the same order as SANDBOXES.
-m4_define([OVN_CONTROLLER_EXPECT_HIT_COND],[
- CHECK_COUNTER_DELTA_COND([$1], [ovn-controller], [$2], [$3], [$4])
-])
-
-AT_SETUP([ovn -- ovn-controller incremental processing])
-# Check which operations the trigger full logical flow processing.
-#
-# Create and destroy logical routers, switches, ports, address sets and ACLs
-# while counting calls to lflow_run() in ovn-controller.
-
-ovn_start
-net_add n1
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-done
-
-# Add router lr1
-OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lr-add lr1]
-)
-
-for i in 1 2; do
- ls=ls$i
- lsp=$ls-lr1
- lrp=lr1-$ls
-
- # Add switch $ls
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv ls-add $ls]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv add Logical_Switch $ls other_config subnet=10.0.$i.0/24]
- )
-
- # Add router port to $ls
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lrp-add lr1 $lrp 02:00:00:00:0$i:01 10.0.$i.1/24]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lsp-add $ls $lsp]
- )
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lsp-set-type $lsp router]
- )
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lsp-set-options $lsp router-port=$lrp]
- )
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lsp-set-addresses $lsp router]
- )
-done
-
-get_lsp_uuid () {
- ovn-nbctl lsp-list ls${1#lp} | grep $1 | awk '{ print $1 }'
-}
-
-pg_ports=
-
-for i in 1 2; do
- j=$((i%2 + 1))
- as=as$i
- ls=ls$i
- lp=lp$i
- vif=vif$i
-
- # Add port $lp
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lsp-add $ls $lp]
- )
-
- pg_ports="$pg_port `get_lsp_uuid $lp`"
-
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lsp-set-addresses $lp "dynamic"]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl wait-until Logical_Switch_Port $lp dynamic_addresses!=[[]] &&
- ovn-nbctl --wait=hv sync]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl get Logical_Switch_Port $lp dynamic_addresses &&
- ovn-nbctl --wait=hv sync]
- )
-
- # Add address set $as
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv create Address_Set name="$as"]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv add Address_Set "$as" addresses "10.0.$i.10"]
- )
-
- # Add ACLs for port $lp
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv acl-add $ls to-lport 1001 'outport == \"$lp\" && ip4.src == \$$as' allow]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv acl-add $ls to-lport 1000 'outport == \"$lp\"' drop]
- )
-
- # Bind port $lp and wait for it to come up
- OVN_CONTROLLER_EXPECT_HIT_COND(
- [hv$i hv$j], [lflow_run], [>0 =0],
- [as hv$i ovs-vsctl add-port br-int $vif -- set Interface $vif external-ids:iface-id=$lp &&
- ovn-nbctl wait-until Logical_Switch_Port $lp 'up=true' &&
- ovn-nbctl --wait=hv sync]
- )
-done
-
-for i in 1 2; do
- j=$((i%2 + 1))
- as=as$i
- ls=ls$i
- lp=lp$i
-
- # Delete ACLs for port $lp
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv acl-del $ls to-lport 1001 'outport == \"$lp\" && ip4.src == \$$as']
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv acl-del $ls to-lport 1000 'outport == \"$lp\"']
- )
-
- # Delete address set $as
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv remove Address_Set "$as" addresses "10.0.$i.10"]
- )
- OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv destroy Address_Set "$as"]
- )
-done
-
-OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv create Port_Group name=pg1 ports=\"$pg_ports\"]
-)
-
-# Add ACLs for port group pg1
-OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv acl-add pg1 to-lport 1001 'outport == @pg1 && ip4.src == $pg1_ip4' allow]
-)
-
-for i in 1 2; do
- j=$((i%2 + 1))
- lp=lp$i
-
- # Delete port $lp
- OVN_CONTROLLER_EXPECT_HIT_COND(
- [hv$i hv$j], [lflow_run], [>0 =0],
- [ovn-nbctl --wait=hv lsp-del $lp]
- )
-done
-
-# Delete port group pg1
-OVN_CONTROLLER_EXPECT_NO_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv destroy Port_Group pg1]
-)
-
-for i in 1 2; do
- ls=ls$i
-
- # Delete switch $ls
- OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv ls-del $ls]
- )
-done
-
-# Delete router lr1
-OVN_CONTROLLER_EXPECT_HIT(
- [hv1 hv2], [lflow_run],
- [ovn-nbctl --wait=hv lr-del lr1]
-)
-
-OVN_CLEANUP([hv1], [hv2])
-
-AT_CLEANUP
diff --git a/tests/ovn-sbctl.at b/tests/ovn-sbctl.at
deleted file mode 100644
index 650e357da..000000000
--- a/tests/ovn-sbctl.at
+++ /dev/null
@@ -1,150 +0,0 @@
-AT_BANNER([ovn-sbctl])
-
-# OVN_SBCTL_TEST_START
-m4_define([OVN_SBCTL_TEST_START],
- [dnl Create databases (ovn-nb, ovn-sb).
- AT_KEYWORDS([ovn])
- for daemon in ovn-nb ovn-sb; do
- AT_CHECK([ovsdb-tool create $daemon.db $abs_top_srcdir/${daemon%%-*}/${daemon}.ovsschema])
- done
-
- dnl Start ovsdb-servers.
- AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=ovnnb_db.pid --unixctl=$OVS_RUNDIR/ovnnb_db.ctl --log-file=ovsdb_nb.log --remote=punix:$OVS_RUNDIR/ovnnb_db.sock ovn-nb.db ], [0], [], [stderr])
- AT_CHECK([ovsdb-server --detach --no-chdir --pidfile=ovnsb_db.pid --unixctl=$OVS_RUNDIR/ovnsb_db.ctl --log-file=ovsdb_sb.log --remote=punix:$OVS_RUNDIR/ovnsb_db.sock ovn-sb.db], [0], [], [stderr])
- on_exit "kill `cat ovnnb_db.pid` `cat ovnsb_db.pid`"
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d
-/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']])
- AT_CAPTURE_FILE([ovsdb-server.log])
-
- dnl Start ovn-northd.
- AT_CHECK([ovn-northd --detach --no-chdir --pidfile --log-file --ovnnb-db=unix:$OVS_RUNDIR/ovnnb_db.sock --ovnsb-db=unix:$OVS_RUNDIR/ovnsb_db.sock], [0], [], [stderr])
- on_exit "kill `cat ovn-northd.pid`"
- AT_CHECK([[sed < stderr '
-/vlog|INFO|opened log file/d']])
- AT_CAPTURE_FILE([ovn-northd.log])
-])
-
-# OVN_SBCTL_TEST_STOP
-m4_define([OVN_SBCTL_TEST_STOP],
- [AT_CHECK([check_logs "$1"])
- OVS_APP_EXIT_AND_WAIT([ovn-northd])
- OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnnb_db.ctl], [$OVS_RUNDIR/ovnnb_db.pid])
- OVS_APP_EXIT_AND_WAIT_BY_TARGET([$OVS_RUNDIR/ovnsb_db.ctl], [$OVS_RUNDIR/ovnsb_db.pid])])
-
-dnl ---------------------------------------------------------------------
-
-AT_SETUP([ovn-sbctl - chassis commands])
-OVN_SBCTL_TEST_START
-ovn_init_db ovn-sb
-
-AT_CHECK([ovn-sbctl chassis-add ch0 geneve 1.2.3.4])
-AT_CHECK([ovn-sbctl -f csv -d bare --no-headings --columns ip,type list encap | sort],
- [0], [dnl
-1.2.3.4,geneve
-])
-
-AT_CHECK([ovn-sbctl chassis-add ch1 stt,geneve,vxlan 1.2.3.5])
-AT_CHECK([ovn-sbctl -f csv -d bare --no-headings --columns ip,type list encap | sort],
- [0], [dnl
-1.2.3.4,geneve
-1.2.3.5,geneve
-1.2.3.5,stt
-1.2.3.5,vxlan
-])
-
-AT_CHECK([ovn-sbctl chassis-del ch0])
-AT_CHECK([ovn-sbctl -f csv -d bare --no-headings --columns ip,type list encap | sort],
- [0], [dnl
-1.2.3.5,geneve
-1.2.3.5,stt
-1.2.3.5,vxlan
-])
-
-OVN_SBCTL_TEST_STOP
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-AT_CLEANUP
-
-dnl ---------------------------------------------------------------------
-
-AT_SETUP([ovn-sbctl])
-OVN_SBCTL_TEST_START
-
-AT_CHECK([ovn-nbctl ls-add br-test])
-AT_CHECK([ovn-nbctl lsp-add br-test vif0])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif0 f0:ab:cd:ef:01:02])
-AT_CHECK([ovn-sbctl chassis-add ch0 stt 1.2.3.5])
-AT_CHECK([ovn-nbctl --wait=sb sync])
-AT_CHECK([ovn-sbctl lsp-bind vif0 ch0])
-
-AT_CHECK([ovn-sbctl show], [0], [dnl
-Chassis ch0
- Encap stt
- ip: "1.2.3.5"
- options: {csum="true"}
- Port_Binding vif0
-])
-
-# adds another 'vif1'
-AT_CHECK([ovn-nbctl --wait=sb lsp-add br-test vif1])
-AT_CHECK([ovn-nbctl lsp-set-addresses vif1 f0:ab:cd:ef:01:03])
-AT_CHECK([ovn-sbctl lsp-bind vif1 ch0])
-
-AT_CHECK([ovn-sbctl show | sed 's/vif[[0-9]]/vif/'], [0], [dnl
-Chassis ch0
- Encap stt
- ip: "1.2.3.5"
- options: {csum="true"}
- Port_Binding vif
- Port_Binding vif
-])
-
-# deletes 'vif1'
-AT_CHECK([ovn-nbctl lsp-del vif1])
-AT_CHECK([ovn-nbctl --wait=sb sync])
-
-AT_CHECK([ovn-sbctl show], [0], [dnl
-Chassis ch0
- Encap stt
- ip: "1.2.3.5"
- options: {csum="true"}
- Port_Binding vif0
-])
-
-uuid=$(ovn-sbctl --columns=_uuid list Chassis ch0 | cut -d ':' -f2 | tr -d ' ')
-AT_CHECK_UNQUOTED([ovn-sbctl --columns=logical_port,mac,chassis list Port_Binding], [0], [dnl
-logical_port : vif0
-mac : [["f0:ab:cd:ef:01:02"]]
-chassis : ${uuid}
-])
-
-# test the passing down of logical port type and options.
-AT_CHECK([ovn-nbctl --wait=sb lsp-add br-test vtep0])
-AT_CHECK([ovn-nbctl lsp-set-type vtep0 vtep])
-AT_CHECK([ovn-nbctl lsp-set-options vtep0 vtep_physical_switch=p0 vtep_logical_switch=l0])
-
-AT_CHECK([ovn-sbctl --timeout=10 wait-until Port_Binding vtep0 options!={}])
-AT_CHECK([ovn-sbctl --columns=logical_port,mac,type,options list Port_Binding vtep0], [0], [dnl
-logical_port : vtep0
-mac : [[]]
-type : vtep
-options : {vtep_logical_switch=l0, vtep_physical_switch=p0}
-])
-
-OVN_SBCTL_TEST_STOP
-AT_CLEANUP
-
-dnl ---------------------------------------------------------------------
-
-AT_SETUP([ovn-sbctl - connection])
-OVN_SBCTL_TEST_START
-
-AT_CHECK([ovn-sbctl --inactivity-probe=30000 set-connection ptcp:6641:127.0.0.1 punix:$OVS_RUNDIR/ovnsb_db.sock])
-AT_CHECK([ovn-sbctl list connection | grep inactivity_probe], [0], [dnl
-inactivity_probe : 30000
-inactivity_probe : 30000
-])
-
-OVN_SBCTL_TEST_STOP
-AT_CLEANUP
diff --git a/tests/ovn.at b/tests/ovn.at
deleted file mode 100644
index cb380d275..000000000
--- a/tests/ovn.at
+++ /dev/null
@@ -1,14702 +0,0 @@
-# OVN_CHECK_PACKETS([PCAP], [EXPECTED])
-#
-# This compares packets read from PCAP, in pcap format, to those read
-# from EXPECTED, which is a text file containing packets as hex
-# strings, one per line. If PCAP contains fewer packets than
-# EXPECTED, it waits up to 10 seconds for more packets to appear.
-#
-# The implementation is an m4 macro that is mostly implemented in
-# terms of a shell function. This reduces the size of the generated
-# testsuite file since the shell function is only emitted once even
-# when this macro is invoked many times.
-m4_divert_text([PREPARE_TESTS],
- [ovn_check_packets__ () {
- echo
- echo "checking packets in $1 against $2:"
- rcv_pcap=$1
- rcv_text=`echo "$rcv_pcap.packets" | sed 's/\.pcap//'`
- exp_text=$2
- exp_n=`wc -l < "$exp_text"`
- OVS_WAIT_UNTIL(
- [$PYTHON "$top_srcdir/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text
- rcv_n=`wc -l < "$rcv_text"`
- echo "rcv_n=$rcv_n exp_n=$exp_n"
- test $rcv_n -ge $exp_n])
- sort $exp_text > expout
- }
-])
-m4_define([OVN_CHECK_PACKETS],
- [ovn_check_packets__ "$1" "$2"
- AT_CHECK([sort $rcv_text], [0], [expout])])
-
-AT_BANNER([OVN components])
-
-AT_SETUP([ovn -- lexer])
-dnl For lines without =>, input and expected output are identical.
-dnl For lines with =>, input precedes => and expected output follows =>.
-AT_DATA([test-cases.txt], [dnl
-foo bar baz quuxquuxquux _abcd_ a.b.c.d a123_.456
-"abc\u0020def" => "abc def"
-" => error("Input ends inside quoted string.")dnl "
-
-$foo $bar $baz $quuxquuxquux $_abcd_ $a.b.c.d $a123_.456
-$1 => error("`$' must be followed by a valid identifier.") 1
-
-a/*b*/c => a c
-a//b c => a
-a/**/b => a b
-a/*/b => a error("`/*' without matching `*/'.")
-a/*/**/b => a b
-a/b => a error("`/' is only valid as part of `//' or `/*'.") b
-
-0 1 12345 18446744073709551615
-18446744073709551616 => error("Decimal constants must be less than 2**64.")
-9999999999999999999999 => error("Decimal constants must be less than 2**64.")
-01 => error("Decimal constants must not have leading zeros.")
-
-0/0
-0/1
-1/0 => error("Value contains unmasked 1-bits.")
-1/1
-128/384
-1/3
-1/ => error("Integer constant expected.")
-
-1/0x123 => error("Value and mask have incompatible formats.")
-
-0x1234
-0x01234 => 0x1234
-0x0 => 0
-0x000 => 0
-0xfedcba9876543210
-0XFEDCBA9876543210 => 0xfedcba9876543210
-0xfedcba9876543210fedcba9876543210
-0x0000fedcba9876543210fedcba9876543210 => 0xfedcba9876543210fedcba9876543210
-0x => error("Hex digits expected following 0x.")
-0X => error("Hex digits expected following 0X.")
-0x0/0x0 => 0/0
-0x0/0x1 => 0/0x1
-0x1/0x0 => error("Value contains unmasked 1-bits.")
-0xffff/0x1ffff
-0x. => error("Invalid syntax in hexadecimal constant.")
-
-192.168.128.1 1.2.3.4 255.255.255.255 0.0.0.0
-256.1.2.3 => error("Invalid numeric constant.")
-192.168.0.0/16
-192.168.0.0/255.255.0.0 => 192.168.0.0/16
-192.168.0.0/255.255.255.0 => 192.168.0.0/24
-192.168.0.0/255.255.0.255
-192.168.0.0/255.0.0.0 => error("Value contains unmasked 1-bits.")
-192.168.0.0/32
-192.168.0.0/255.255.255.255 => 192.168.0.0/32
-1.2.3.4:5 => 1.2.3.4 : 5
-
-::
-::1
-ff00::1234 => ff00::1234
-2001:db8:85a3::8a2e:370:7334
-2001:db8:85a3:0:0:8a2e:370:7334 => 2001:db8:85a3::8a2e:370:7334
-2001:0db8:85a3:0000:0000:8a2e:0370:7334 => 2001:db8:85a3::8a2e:370:7334
-::ffff:192.0.2.128
-::ffff:c000:0280 => ::ffff:192.0.2.128
-::1/::1
-::1/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff => ::1/128
-::1/128
-ff00::/8
-ff00::/ff00:: => ff00::/8
-
-01:23:45:67:ab:cd
-01:23:45:67:AB:CD => 01:23:45:67:ab:cd
-fe:dc:ba:98:76:54
-FE:DC:ba:98:76:54 => fe:dc:ba:98:76:54
-01:00:00:00:00:00/01:00:00:00:00:00
-ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff
-fe:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff
-ff:ff:ff:ff:ff:ff/fe:ff:ff:ff:ff:ff => error("Value contains unmasked 1-bits.")
-fe:x => error("Invalid numeric constant.")
-00:01:02:03:04:x => error("Invalid numeric constant.")
-
-# Test that operators are tokenized as expected, even without white space.
-(){}[[]]==!=<<=>>=!&&||..,;=<->--: => ( ) { } [[ ]] == != < <= > >= ! && || .. , ; = <-> -- :
-& => error("`&' is only valid as part of `&&'.")
-| => error("`|' is only valid as part of `||'.")
-- => error("`-' is only valid as part of `--'.")
-
-^ => error("Invalid character `^' in input.")
-])
-AT_CAPTURE_FILE([input.txt])
-sed 's/ =>.*//' test-cases.txt > input.txt
-sed 's/.* => //' test-cases.txt > expout
-AT_CHECK([ovstest test-ovn lex < input.txt], [0], [expout])
-AT_CLEANUP
-
-dnl The OVN expression parser needs to know what fields overlap with one
-dnl another. This test therefore verifies that all the smaller registers
-dnl are defined as terms of subfields of the larger ones.
-dnl
-dnl When we add or remove registers this test needs to be updated, of course.
-AT_SETUP([ovn -- registers])
-AT_CHECK([ovstest test-ovn dump-symtab | grep reg | sort], [0],
-[[reg0 = xxreg0[96..127]
-reg1 = xxreg0[64..95]
-reg2 = xxreg0[32..63]
-reg3 = xxreg0[0..31]
-reg4 = xxreg1[96..127]
-reg5 = xxreg1[64..95]
-reg6 = xxreg1[32..63]
-reg7 = xxreg1[0..31]
-reg8 = xreg4[32..63]
-reg9 = xreg4[0..31]
-xreg0 = xxreg0[64..127]
-xreg1 = xxreg0[0..63]
-xreg2 = xxreg1[64..127]
-xreg3 = xxreg1[0..63]
-xreg4 = OXM_OF_PKT_REG4
-xxreg0 = NXM_NX_XXREG0
-xxreg1 = NXM_NX_XXREG1
-]])
-AT_CLEANUP
-
-dnl Check that the OVN conntrack field definitions are correct.
-AT_SETUP([ovn -- conntrack fields])
-AT_CHECK([ovstest test-ovn dump-symtab | grep ^ct | sort], [0],
-[[ct.dnat = ct_state[7]
-ct.est = ct_state[1]
-ct.inv = ct_state[4]
-ct.new = ct_state[0]
-ct.rel = ct_state[2]
-ct.rpl = ct_state[3]
-ct.snat = ct_state[6]
-ct.trk = ct_state[5]
-ct_label = NXM_NX_CT_LABEL
-ct_label.blocked = ct_label[0]
-ct_mark = NXM_NX_CT_MARK
-ct_state = NXM_NX_CT_STATE
-]])
-AT_CLEANUP
-
-AT_SETUP([ovn -- composition])
-AT_CHECK([ovstest test-ovn composition 2], [0], [ignore])
-AT_CLEANUP
-
-AT_SETUP([ovn -- expression parser])
-dnl For lines without =>, input and expected output are identical.
-dnl For lines with =>, input precedes => and expected output follows =>.
-AT_DATA([test-cases.txt], [[
-eth.type == 0x800
-eth.type==0x800 => eth.type == 0x800
-eth.type[0..15] == 0x800 => eth.type == 0x800
-
-vlan.present
-vlan.present == 1 => vlan.present
-!(vlan.present == 0) => vlan.present
-!(vlan.present != 1) => vlan.present
-!vlan.present
-vlan.present == 0 => !vlan.present
-vlan.present != 1 => !vlan.present
-!(vlan.present == 1) => !vlan.present
-!(vlan.present != 0) => !vlan.present
-
-eth.dst[0]
-eth.dst[0] == 1 => eth.dst[0]
-eth.dst[0] != 0 => eth.dst[0]
-!(eth.dst[0] == 0) => eth.dst[0]
-!(eth.dst[0] != 1) => eth.dst[0]
-
-!eth.dst[0]
-eth.dst[0] == 0 => !eth.dst[0]
-eth.dst[0] != 1 => !eth.dst[0]
-!(eth.dst[0] == 1) => !eth.dst[0]
-!(eth.dst[0] != 0) => !eth.dst[0]
-
-vlan.tci[12..15] == 0x3
-vlan.tci == 0x3000/0xf000 => vlan.tci[12..15] == 0x3
-vlan.tci[12..15] != 0x3
-vlan.tci != 0x3000/0xf000 => vlan.tci[12..15] != 0x3
-
-!vlan.pcp => vlan.pcp == 0
-!(vlan.pcp) => vlan.pcp == 0
-vlan.pcp == 0x4
-vlan.pcp != 0x4
-vlan.pcp > 0x4
-vlan.pcp >= 0x4
-vlan.pcp < 0x4
-vlan.pcp <= 0x4
-!(vlan.pcp != 0x4) => vlan.pcp == 0x4
-!(vlan.pcp == 0x4) => vlan.pcp != 0x4
-!(vlan.pcp <= 0x4) => vlan.pcp > 0x4
-!(vlan.pcp < 0x4) => vlan.pcp >= 0x4
-!(vlan.pcp >= 0x4) => vlan.pcp < 0x4
-!(vlan.pcp > 0x4) => vlan.pcp <= 0x4
-0x4 == vlan.pcp => vlan.pcp == 0x4
-0x4 != vlan.pcp => vlan.pcp != 0x4
-0x4 < vlan.pcp => vlan.pcp > 0x4
-0x4 <= vlan.pcp => vlan.pcp >= 0x4
-0x4 > vlan.pcp => vlan.pcp < 0x4
-0x4 >= vlan.pcp => vlan.pcp <= 0x4
-!(0x4 != vlan.pcp) => vlan.pcp == 0x4
-!(0x4 == vlan.pcp) => vlan.pcp != 0x4
-!(0x4 >= vlan.pcp) => vlan.pcp > 0x4
-!(0x4 > vlan.pcp) => vlan.pcp >= 0x4
-!(0x4 <= vlan.pcp) => vlan.pcp < 0x4
-!(0x4 < vlan.pcp) => vlan.pcp <= 0x4
-
-1 < vlan.pcp < 4 => vlan.pcp > 0x1 && vlan.pcp < 0x4
-1 <= vlan.pcp <= 4 => vlan.pcp >= 0x1 && vlan.pcp <= 0x4
-1 < vlan.pcp <= 4 => vlan.pcp > 0x1 && vlan.pcp <= 0x4
-1 <= vlan.pcp < 4 => vlan.pcp >= 0x1 && vlan.pcp < 0x4
-1 <= vlan.pcp <= 4 => vlan.pcp >= 0x1 && vlan.pcp <= 0x4
-4 > vlan.pcp > 1 => vlan.pcp < 0x4 && vlan.pcp > 0x1
-4 >= vlan.pcp > 1 => vlan.pcp <= 0x4 && vlan.pcp > 0x1
-4 > vlan.pcp >= 1 => vlan.pcp < 0x4 && vlan.pcp >= 0x1
-4 >= vlan.pcp >= 1 => vlan.pcp <= 0x4 && vlan.pcp >= 0x1
-!(1 < vlan.pcp < 4) => vlan.pcp <= 0x1 || vlan.pcp >= 0x4
-!(1 <= vlan.pcp <= 4) => vlan.pcp < 0x1 || vlan.pcp > 0x4
-!(1 < vlan.pcp <= 4) => vlan.pcp <= 0x1 || vlan.pcp > 0x4
-!(1 <= vlan.pcp < 4) => vlan.pcp < 0x1 || vlan.pcp >= 0x4
-!(1 <= vlan.pcp <= 4) => vlan.pcp < 0x1 || vlan.pcp > 0x4
-!(4 > vlan.pcp > 1) => vlan.pcp >= 0x4 || vlan.pcp <= 0x1
-!(4 >= vlan.pcp > 1) => vlan.pcp > 0x4 || vlan.pcp <= 0x1
-!(4 > vlan.pcp >= 1) => vlan.pcp >= 0x4 || vlan.pcp < 0x1
-!(4 >= vlan.pcp >= 1) => vlan.pcp > 0x4 || vlan.pcp < 0x1
-
-vlan.pcp == {1, 2, 3, 4} => vlan.pcp == 0x1 || vlan.pcp == 0x2 || vlan.pcp == 0x3 || vlan.pcp == 0x4
-vlan.pcp == 1 || ((vlan.pcp == 2 || vlan.pcp == 3) || vlan.pcp == 4) => vlan.pcp == 0x1 || vlan.pcp == 0x2 || vlan.pcp == 0x3 || vlan.pcp == 0x4
-
-vlan.pcp != {1, 2, 3, 4} => vlan.pcp != 0x1 && vlan.pcp != 0x2 && vlan.pcp != 0x3 && vlan.pcp != 0x4
-vlan.pcp == 1 && ((vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && vlan.pcp == 0x2 && vlan.pcp == 0x3 && vlan.pcp == 0x4
-
-vlan.pcp == 1 && !((vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && (vlan.pcp != 0x2 || vlan.pcp != 0x3 || vlan.pcp != 0x4)
-vlan.pcp == 1 && (!(vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && (vlan.pcp != 0x2 || vlan.pcp != 0x3) && vlan.pcp == 0x4
-vlan.pcp == 1 && !(!(vlan.pcp == 2 && vlan.pcp == 3) && vlan.pcp == 4) => vlan.pcp == 0x1 && ((vlan.pcp == 0x2 && vlan.pcp == 0x3) || vlan.pcp != 0x4)
-
-ip4.src == {10.0.0.0/8, 192.168.0.0/16, 172.16.20.0/24, 8.8.8.8} => ip4.src[24..31] == 0xa || ip4.src[16..31] == 0xc0a8 || ip4.src[8..31] == 0xac1014 || ip4.src == 0x8080808
-ip6.src == ::1 => ip6.src == 0x1
-
-ip4.src == 1.2.3.4 => ip4.src == 0x1020304
-ip4.src == ::1.2.3.4/::ffff:ffff => ip4.src == 0x1020304
-ip6.src == ::1 => ip6.src == 0x1
-
-1
-0
-!1 => 0
-!0 => 1
-
-inport == "eth0"
-!(inport != "eth0") => inport == "eth0"
-
-(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) => 0
-
-ip4.src == "eth0" => Integer field ip4.src is not compatible with string constant.
-inport == 1 => String field inport is not compatible with integer constant.
-ip4.src = 1.2.3.4 => Syntax error at `=' expecting relational operator.
-
-ip4.src > {1, 2, 3} => Only == and != operators may be used with value sets.
-eth.type > 0x800 => Only == and != operators may be used with nominal field eth.type.
-vlan.present > 0 => Only == and != operators may be used with Boolean field vlan.present.
-
-inport != "eth0" => Nominal field inport may only be tested for equality (taking enclosing `!' operators into account).
-!(inport == "eth0") => Nominal field inport may only be tested for equality (taking enclosing `!' operators into account).
-eth.type != 0x800 => Nominal field eth.type may only be tested for equality (taking enclosing `!' operators into account).
-!(eth.type == 0x800) => Nominal field eth.type may only be tested for equality (taking enclosing `!' operators into account).
-inport = "eth0" => Syntax error at `=' expecting relational operator.
-
-123 == 123 => Syntax error at `123' expecting field name.
-
-$name => Syntax error at `$name' expecting address set name.
-@name => Syntax error at `@name' expecting port group name.
-
-123 == xyzzy => Syntax error at `xyzzy' expecting field name.
-xyzzy == 1 => Syntax error at `xyzzy' expecting field name.
-
-inport[1] == 1 => Cannot select subfield of string field inport.
-
-eth.type[] == 1 => Syntax error at `@:>@' expecting small integer.
-eth.type[::1] == 1 => Syntax error at `::1' expecting small integer.
-eth.type[18446744073709551615] == 1 => Syntax error at `18446744073709551615' expecting small integer.
-
-eth.type[5!] => Syntax error at `!' expecting `@:>@'.
-
-eth.type[5..1] => Invalid bit range 5 to 1.
-
-eth.type[12..16] => Cannot select bits 12 to 16 of 16-bit field eth.type.
-
-eth.type[10] == 1 => Cannot select subfield of nominal field eth.type.
-
-eth.type => Explicit `!= 0' is required for inequality test of multibit field against 0.
-
-!(!(vlan.pcp)) => Explicit `!= 0' is required for inequality test of multibit field against 0.
-
-123 => Syntax error at end of input expecting relational operator.
-
-123 x => Syntax error at `x' expecting relational operator.
-
-{1, "eth0"} => Syntax error at `"eth0"' expecting integer.
-
-eth.type == xyzzy => Syntax error at `xyzzy' expecting constant.
-
-(1 x) => Syntax error at `x' expecting `)'.
-
-!0x800 != eth.type => Missing parentheses around operand of !.
-
-eth.type == 0x800 || eth.type == 0x86dd && ip.proto == 17 => && and || must be parenthesized when used together.
-
-eth.dst == {} => Syntax error at `}' expecting constant.
-
-eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff => Only == and != operators may be used with masked constants. Consider using subfields instead (e.g. eth.src[0..15] > 0x1111 in place of eth.src > 00:00:00:00:11:11/00:00:00:00:ff:ff).
-
-ip4.src == ::1 => 128-bit constant is not compatible with 32-bit field ip4.src.
-
-1 == eth.type == 2 => Range expressions must have the form `x < field < y' or `x > field > y', with each `<' optionally replaced by `<=' or `>' by `>=').
-
-eth.dst[40] x => Syntax error at `x' expecting end of input.
-
-ip4.src == {1.2.3.4, $set1, $unknownset} => Syntax error at `$unknownset' expecting address set name.
-eth.src == {$set3, badmac, 00:00:00:00:00:01} => Syntax error at `badmac' expecting constant.
-
-((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) => Parentheses nested too deeply.
-
-ct_label > $set4 => Only == and != operators may be used to compare a field against an empty value set.
-]])
-sed 's/ =>.*//' test-cases.txt > input.txt
-sed 's/.* => //' test-cases.txt > expout
-AT_CHECK([ovstest test-ovn parse-expr < input.txt], [0], [expout])
-AT_CLEANUP
-
-AT_SETUP([ovn -- expression annotation])
-dnl Input precedes =>, expected output follows =>.
-dnl Empty lines and lines starting with # are ignored.
-AT_DATA([test-cases.txt], [[
-ip4.src == 1.2.3.4 => ip4.src == 0x1020304 && eth.type == 0x800
-ip4.src != 1.2.3.4 => ip4.src != 0x1020304 && eth.type == 0x800
-ip.proto == 123 => ip.proto == 0x7b && (eth.type == 0x800 || eth.type == 0x86dd)
-ip.proto == {123, 234} => (ip.proto == 0x7b || ip.proto == 0xea) && (eth.type == 0x800 || eth.type == 0x86dd)
-ip4.src == 1.2.3.4 && ip4.dst == 5.6.7.8 => ip4.src == 0x1020304 && eth.type == 0x800 && ip4.dst == 0x5060708 && eth.type == 0x800
-
-# Nested expressions over a single symbol should be annotated with symbol's
-# prerequisites only once, at the top level.
-tcp.dst == 1 || (tcp.dst >= 2 && tcp.dst <= 3) => (tcp.dst == 0x1 || (tcp.dst >= 0x2 && tcp.dst <= 0x3)) && ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
-
-ip => eth.type == 0x800 || eth.type == 0x86dd
-ip == 1 => eth.type == 0x800 || eth.type == 0x86dd
-ip[0] == 1 => eth.type == 0x800 || eth.type == 0x86dd
-ip > 0 => Only == and != operators may be used with nominal field ip.
-!ip => Nominal predicate ip may only be tested positively, e.g. `ip' or `ip == 1' but not `!ip' or `ip == 0'.
-ip == 0 => Nominal predicate ip may only be tested positively, e.g. `ip' or `ip == 1' but not `!ip' or `ip == 0'.
-
-vlan.present => vlan.tci[12]
-!vlan.present => !vlan.tci[12]
-
-!vlan.pcp => vlan.tci[13..15] == 0 && vlan.tci[12]
-vlan.pcp == 1 && vlan.vid == 2 => vlan.tci[13..15] == 0x1 && vlan.tci[12] && vlan.tci[0..11] == 0x2 && vlan.tci[12]
-!reg0 && !reg1 && !reg2 && !reg3 => xxreg0[96..127] == 0 && xxreg0[64..95] == 0 && xxreg0[32..63] == 0 && xxreg0[0..31] == 0
-
-ip.first_frag => ip.frag[0] && (eth.type == 0x800 || eth.type == 0x86dd) && (!ip.frag[1] || (eth.type != 0x800 && eth.type != 0x86dd))
-!ip.first_frag => !ip.frag[0] || (eth.type != 0x800 && eth.type != 0x86dd) || (ip.frag[1] && (eth.type == 0x800 || eth.type == 0x86dd))
-ip.later_frag => ip.frag[1] && (eth.type == 0x800 || eth.type == 0x86dd)
-
-bad_prereq != 0 => Error parsing expression `xyzzy' encountered as prerequisite or predicate of initial expression: Syntax error at `xyzzy' expecting field name.
-self_recurse != 0 => Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `self_recurse'.
-mutual_recurse_1 != 0 => Error parsing expression `mutual_recurse_2 != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `mutual_recurse_1 != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `mutual_recurse_1'.
-mutual_recurse_2 != 0 => Error parsing expression `mutual_recurse_1 != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `mutual_recurse_2 != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `mutual_recurse_2'.
-]])
-sed 's/ =>.*//' test-cases.txt > input.txt
-sed 's/.* => //' test-cases.txt > expout
-AT_CHECK([ovstest test-ovn annotate-expr < input.txt], [0], [expout])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 1-term expression conversion])
-AT_CHECK([ovstest test-ovn exhaustive --operation=convert 1], [0],
- [Tested converting all 1-terminal expressions with 2 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 2 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2-term expression conversion])
-AT_CHECK([ovstest test-ovn exhaustive --operation=convert 2], [0],
- [Tested converting 578 expressions of 2 terminals with 2 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 2 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 3-term expression conversion])
-AT_CHECK([ovstest test-ovn exhaustive --operation=convert --bits=2 3], [0],
- [Tested converting 67410 expressions of 3 terminals with 2 numeric vars (each 2 bits) in terms of operators == != < <= > >= and 2 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 3-term numeric expression simplification])
-AT_CHECK([ovstest test-ovn exhaustive --operation=simplify --nvars=2 --svars=0 3], [0],
- [Tested simplifying 490770 expressions of 3 terminals with 2 numeric vars (each 3 bits) in terms of operators == != < <= > >=.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term string expression simplification])
-AT_CHECK([ovstest test-ovn exhaustive --operation=simplify --nvars=0 --svars=4 4], [0],
- [Tested simplifying 21978 expressions of 4 terminals with 4 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 3-term mixed expression simplification])
-AT_CHECK([ovstest test-ovn exhaustive --operation=simplify --nvars=1 --svars=1 3], [0],
- [Tested simplifying 127890 expressions of 3 terminals with 1 numeric vars (each 3 bits) in terms of operators == != < <= > >= and 1 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- simplification special cases])
-simplify() {
- echo "$1" | ovstest test-ovn simplify-expr
-}
-AT_CHECK([simplify 'eth.dst == 0/0'], [0], [1
-])
-AT_CHECK([simplify 'eth.dst != 0/0'], [0], [0
-])
-AT_CHECK([simplify 'tcp.dst >= 0'], [0],
- [ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
-])
-AT_CHECK([simplify 'tcp.dst <= 65535'], [0],
- [ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
-])
-AT_CHECK([simplify 'tcp.dst > 0'], [0],
- [[(tcp.dst[0] || tcp.dst[1] || tcp.dst[2] || tcp.dst[3] || tcp.dst[4] || tcp.dst[5] || tcp.dst[6] || tcp.dst[7] || tcp.dst[8] || tcp.dst[9] || tcp.dst[10] || tcp.dst[11] || tcp.dst[12] || tcp.dst[13] || tcp.dst[14] || tcp.dst[15]) && ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
-]])
-AT_CHECK([simplify 'tcp.dst < 65535'], [0],
- [[(!tcp.dst[0] || !tcp.dst[1] || !tcp.dst[2] || !tcp.dst[3] || !tcp.dst[4] || !tcp.dst[5] || !tcp.dst[6] || !tcp.dst[7] || !tcp.dst[8] || !tcp.dst[9] || !tcp.dst[10] || !tcp.dst[11] || !tcp.dst[12] || !tcp.dst[13] || !tcp.dst[14] || !tcp.dst[15]) && ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
-]])
-AT_CLEANUP
-
-AT_SETUP([ovn -- is_chassis_resident simplification])
-simplify() {
- echo "$1" | ovstest test-ovn simplify-expr
-}
-AT_CHECK([simplify 'is_chassis_resident("eth1")'], [0], [1
-])
-AT_CHECK([simplify 'is_chassis_resident("eth2")'], [0], [0
-])
-AT_CHECK([simplify '!is_chassis_resident("eth1")'], [0], [0
-])
-AT_CHECK([simplify '!is_chassis_resident("eth2")'], [0], [1
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term numeric expression normalization])
-AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=3 --svars=0 --bits=1 4], [0],
- [Tested normalizing 1874026 expressions of 4 terminals with 3 numeric vars (each 1 bits) in terms of operators == != < <= > >=.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term string expression normalization])
-AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=0 --svars=3 --bits=1 4], [0],
- [Tested normalizing 11242 expressions of 4 terminals with 3 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term mixed expression normalization])
-AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=1 --bits=1 --svars=2 4], [0],
- [Tested normalizing 175978 expressions of 4 terminals with 1 numeric vars (each 1 bits) in terms of operators == != < <= > >= and 2 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 5-term numeric expression normalization])
-AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=3 --svars=0 --bits=1 --relops='==' 5], [0],
- [Tested normalizing 1317600 expressions of 5 terminals with 3 numeric vars (each 1 bits) in terms of operators ==.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 5-term string expression normalization])
-AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=0 --svars=3 --bits=1 --relops='==' 5], [0],
- [Tested normalizing 368550 expressions of 5 terminals with 3 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 5-term mixed expression normalization])
-AT_CHECK([ovstest test-ovn exhaustive --operation=normalize --nvars=1 --svars=1 --bits=1 --relops='==' 5], [0],
- [Tested normalizing 216000 expressions of 5 terminals with 1 numeric vars (each 1 bits) in terms of operators == and 1 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term numeric expressions to flows])
-AT_KEYWORDS([expression])
-AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=2 --svars=0 --bits=2 --relops='==' 4], [0],
- [Tested converting to flows 175978 expressions of 4 terminals with 2 numeric vars (each 2 bits) in terms of operators ==.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term string expressions to flows])
-AT_KEYWORDS([expression])
-AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=0 --svars=4 4], [0],
- [Tested converting to flows 21978 expressions of 4 terminals with 4 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4-term mixed expressions to flows])
-AT_KEYWORDS([expression])
-AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=1 --bits=2 --svars=1 --relops='==' 4], [0],
- [Tested converting to flows 48312 expressions of 4 terminals with 1 numeric vars (each 2 bits) in terms of operators == and 1 string vars.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 3-term numeric expressions to flows])
-AT_KEYWORDS([expression])
-AT_CHECK([ovstest test-ovn exhaustive --operation=flow --nvars=3 --svars=0 --bits=3 --relops='==' 3], [0],
- [Tested converting to flows 41328 expressions of 3 terminals with 3 numeric vars (each 3 bits) in terms of operators ==.
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- converting expressions to flows -- string fields])
-AT_KEYWORDS([expression])
-expr_to_flow () {
- echo "$1" | ovstest test-ovn expr-to-flows | sort
-}
-AT_CHECK([expr_to_flow 'inport == "eth0"'], [0], [reg14=0x5
-])
-AT_CHECK([expr_to_flow 'inport == "eth1"'], [0], [reg14=0x6
-])
-AT_CHECK([expr_to_flow 'inport == "eth2"'], [0], [(no flows)
-])
-AT_CHECK([expr_to_flow 'inport == "eth0" && ip'], [0], [dnl
-ip,reg14=0x5
-ipv6,reg14=0x5
-])
-AT_CHECK([expr_to_flow 'inport == "eth1" && ip'], [0], [dnl
-ip,reg14=0x6
-ipv6,reg14=0x6
-])
-AT_CHECK([expr_to_flow 'inport == "eth2" && ip'], [0], [(no flows)
-])
-AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2", "LOCAL"}'], [0],
-[reg14=0x5
-reg14=0x6
-reg14=0xfffe
-])
-AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip'], [0], [dnl
-ip,reg14=0x5
-ip,reg14=0x6
-ipv6,reg14=0x5
-ipv6,reg14=0x6
-])
-AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
-(no flows)
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- converting expressions to flows -- address sets])
-AT_KEYWORDS([expression])
-expr_to_flow () {
- echo "$1" | ovstest test-ovn expr-to-flows | sort
-}
-AT_CHECK([expr_to_flow 'ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3}'], [0], [dnl
-ip,nw_src=10.0.0.1
-ip,nw_src=10.0.0.2
-ip,nw_src=10.0.0.3
-])
-AT_CHECK([expr_to_flow 'ip4.src == $set1'], [0], [dnl
-ip,nw_src=10.0.0.1
-ip,nw_src=10.0.0.2
-ip,nw_src=10.0.0.3
-])
-AT_CHECK([expr_to_flow 'ip4.src == {1.2.3.4, $set1}'], [0], [dnl
-ip,nw_src=1.2.3.4
-ip,nw_src=10.0.0.1
-ip,nw_src=10.0.0.2
-ip,nw_src=10.0.0.3
-])
-AT_CHECK([expr_to_flow 'ip4.src == {1.2.0.0/20, 5.5.5.0/24, $set1}'], [0], [dnl
-ip,nw_src=1.2.0.0/20
-ip,nw_src=10.0.0.1
-ip,nw_src=10.0.0.2
-ip,nw_src=10.0.0.3
-ip,nw_src=5.5.5.0/24
-])
-AT_CHECK([expr_to_flow 'ip6.src == {::1, ::2, ::3}'], [0], [dnl
-ipv6,ipv6_src=::1
-ipv6,ipv6_src=::2
-ipv6,ipv6_src=::3
-])
-AT_CHECK([expr_to_flow 'ip6.src == {::1, $set2, ::4}'], [0], [dnl
-ipv6,ipv6_src=::1
-ipv6,ipv6_src=::2
-ipv6,ipv6_src=::3
-ipv6,ipv6_src=::4
-])
-AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, 00:00:00:00:00:02, 00:00:00:00:00:03}'], [0], [dnl
-dl_src=00:00:00:00:00:01
-dl_src=00:00:00:00:00:02
-dl_src=00:00:00:00:00:03
-])
-AT_CHECK([expr_to_flow 'eth.src == {$set3}'], [0], [dnl
-dl_src=00:00:00:00:00:01
-dl_src=00:00:00:00:00:02
-dl_src=00:00:00:00:00:03
-])
-AT_CHECK([expr_to_flow 'eth.src == {00:00:00:00:00:01, $set3, ba:be:be:ef:de:ad, $set3}'], [0], [dnl
-dl_src=00:00:00:00:00:01
-dl_src=00:00:00:00:00:02
-dl_src=00:00:00:00:00:03
-dl_src=ba:be:be:ef:de:ad
-])
-AT_CHECK([expr_to_flow 'ip4.src == {$set4}'], [0], [dnl
-(no flows)
-])
-AT_CHECK([expr_to_flow 'ip4.src == {1.2.3.4, $set4}'], [0], [dnl
-ip,nw_src=1.2.3.4
-])
-AT_CHECK([expr_to_flow 'ip4.src == 1.2.3.4 || ip4.src == {$set4}'], [0], [dnl
-ip,nw_src=1.2.3.4
-])
-AT_CHECK([expr_to_flow 'ip4.src != {$set4}'], [0], [dnl
-
-])
-AT_CHECK([expr_to_flow 'ip4.src != {1.0.0.0/8, $set4}'], [0], [dnl
-ip,nw_src=0.0.0.0/1.0.0.0
-ip,nw_src=128.0.0.0/1
-ip,nw_src=16.0.0.0/16.0.0.0
-ip,nw_src=2.0.0.0/2.0.0.0
-ip,nw_src=32.0.0.0/32.0.0.0
-ip,nw_src=4.0.0.0/4.0.0.0
-ip,nw_src=64.0.0.0/64.0.0.0
-ip,nw_src=8.0.0.0/8.0.0.0
-])
-AT_CHECK([expr_to_flow 'ip4.src != 1.0.0.0/8 && ip4.src != {$set4}'], [0], [dnl
-ip,nw_src=0.0.0.0/1.0.0.0
-ip,nw_src=128.0.0.0/1
-ip,nw_src=16.0.0.0/16.0.0.0
-ip,nw_src=2.0.0.0/2.0.0.0
-ip,nw_src=32.0.0.0/32.0.0.0
-ip,nw_src=4.0.0.0/4.0.0.0
-ip,nw_src=64.0.0.0/64.0.0.0
-ip,nw_src=8.0.0.0/8.0.0.0
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- converting expressions to flows -- port groups])
-AT_KEYWORDS([expression])
-expr_to_flow () {
- echo "$1" | ovstest test-ovn expr-to-flows | sort
-}
-AT_CHECK([expr_to_flow 'outport == @pg1'], [0], [dnl
-reg15=0x11
-reg15=0x12
-reg15=0x13
-])
-AT_CHECK([expr_to_flow 'outport == {@pg_empty}'], [0], [dnl
-(no flows)
-])
-AT_CHECK([expr_to_flow 'outport == {"lsp1", @pg_empty}'], [0], [dnl
-reg15=0x11
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- converting expressions to flows -- conjunction])
-AT_KEYWORDS([conjunction])
-expr_to_flow () {
- echo "$1" | ovstest test-ovn expr-to-flows | sort
-}
-
-lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
-ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3}"
-AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
-conj_id=1,ip
-ip,nw_dst=20.0.0.1: conjunction(1, 0/2)
-ip,nw_dst=20.0.0.2: conjunction(1, 0/2)
-ip,nw_dst=20.0.0.3: conjunction(1, 0/2)
-ip,nw_src=10.0.0.1: conjunction(1, 1/2)
-ip,nw_src=10.0.0.2: conjunction(1, 1/2)
-ip,nw_src=10.0.0.3: conjunction(1, 1/2)
-])
-
-lflow="ip && (!ct.est || (ct.est && ct_label.blocked == 1))"
-AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
-ct_state=+est+trk,ct_label=0x1/0x1,ip
-ct_state=+est+trk,ct_label=0x1/0x1,ipv6
-ct_state=-est+trk,ip
-ct_state=-est+trk,ipv6
-])
-
-lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
-ip4.dst == {20.0.0.1, 20.0.0.2}"
-AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
-conj_id=1,ip
-ip,nw_dst=20.0.0.1: conjunction(1, 0/2)
-ip,nw_dst=20.0.0.2: conjunction(1, 0/2)
-ip,nw_src=10.0.0.1: conjunction(1, 1/2)
-ip,nw_src=10.0.0.2: conjunction(1, 1/2)
-ip,nw_src=10.0.0.3: conjunction(1, 1/2)
-])
-
-lflow="ip4 && ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
-ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3} && \
-tcp.dst >= 1000 && tcp.dst <= 1010"
-
-AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
-conj_id=1,tcp
-tcp,nw_dst=20.0.0.1: conjunction(1, 0/3)
-tcp,nw_dst=20.0.0.2: conjunction(1, 0/3)
-tcp,nw_dst=20.0.0.3: conjunction(1, 0/3)
-tcp,nw_src=10.0.0.1: conjunction(1, 1/3)
-tcp,nw_src=10.0.0.2: conjunction(1, 1/3)
-tcp,nw_src=10.0.0.3: conjunction(1, 1/3)
-tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/3)
-tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/3)
-tcp,tp_dst=0x3f0/0xfffe: conjunction(1, 2/3)
-tcp,tp_dst=1000: conjunction(1, 2/3)
-tcp,tp_dst=1001: conjunction(1, 2/3)
-tcp,tp_dst=1010: conjunction(1, 2/3)
-])
-
-lflow="ip4 && ip4.src == {10.0.0.4, 10.0.0.5, 10.0.0.6} && \
-((ip4.dst == {20.0.0.4, 20.0.0.7, 20.0.0.8} && tcp.dst >= 1000 && \
-tcp.dst <= 2000 && tcp.src >=1000 && tcp.src <= 2000) \
-|| ip4.dst == 20.0.0.5 || ip4.dst == 20.0.0.6)"
-
-AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
-conj_id=1,tcp
-ip,nw_src=10.0.0.4,nw_dst=20.0.0.5
-ip,nw_src=10.0.0.4,nw_dst=20.0.0.6
-ip,nw_src=10.0.0.5,nw_dst=20.0.0.5
-ip,nw_src=10.0.0.5,nw_dst=20.0.0.6
-ip,nw_src=10.0.0.6,nw_dst=20.0.0.5
-ip,nw_src=10.0.0.6,nw_dst=20.0.0.6
-tcp,nw_dst=20.0.0.4: conjunction(1, 0/4)
-tcp,nw_dst=20.0.0.7: conjunction(1, 0/4)
-tcp,nw_dst=20.0.0.8: conjunction(1, 0/4)
-tcp,nw_src=10.0.0.4: conjunction(1, 1/4)
-tcp,nw_src=10.0.0.5: conjunction(1, 1/4)
-tcp,nw_src=10.0.0.6: conjunction(1, 1/4)
-tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/4)
-tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/4)
-tcp,tp_dst=0x3f0/0xfff0: conjunction(1, 2/4)
-tcp,tp_dst=0x400/0xfe00: conjunction(1, 2/4)
-tcp,tp_dst=0x600/0xff00: conjunction(1, 2/4)
-tcp,tp_dst=0x700/0xff80: conjunction(1, 2/4)
-tcp,tp_dst=0x780/0xffc0: conjunction(1, 2/4)
-tcp,tp_dst=0x7c0/0xfff0: conjunction(1, 2/4)
-tcp,tp_dst=1000: conjunction(1, 2/4)
-tcp,tp_dst=1001: conjunction(1, 2/4)
-tcp,tp_dst=2000: conjunction(1, 2/4)
-tcp,tp_src=0x3ea/0xfffe: conjunction(1, 3/4)
-tcp,tp_src=0x3ec/0xfffc: conjunction(1, 3/4)
-tcp,tp_src=0x3f0/0xfff0: conjunction(1, 3/4)
-tcp,tp_src=0x400/0xfe00: conjunction(1, 3/4)
-tcp,tp_src=0x600/0xff00: conjunction(1, 3/4)
-tcp,tp_src=0x700/0xff80: conjunction(1, 3/4)
-tcp,tp_src=0x780/0xffc0: conjunction(1, 3/4)
-tcp,tp_src=0x7c0/0xfff0: conjunction(1, 3/4)
-tcp,tp_src=1000: conjunction(1, 3/4)
-tcp,tp_src=1001: conjunction(1, 3/4)
-tcp,tp_src=2000: conjunction(1, 3/4)
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- action parsing])
-dnl Unindented text is input (a set of OVN logical actions).
-dnl Indented text is expected output.
-AT_DATA([test-cases.txt],
-[[# drop
-drop;
- encodes as drop
-drop; next;
- Syntax error at `next' expecting end of input.
-next; drop;
- Syntax error at `drop' expecting action.
-
-# output
-output;
- encodes as resubmit(,64)
-
-# next
-next;
- encodes as resubmit(,19)
-next(11);
- formats as next;
- encodes as resubmit(,19)
-next(0);
- encodes as resubmit(,8)
-next(23);
- encodes as resubmit(,31)
-
-next();
- Syntax error at `)' expecting "pipeline" or "table".
-next(10;
- Syntax error at `;' expecting `)'.
-next(24);
- "next" action cannot advance beyond table 23.
-
-next(table=11);
- formats as next;
- encodes as resubmit(,19)
-next(pipeline=ingress);
- formats as next;
- encodes as resubmit(,19)
-next(table=11, pipeline=ingress);
- formats as next;
- encodes as resubmit(,19)
-next(pipeline=ingress, table=11);
- formats as next;
- encodes as resubmit(,19)
-
-next(pipeline=egress);
- "next" action cannot advance from ingress to egress pipeline (use "output" action instead)
-
-next(table=10);
- formats as next(10);
- encodes as resubmit(,18)
-
-# Loading a constant value.
-tcp.dst=80;
- formats as tcp.dst = 80;
- encodes as set_field:80->tcp_dst
- has prereqs ip.proto == 0x6 && (eth.type == 0x800 || eth.type == 0x86dd)
-eth.dst[40] = 1;
- encodes as set_field:01:00:00:00:00:00/01:00:00:00:00:00->eth_dst
-vlan.pcp = 2;
- encodes as set_field:0x4000/0xe000->vlan_tci
- has prereqs vlan.tci[12]
-vlan.tci[13..15] = 2;
- encodes as set_field:0x4000/0xe000->vlan_tci
-inport = "";
- encodes as set_field:0->reg14
-ip.ttl=4;
- formats as ip.ttl = 4;
- encodes as set_field:4->nw_ttl
- has prereqs eth.type == 0x800 || eth.type == 0x86dd
-outport="eth0"; next; outport="LOCAL"; next;
- formats as outport = "eth0"; next; outport = "LOCAL"; next;
- encodes as set_field:0x5->reg15,resubmit(,19),set_field:0xfffe->reg15,resubmit(,19)
-
-inport[1] = 1;
- Cannot select subfield of string field inport.
-ip.proto[1] = 1;
- Cannot select subfield of nominal field ip.proto.
-eth.dst[40] == 1;
- Syntax error at `==' expecting `=' or `<->'.
-ip = 1;
- Predicate symbol ip used where lvalue required.
-ip.proto = 6;
- Field ip.proto is not modifiable.
-inport = {"a", "b"};
- Syntax error at `{' expecting constant.
-inport = {};
- Syntax error at `{' expecting constant.
-bad_prereq = 123;
- Error parsing expression `xyzzy' encountered as prerequisite or predicate of initial expression: Syntax error at `xyzzy' expecting field name.
-self_recurse = 123;
- Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Error parsing expression `self_recurse != 0' encountered as prerequisite or predicate of initial expression: Recursive expansion of symbol `self_recurse'.
-vlan.present = 0;
- Predicate symbol vlan.present used where lvalue required.
-
-# Moving one field into another.
-reg0=reg1;
- formats as reg0 = reg1;
- encodes as move:NXM_NX_XXREG0[64..95]->NXM_NX_XXREG0[96..127]
-vlan.pcp = reg0[0..2];
- encodes as move:NXM_NX_XXREG0[96..98]->NXM_OF_VLAN_TCI[13..15]
- has prereqs vlan.tci[12]
-reg0[10] = vlan.pcp[1];
- encodes as move:NXM_OF_VLAN_TCI[14]->NXM_NX_XXREG0[106]
- has prereqs vlan.tci[12]
-outport = inport;
- encodes as move:NXM_NX_REG14[]->NXM_NX_REG15[]
-
-reg0[0] = vlan.present;
- Predicate symbol vlan.present used where lvalue required.
-reg0 = reg1[0..10];
- Can't assign 11-bit value to 32-bit destination.
-inport = reg0;
- Can't assign integer field (reg0) to string field (inport).
-inport = big_string;
- String fields inport and big_string are incompatible for assignment.
-ip.proto = reg0[0..7];
- Field ip.proto is not modifiable.
-
-# Exchanging fields.
-reg0 <-> reg1;
- encodes as push:NXM_NX_XXREG0[64..95],push:NXM_NX_XXREG0[96..127],pop:NXM_NX_XXREG0[64..95],pop:NXM_NX_XXREG0[96..127]
-vlan.pcp <-> reg0[0..2];
- encodes as push:NXM_NX_XXREG0[96..98],push:NXM_OF_VLAN_TCI[13..15],pop:NXM_NX_XXREG0[96..98],pop:NXM_OF_VLAN_TCI[13..15]
- has prereqs vlan.tci[12]
-reg0[10] <-> vlan.pcp[1];
- encodes as push:NXM_OF_VLAN_TCI[14],push:NXM_NX_XXREG0[106],pop:NXM_OF_VLAN_TCI[14],pop:NXM_NX_XXREG0[106]
- has prereqs vlan.tci[12]
-outport <-> inport;
- encodes as push:NXM_NX_REG14[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],pop:NXM_NX_REG15[]
-
-reg0[0] <-> vlan.present;
- Predicate symbol vlan.present used where lvalue required.
-reg0 <-> reg1[0..10];
- Can't exchange 32-bit field with 11-bit field.
-inport <-> reg0;
- Can't exchange string field (inport) with integer field (reg0).
-inport <-> big_string;
- String fields inport and big_string are incompatible for exchange.
-ip.proto <-> reg0[0..7];
- Field ip.proto is not modifiable.
-reg0[0..7] <-> ip.proto;
- Field ip.proto is not modifiable.
-
-# TTL decrement.
-ip.ttl--;
- encodes as dec_ttl
- has prereqs ip
-ip.ttl
- Syntax error at end of input expecting `--'.
-
-# load balancing.
-ct_lb;
- encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat)
- has prereqs ip
-ct_lb();
- formats as ct_lb;
- encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat)
- has prereqs ip
-ct_lb(192.168.1.2:80, 192.168.1.3:80);
- encodes as group:1
- has prereqs ip
-ct_lb(192.168.1.2, 192.168.1.3, );
- formats as ct_lb(192.168.1.2, 192.168.1.3);
- encodes as group:2
- has prereqs ip
-ct_lb(fd0f::2, fd0f::3, );
- formats as ct_lb(fd0f::2, fd0f::3);
- encodes as group:3
- has prereqs ip
-
-ct_lb(192.168.1.2:);
- Syntax error at `)' expecting port number.
-ct_lb(192.168.1.2:123456);
- Syntax error at `123456' expecting port number.
-ct_lb(foo);
- Syntax error at `foo' expecting IP address.
-ct_lb([192.168.1.2]);
- Syntax error at `192.168.1.2' expecting IPv6 address.
-
-# ct_next
-ct_next;
- encodes as ct(table=19,zone=NXM_NX_REG13[0..15])
- has prereqs ip
-
-# ct_commit
-ct_commit;
- encodes as ct(commit,zone=NXM_NX_REG13[0..15])
- has prereqs ip
-ct_commit();
- formats as ct_commit;
- encodes as ct(commit,zone=NXM_NX_REG13[0..15])
- has prereqs ip
-ct_commit(ct_mark=1);
- formats as ct_commit(ct_mark=0x1);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark))
- has prereqs ip
-ct_commit(ct_mark=1/1);
- formats as ct_commit(ct_mark=0x1/0x1);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_mark))
- has prereqs ip
-ct_commit(ct_label=1);
- formats as ct_commit(ct_label=0x1);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_label))
- has prereqs ip
-ct_commit(ct_label=1/1);
- formats as ct_commit(ct_label=0x1/0x1);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1/0x1->ct_label))
- has prereqs ip
-ct_commit(ct_mark=1, ct_label=2);
- formats as ct_commit(ct_mark=0x1, ct_label=0x2);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1->ct_mark,set_field:0x2->ct_label))
- has prereqs ip
-
-ct_commit(ct_label=0x01020304050607080910111213141516);
- formats as ct_commit(ct_label=0x1020304050607080910111213141516);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1020304050607080910111213141516->ct_label))
- has prereqs ip
-ct_commit(ct_label=0x181716151413121110090807060504030201);
- formats as ct_commit(ct_label=0x16151413121110090807060504030201);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x16151413121110090807060504030201->ct_label))
- has prereqs ip
-ct_commit(ct_label=0x1000000000000000000000000000000/0x1000000000000000000000000000000);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0x1000000000000000000000000000000/0x1000000000000000000000000000000->ct_label))
- has prereqs ip
-ct_commit(ct_label=18446744073709551615);
- formats as ct_commit(ct_label=0xffffffffffffffff);
- encodes as ct(commit,zone=NXM_NX_REG13[0..15],exec(set_field:0xffffffffffffffff->ct_label))
- has prereqs ip
-ct_commit(ct_label=18446744073709551616);
- Decimal constants must be less than 2**64.
-
-# ct_dnat
-ct_dnat;
- encodes as ct(table=19,zone=NXM_NX_REG11[0..15],nat)
- has prereqs ip
-ct_dnat(192.168.1.2);
- encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2))
- has prereqs ip
-
-ct_dnat(192.168.1.2, 192.168.1.3);
- Syntax error at `,' expecting `)'.
-ct_dnat(foo);
- Syntax error at `foo' expecting IPv4 address.
-ct_dnat(foo, bar);
- Syntax error at `foo' expecting IPv4 address.
-ct_dnat();
- Syntax error at `)' expecting IPv4 address.
-
-# ct_snat
-ct_snat;
- encodes as ct(table=19,zone=NXM_NX_REG12[0..15],nat)
- has prereqs ip
-ct_snat(192.168.1.2);
- encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=192.168.1.2))
- has prereqs ip
-
-ct_snat(192.168.1.2, 192.168.1.3);
- Syntax error at `,' expecting `)'.
-ct_snat(foo);
- Syntax error at `foo' expecting IPv4 address.
-ct_snat(foo, bar);
- Syntax error at `foo' expecting IPv4 address.
-ct_snat();
- Syntax error at `)' expecting IPv4 address.
-
-# ct_clear
-ct_clear;
- encodes as ct_clear
-
-# clone
-clone { ip4.dst = 255.255.255.255; output; }; next;
- encodes as clone(set_field:255.255.255.255->ip_dst,resubmit(,64)),resubmit(,19)
- has prereqs eth.type == 0x800
-
-# arp
-arp { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
- encodes as controller(userdata=00.00.00.00.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs ip4
-arp { };
- formats as arp { drop; };
- encodes as controller(userdata=00.00.00.00.00.00.00.00)
- has prereqs ip4
-
-# get_arp
-get_arp(outport, ip4.dst);
- encodes as push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],pop:NXM_NX_REG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG0[]
- has prereqs eth.type == 0x800
-get_arp(inport, reg0);
- encodes as push:NXM_NX_REG15[],push:NXM_NX_REG0[],push:NXM_NX_XXREG0[96..127],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],pop:NXM_NX_REG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG0[],pop:NXM_NX_REG15[]
-
-get_arp;
- Syntax error at `;' expecting `('.
-get_arp();
- Syntax error at `)' expecting field name.
-get_arp(inport);
- Syntax error at `)' expecting `,'.
-get_arp(inport ip4.dst);
- Syntax error at `ip4.dst' expecting `,'.
-get_arp(inport, ip4.dst;
- Syntax error at `;' expecting `)'.
-get_arp(inport, eth.dst);
- Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required.
-get_arp(inport, outport);
- Cannot use string field outport where numeric field is required.
-get_arp(reg0, ip4.dst);
- Cannot use numeric field reg0 where string field is required.
-
-# put_arp
-put_arp(inport, arp.spa, arp.sha);
- encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.01.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[]
- has prereqs eth.type == 0x806 && eth.type == 0x806
-
-# put_dhcp_opts
-reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
- encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
-reg2[5] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.254.0,mtu=1400,domain_name="ovn.org",wpad="https://example.org",bootfile_name="https://127.0.0.1/boot.ipxe",path_prefix="/tftpboot");
- formats as reg2[5] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.254.0, mtu = 1400, domain_name = "ovn.org", wpad = "https://example.org", bootfile_name = "https://127.0.0.1/boot.ipxe", path_prefix = "/tftpboot");
- encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.25.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.fe.00.1a.02.05.78.0f.07.6f.76.6e.2e.6f.72.67.fc.13.68.74.74.70.73.3a.2f.2f.65.78.61.6d.70.6c.65.2e.6f.72.67.43.1b.68.74.74.70.73.3a.2f.2f.31.32.37.2e.30.2e.30.2e.31.2f.62.6f.6f.74.2e.69.70.78.65.d2.09.2f.74.66.74.70.62.6f.6f.74,pause)
-reg0[15] = put_dhcp_opts(offerip=10.0.0.4,router=10.0.0.1,netmask=255.255.255.0,mtu=1400,ip_forward_enable=1,default_ttl=121,dns_server={8.8.8.8,7.7.7.7},classless_static_route={30.0.0.0/24,10.0.0.4,40.0.0.0/16,10.0.0.6,0.0.0.0/0,10.0.0.1},ethernet_encap=1,router_discovery=0,tftp_server_address={10.0.0.4,10.0.0.5});
- formats as reg0[15] = put_dhcp_opts(offerip = 10.0.0.4, router = 10.0.0.1, netmask = 255.255.255.0, mtu = 1400, ip_forward_enable = 1, default_ttl = 121, dns_server = {8.8.8.8, 7.7.7.7}, classless_static_route = {30.0.0.0/24, 10.0.0.4, 40.0.0.0/16, 10.0.0.6, 0.0.0.0/0, 10.0.0.1}, ethernet_encap = 1, router_discovery = 0, tftp_server_address = {10.0.0.4, 10.0.0.5});
- encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.6f.0a.00.00.04.03.04.0a.00.00.01.01.04.ff.ff.ff.00.1a.02.05.78.13.01.01.17.01.79.06.08.08.08.08.08.07.07.07.07.79.14.18.1e.00.00.0a.00.00.04.10.28.00.0a.00.00.06.00.0a.00.00.01.24.01.01.1f.01.00.96.08.0a.00.00.04.0a.00.00.05,pause)
-
-reg1[0..1] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
- Cannot use 2-bit field reg1[0..1] where 1-bit field is required.
-reg1[0] = put_dhcp_opts();
- put_dhcp_opts requires offerip to be specified.
-reg1[0] = put_dhcp_opts(x = 1.2.3.4, router = 10.0.0.1);
- Syntax error at `x' expecting DHCPv4 option name.
-reg1[0] = put_dhcp_opts(router = 10.0.0.1);
- put_dhcp_opts requires offerip to be specified.
-reg1[0] = put_dhcp_opts(offerip=1.2.3.4, "hi");
- Syntax error at `"hi"'.
-reg1[0] = put_dhcp_opts(offerip=1.2.3.4, xyzzy);
- Syntax error at `xyzzy' expecting DHCPv4 option name.
-reg1[0] = put_dhcp_opts(offerip="xyzzy");
- DHCPv4 option offerip requires numeric value.
-reg1[0] = put_dhcp_opts(offerip=1.2.3.4, domain_name=1.2.3.4);
- DHCPv4 option domain_name requires string value.
-
-# nd_ns
-nd_ns { nd.target = xxreg0; output; };
- encodes as controller(userdata=00.00.00.09.00.00.00.00.ff.ff.00.18.00.00.23.20.00.06.00.80.00.00.00.00.00.01.de.10.00.01.2e.10.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
- has prereqs ip6
-
-nd_ns { };
- formats as nd_ns { drop; };
- encodes as controller(userdata=00.00.00.09.00.00.00.00)
- has prereqs ip6
-
-# nd_na
-nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; };
- formats as nd_na { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; };
- encodes as controller(userdata=00.00.00.03.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
- has prereqs nd_ns
-# nd_na_router
-nd_na_router { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; /* Allow sending out inport. */ output; };
- formats as nd_na_router { eth.src = 12:34:56:78:9a:bc; nd.tll = 12:34:56:78:9a:bc; outport = inport; inport = ""; output; };
- encodes as controller(userdata=00.00.00.0c.00.00.00.00.00.19.00.10.80.00.08.06.12.34.56.78.9a.bc.00.00.00.19.00.10.80.00.42.06.12.34.56.78.9a.bc.00.00.ff.ff.00.18.00.00.23.20.00.06.00.20.00.00.00.00.00.01.1c.04.00.01.1e.04.00.19.00.10.00.01.1c.04.00.00.00.00.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00)
- has prereqs nd_ns
-
-# get_nd
-get_nd(outport, ip6.dst);
- encodes as push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_XXREG0[]
- has prereqs eth.type == 0x86dd
-get_nd(inport, xxreg0);
- encodes as push:NXM_NX_REG15[],push:NXM_NX_REG14[],pop:NXM_NX_REG15[],set_field:00:00:00:00:00:00->eth_dst,resubmit(,65),pop:NXM_NX_REG15[]
-get_nd;
- Syntax error at `;' expecting `('.
-get_nd();
- Syntax error at `)' expecting field name.
-get_nd(inport);
- Syntax error at `)' expecting `,'.
-get_nd(inport ip6.dst);
- Syntax error at `ip6.dst' expecting `,'.
-get_nd(inport, ip6.dst;
- Syntax error at `;' expecting `)'.
-get_nd(inport, eth.dst);
- Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required.
-get_nd(inport, outport);
- Cannot use string field outport where numeric field is required.
-get_nd(xxreg0, ip6.dst);
- Cannot use numeric field xxreg0 where string field is required.
-
-# put_nd
-put_nd(inport, nd.target, nd.sll);
- encodes as push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_SLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.04.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[]
- has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x87 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd)
-
-# put_dhcpv6_opts
-reg1[0] = put_dhcpv6_opts(ia_addr = ae70::4, server_id = 00:00:00:00:10:02);
- encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.05.00.10.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.04.00.02.00.06.00.00.00.00.10.02,pause)
-reg1[0] = put_dhcpv6_opts();
- encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40,pause)
-reg1[0] = put_dhcpv6_opts(dns_server={ae70::1,ae70::2});
- formats as reg1[0] = put_dhcpv6_opts(dns_server = {ae70::1, ae70::2});
- encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.17.00.20.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.01.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.02,pause)
-reg1[0] = put_dhcpv6_opts(server_id=12:34:56:78:9a:bc, dns_server={ae70::1,ae89::2});
- formats as reg1[0] = put_dhcpv6_opts(server_id = 12:34:56:78:9a:bc, dns_server = {ae70::1, ae89::2});
- encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.02.00.06.12.34.56.78.9a.bc.00.17.00.20.ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.01.ae.89.00.00.00.00.00.00.00.00.00.00.00.00.00.02,pause)
-reg1[0] = put_dhcpv6_opts(domain_search = "ovn.org");
- encodes as controller(userdata=00.00.00.05.00.00.00.00.00.01.de.10.00.00.00.40.00.18.00.07.6f.76.6e.2e.6f.72.67,pause)
-reg1[0] = put_dhcpv6_opts(x = 1.2.3.4);
- Syntax error at `x' expecting DHCPv6 option name.
-reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, "hi");
- Syntax error at `"hi"'.
-reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, xyzzy);
- Syntax error at `xyzzy' expecting DHCPv6 option name.
-reg1[0] = put_dhcpv6_opts(ia_addr="ae70::4");
- DHCPv6 option ia_addr requires numeric value.
-reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, domain_search=ae70::1);
- DHCPv6 option domain_search requires string value.
-
-# set_queue
-set_queue(0);
- encodes as set_queue:0
-set_queue(61440);
- encodes as set_queue:61440
-set_queue(65535);
- Queue ID 65535 for set_queue is not in valid range 0 to 61440.
-
-# dns_lookup
-reg1[0] = dns_lookup();
- encodes as controller(userdata=00.00.00.06.00.00.00.00.00.01.de.10.00.00.00.40,pause)
- has prereqs udp
-reg1[0] = dns_lookup("foo");
- dns_lookup doesn't take any parameters
-
-# set_meter
-set_meter(0);
- Rate 0 for set_meter is not in valid.
-set_meter(1);
- encodes as meter:1
-set_meter(100, 1000);
- encodes as meter:2
-set_meter(100, 1000, );
- Syntax error at `,' expecting `)'.
-set_meter(4294967295, 4294967295);
- encodes as meter:3
-
-# log
-log(verdict=allow, severity=warning);
- encodes as controller(userdata=00.00.00.07.00.00.00.00.00.04)
-log(name="test1", verdict=drop, severity=info);
- encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06.74.65.73.74.31)
-log(verdict=drop, severity=info, meter="meter1");
- encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06,meter_id=4)
-log(name="test1", verdict=drop, severity=info, meter="meter1");
- encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06.74.65.73.74.31,meter_id=4)
-log(verdict=drop);
- formats as log(verdict=drop, severity=info);
- encodes as controller(userdata=00.00.00.07.00.00.00.00.01.06)
-log(verdict=bad_verdict, severity=info);
- Syntax error at `bad_verdict' unknown verdict.
-log(verdict=drop, severity=bad_severity);
- Syntax error at `bad_severity' unknown severity.
-log(severity=notice);
- Syntax error at `;' expecting verdict.
-
-# put_nd_ra_opts
-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64, slla = ae:01:02:03:04:05);
- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.00.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
- has prereqs ip6
-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", slla = ae:01:02:03:04:10, mtu = 1450);
- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10.05.01.00.00.00.00.05.aa,pause)
- has prereqs ip6
-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla = ae:01:02:03:04:06, prefix = aef0::/64);
- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.40.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause)
- has prereqs ip6
-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64);
- slla option not present
-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", mtu = 1450, prefix = aef0::/64, prefix = bef0::/64, slla = ae:01:02:03:04:10);
- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.aa.03.04.40.80.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.03.04.40.80.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.be.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10,pause)
- has prereqs ip6
-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", mtu = 1450, prefix = aef0::/64, prefix = bef0::/64, slla = ae:01:02:03:04:10);
- encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.aa.03.04.40.80.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.03.04.40.80.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.be.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10,pause)
- has prereqs ip6
-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", slla = ae:01:02:03:04:10);
- prefix option needs to be set when address mode is slaac/dhcpv6_stateless.
-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla = ae:01:02:03:04:10);
- prefix option needs to be set when address mode is slaac/dhcpv6_stateless.
-reg1[0] = put_nd_ra_opts(addr_mode = dhcpv6_stateless, prefix = aef0::/64, slla = ae:01:02:03:04:10);
- Syntax error at `dhcpv6_stateless' expecting constant.
-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::, slla = ae:01:02:03:04:10);
- Invalid value for "prefix" option
-reg1[0] = put_nd_ra_opts(addr_mode = "foo", mtu = 1500, slla = ae:01:02:03:04:10);
- Invalid value for "addr_mode" option
-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = "1500", slla = ae:01:02:03:04:10);
- IPv6 ND RA option mtu requires numeric value.
-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 10.0.0.4, slla = ae:01:02:03:04:10);
- Invalid value for "mtu" option
-
-# icmp4
-icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
- encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs ip4
-
-icmp4 { };
- formats as icmp4 { drop; };
- encodes as controller(userdata=00.00.00.0a.00.00.00.00)
- has prereqs ip4
-
-# icmp4 with icmp4.frag_mtu
-icmp4 { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output;
- encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.dc.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs ip4
-
-# icmp4_error
-icmp4_error { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
- encodes as controller(userdata=00.00.00.0e.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs ip4
-
-icmp4_error { };
- formats as icmp4_error { drop; };
- encodes as controller(userdata=00.00.00.0e.00.00.00.00)
- has prereqs ip4
-
-# icmp4_error with icmp4.frag_mtu
-icmp4_error { eth.dst = ff:ff:ff:ff:ff:ff; icmp4.frag_mtu = 1500; output; }; output;
- encodes as controller(userdata=00.00.00.0e.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.28.00.00.23.20.00.25.00.00.00.00.00.00.00.03.00.0e.00.00.00.0d.00.00.00.00.05.dc.00.00.00.04.00.04.00.00.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs ip4
-
-icmp4.frag_mtu = 1500;
- encodes as controller(userdata=00.00.00.0d.00.00.00.00.05.dc,pause)
-
-# icmp6
-icmp6 { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
- encodes as controller(userdata=00.00.00.0a.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs ip6
-
-icmp6 { };
- formats as icmp6 { drop; };
- encodes as controller(userdata=00.00.00.0a.00.00.00.00)
- has prereqs ip6
-
-# tcp_reset
-tcp_reset { eth.dst = ff:ff:ff:ff:ff:ff; output; }; output;
- encodes as controller(userdata=00.00.00.0b.00.00.00.00.00.19.00.10.80.00.06.06.ff.ff.ff.ff.ff.ff.00.00.ff.ff.00.10.00.00.23.20.00.0e.ff.f8.40.00.00.00),resubmit(,64)
- has prereqs tcp
-
-tcp_reset { };
- formats as tcp_reset { drop; };
- encodes as controller(userdata=00.00.00.0b.00.00.00.00)
- has prereqs tcp
-
-# trigger_event
-trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
- encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63)
-
-# Testing invalid vip results in extra error messages from socket-util.c
-trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "sctp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
- Load balancer protocol 'sctp' is not 'tcp' or 'udp'
-trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "bacon");
- Load balancer 'bacon' is not a UUID
-
-# IGMP
-igmp;
- encodes as controller(userdata=00.00.00.10.00.00.00.00)
-
-# Contradictionary prerequisites (allowed but not useful):
-ip4.src = ip6.src[0..31];
- encodes as move:NXM_NX_IPV6_SRC[0..31]->NXM_OF_IP_SRC[]
- has prereqs eth.type == 0x800 && eth.type == 0x86dd
-ip4.src <-> ip6.src[0..31];
- encodes as push:NXM_NX_IPV6_SRC[0..31],push:NXM_OF_IP_SRC[],pop:NXM_NX_IPV6_SRC[0..31],pop:NXM_OF_IP_SRC[]
- has prereqs eth.type == 0x800 && eth.type == 0x86dd
-
-# check_pkt_larger
-reg0[0] = check_pkt_larger(1500);
- encodes as check_pkt_larger(1500)->NXM_NX_XXREG0[96]
-
-reg0 = check_pkt_larger(1500);
- Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
-
-reg0 = check_pkt_larger(foo);
- Cannot use 32-bit field reg0[0..31] where 1-bit field is required.
-
-reg0[0] = check_pkt_larger(foo);
- Syntax error at `foo' expecting `;'.
-
-# Miscellaneous negative tests.
-;
- Syntax error at `;'.
-xyzzy;
- Syntax error at `xyzzy' expecting action.
-next; 123;
- Syntax error at `123'.
-next; xyzzy;
- Syntax error at `xyzzy' expecting action.
-next
- Syntax error at end of input expecting `;'.
-]])
-sed '/^[[ ]]/d' test-cases.txt > input.txt
-cp test-cases.txt expout
-AT_CHECK([ovstest test-ovn parse-actions < input.txt], [0], [expout])
-AT_CLEANUP
-
-AT_BANNER([OVN end-to-end tests])
-
-# 3 hypervisors, one logical switch, 3 logical ports per hypervisor
-AT_SETUP([ovn -- 3 HVs, 1 LS, 3 lports/HV])
-AT_KEYWORDS([ovnarp])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Create hypervisors hv[123].
-# Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3.
-# Add all of the vifs to a single logical switch lsw0.
-# Turn on port security on all the vifs except vif[123]1.
-# Make vif13, vif2[23], vif3[123] destinations for unknown MACs.
-# Add some ACLs for Ethertypes 1234, 1235, 1236.
-ovn-nbctl ls-add lsw0
-net_add n1
-for i in 1 2 3; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-
- for j in 1 2 3; do
- ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j
- ovn-nbctl lsp-add lsw0 lp$i$j
- if test $j = 1; then
- ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown
- else
- if test $j = 3; then
- ip_addrs="192.168.0.$i$j fe80::ea2a:eaff:fe28:$i$j/64 192.169.0.$i$j"
- else
- ip_addrs="192.168.0.$i$j"
- fi
- ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j $ip_addrs"
- ovn-nbctl lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j
- fi
- done
-done
-ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1234' drop
-ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1235 && inport == "lp11"' drop
-ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1236 && outport == "lp33"' drop
-ovn-nbctl create Address_Set name=set1 addresses=\"f0:00:00:00:00:11\",\"f0:00:00:00:00:21\",\"f0:00:00:00:00:31\"
-ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1237 && eth.src == $set1 && outport == "lp33"' drop
-
-get_lsp_uuid () {
- ovn-nbctl lsp-list lsw0 | grep $1 | awk '{ print $1 }'
-}
-
-ovn-nbctl create Port_Group name=pg1 ports=`get_lsp_uuid lp22`,`get_lsp_uuid lp33`
-ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1238 && outport == @pg1' drop
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Make sure there is no attempt to adding duplicated flows by ovn-controller
-AT_FAIL_IF([test -n "`grep duplicate hv1/ovn-controller.log`"])
-AT_FAIL_IF([test -n "`grep duplicate hv2/ovn-controller.log`"])
-AT_FAIL_IF([test -n "`grep duplicate hv3/ovn-controller.log`"])
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- echo hv${1%?}
-}
-
-# test_packet INPORT DST SRC ETHTYPE OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 11 for vif11.
-for i in 1 2 3; do
- for j in 1 2 3; do
- : > $i$j.expected
- done
-done
-test_packet() {
- local inport=$1 packet=$2$3$4; shift; shift; shift; shift
- hv=`vif_to_hv $inport`
- vif=vif$inport
- as $hv ovs-appctl netdev-dummy/receive $vif $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-# test_arp INPORT SHA SPA TPA [REPLY_HA]
-#
-# Causes a packet to be received on INPORT. The packet is an ARP
-# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
- local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
- local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
- hv=`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $request
-
- if test X$reply_ha = X; then
- # Expect to receive the broadcast ARP on the other logical switch ports
- # if no reply is expected.
- local i j
- for i in 1 2 3; do
- for j in 1 2 3; do
- if test $i$j != $inport; then
- echo $request >> $i$j.expected
- fi
- done
- done
- else
- # Expect to receive the reply, if any.
- local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa}
- echo $reply >> $inport.expected
- fi
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send packets between all pairs of source and destination ports:
-#
-# 1. Unicast packets are delivered to exactly one logical switch port
-# (except that packets destined to their input ports are dropped).
-#
-# 2. Broadcast and multicast are delivered to all logical switch ports
-# except the input port.
-#
-# 3. When port security is turned on, the switch drops packets from the wrong
-# MAC address.
-#
-# 4. The switch drops all packets with a VLAN tag.
-#
-# 5. The switch drops all packets with a multicast source address. (This only
-# affects behavior when port security is turned off, since otherwise port
-# security would drop the packet anyway.)
-#
-# 6. The switch delivers packets with an unknown destination to logical
-# switch ports with "unknown" among their MAC addresses (and port
-# security disabled).
-#
-# 7. The switch drops unicast packets that violate an ACL.
-#
-# 8. The switch drops multicast and broadcast packets that violate an ACL.
-#
-# 9. OVN generates responses to ARP requests for known IPs, except for
-# requests from a port for the port's own IP.
-#
-# 10. No response to ARP requests for unknown IPs.
-
-for is in 1 2 3; do
- for js in 1 2 3; do
- s=$is$js
- bcast=
- unknown=
- bacl2=
- bacl3=
- for id in 1 2 3; do
- for jd in 1 2 3; do
- d=$id$jd
-
- if test $d != $s; then unicast=$d; else unicast=; fi
- test_packet $s f000000000$d f000000000$s $s$d $unicast #1
-
- if test $d != $s && test $js = 1; then
- impersonate=$d
- else
- impersonate=
- fi
- test_packet $s f000000000$d f00000000055 55$d $impersonate #3
-
- if test $d != $s && test $s != 11; then acl2=$d; else acl2=; fi
- if test $d != $s && test $d != 33; then acl3=$d; else acl3=; fi
- if test $d = $s || (test $js = 1 && test $d = 33); then
- # Source of 11, 21, or 31 and dest of 33 should be dropped
- # due to the 4th ACL that uses address_set(set1).
- acl4=
- else
- acl4=$d
- fi
- if test $d = $s || test $d = 22 || test $d = 33; then
- # dest of 22 and 33 should be dropped
- # due to the 5th ACL that uses port_group(pg1).
- acl5=
- else
- acl5=$d
- fi
- test_packet $s f000000000$d f000000000$s 1234 #7, acl1
- test_packet $s f000000000$d f000000000$s 1235 $acl2 #7, acl2
- test_packet $s f000000000$d f000000000$s 1236 $acl3 #7, acl3
- test_packet $s f000000000$d f000000000$s 1237 $acl4 #7, acl4
- test_packet $s f000000000$d f000000000$s 1238 $acl5 #7, acl5
-
- test_packet $s f000000000$d f00000000055 810000091234 #4
- test_packet $s f000000000$d 0100000000$s $s$d #5
-
- if test $d != $s && test $jd = 1; then
- unknown="$unknown $d"
- fi
- bcast="$bcast $unicast"
- bacl2="$bacl2 $acl2"
- bacl3="$bacl3 $acl3"
-
- sip=`ip_to_hex 192 168 0 $is$js`
- tip=`ip_to_hex 192 168 0 $id$jd`
- tip_unknown=`ip_to_hex 11 11 11 11`
- if test $d != $s; then
- reply_ha=f000000000$d
- else
- reply_ha=
- fi
- test_arp $s f000000000$s $sip $tip $reply_ha #9
- test_arp $s f000000000$s $sip $tip_unknown #10
-
- if test $jd = 3; then
- # lsp[123]3 has an additional ip 192.169.0.[123]3.
- tip=`ip_to_hex 192 169 0 $id$jd`
- test_arp $s f000000000$s $sip $tip $reply_ha #9
- fi
- done
- done
-
- # Broadcast and multicast.
- test_packet $s ffffffffffff f000000000$s ${s}ff $bcast #2
- test_packet $s 010000000000 f000000000$s ${s}ff $bcast #2
- if test $js = 1; then
- bcast_impersonate=$bcast
- else
- bcast_impersonate=
- fi
- test_packet $s 010000000000 f00000000044 44ff $bcast_impersonate #3
-
- test_packet $s f0000000ffff f000000000$s ${s}66 $unknown #6
-
- test_packet $s ffffffffffff f000000000$s 1234 #8, acl1
- test_packet $s ffffffffffff f000000000$s 1235 $bacl2 #8, acl2
- test_packet $s ffffffffffff f000000000$s 1236 $bacl3 #8, acl3
- test_packet $s 010000000000 f000000000$s 1234 #8, acl1
- test_packet $s 010000000000 f000000000$s 1235 $bacl2 #8, acl2
- test_packet $s 010000000000 f000000000$s 1236 $bacl3 #8, acl3
- done
-done
-
-# set address for lp13 with invalid characters.
-# lp13 should be configured with only 192.168.0.13.
-ovn-nbctl lsp-set-addresses lp13 "f0:00:00:00:00:13 192.168.0.13 invalid 192.169.0.13"
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-sip=`ip_to_hex 192 168 0 11`
-tip=`ip_to_hex 192 168 0 13`
-test_arp 11 f00000000011 $sip $tip f00000000013
-
-tip=`ip_to_hex 192 169 0 13`
-#arp request for 192.169.0.13 should be flooded
-test_arp 11 f00000000011 $sip $tip
-
-# dump information and flows with counters
-ovn-sbctl dump-flows -- list multicast_group
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv2 dump ------"
-as hv2 ovs-vsctl show
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv3 dump ------"
-as hv3 ovs-vsctl show
-as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- for j in 1 2 3; do
- OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected])
- done
-done
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-
-AT_CLEANUP
-
-# 2 hypervisors, one logical switch, 2 logical ports per hypervisor
-# logical ports bound to chassis encap-ip.
-AT_SETUP([ovn -- 2 HVs, 1 LS, 2 lports/HV])
-AT_KEYWORDS([ovnarp])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Create hypervisors hv[12].
-# Add vif1[12] to hv1, vif2[12] to hv2
-ovn-nbctl ls-add lsw0
-net_add n1
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-
- for j in 1 2; do
- ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j
- ovn-nbctl lsp-add lsw0 lp$i$j
- ip_addrs="192.168.0.$i$j"
- ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j $ip_addrs"
- ovn-nbctl --wait=hv lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j
- done
-done
-
-get_lsp_uuid () {
- ovn-nbctl lsp-list lsw0 | grep $1 | awk '{ print $1 }'
-}
-
-# XXX-Check how to pass lp$i1 in AT_CHECK_UNQUOTED, for now just do it
-# explictly
-
-# For Chassis hv1
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp11], [0], [dnl
-encap : [[]]
-])
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp12], [0], [dnl
-encap : [[]]
-])
-
-# For Chassis hv2
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp21], [0], [dnl
-encap : [[]]
-])
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp22], [0], [dnl
-encap : [[]]
-])
-
-# Bind the ports to the encap-ip
-for i in 1 2; do
- for j in 1 2; do
- as hv$i
- ovs-vsctl set Interface vif$i$j external-ids:encap-ip=192.168.0.$i
- done
-done
-
-sleep 1
-
-# dump port bindings; since we have vxlan and geneve tunnels, we expect the
-# ports to be bound to geneve tunnels.
-
-# For Chassis 1
-encap_rec=`ovn-sbctl --data=bare --no-heading --column _uuid find encap chassis_name=hv1 type=geneve ip=192.168.0.1`
-
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp11], [0], [dnl
-encap : ${encap_rec}
-])
-
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp12], [0], [dnl
-encap : ${encap_rec}
-])
-
-# For Chassis 2
-encap_rec=`ovn-sbctl --data=bare --no-heading --column _uuid find encap chassis_name=hv2 type=geneve ip=192.168.0.2`
-
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp21], [0], [dnl
-encap : ${encap_rec}
-])
-
-AT_CHECK_UNQUOTED([ovn-sbctl --column encap list port_binding lp22], [0], [dnl
-encap : ${encap_rec}
-])
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Make sure there is no attempt to adding duplicated flows by ovn-controller
-AT_FAIL_IF([test -n "`grep duplicate hv1/ovn-controller.log`"])
-AT_FAIL_IF([test -n "`grep duplicate hv2/ovn-controller.log`"])
-AT_FAIL_IF([test -n "`grep duplicate hv3/ovn-controller.log`"])
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- echo hv${1%?}
-}
-
-# test_packet INPORT DST SRC ETHTYPE OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 11 for vif11.
-for i in 1 2; do
- for j in 1 2; do
- : > $i$j.expected
- done
-done
-test_packet() {
- local inport=$1 packet=$2$3$4; shift; shift; shift; shift
- hv=`vif_to_hv $inport`
- vif=vif$inport
- as $hv ovs-appctl netdev-dummy/receive $vif $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send packets between all pairs of source and destination ports:
-#
-# 1. Unicast packets are delivered to exactly one logical switch port
-# (except that packets destined to their input ports are dropped).
-
-for is in 1 2; do
- for js in 1 2; do
- s=$is$js
- bcast=
- unknown=
- bacl2=
- bacl3=
- for id in 1 2 3; do
- for jd in 1 2 3; do
- d=$id$jd
-
- if test $d != $s; then unicast=$d; else unicast=; fi
- test_packet $s f000000000$d f000000000$s $s$d $unicast #1
- done
- done
-
- done
-done
-
-# dump information and flows with counters
-ovn-sbctl dump-flows -- list multicast_group
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv2 dump ------"
-as hv2 ovs-vsctl show
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv3 dump ------"
-as hv3 ovs-vsctl show
-as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2; do
- for j in 1 2; do
- OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected])
- done
-done
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- trace 1 LS, 3 LSPs])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Create a logical switch and some logical ports.
-# Turn on port security on all lports except ls1.
-# Make ls1 a destination for unknown MACs.
-# Add some ACLs for Ethertypes 1234, 1235, 1236.
-ovn-nbctl ls-add lsw0
-ovn-sbctl chassis-add hv0 geneve 127.0.0.1
-for i in 1 2 3; do
- ovn-nbctl lsp-add lsw0 lp$i
-done
-ovn-nbctl --wait=sb sync
-for i in 1 2 3; do
- ovn-sbctl lsp-bind lp$i hv0
- if test $i = 1; then
- ovn-nbctl lsp-set-addresses lp$i "f0:00:00:00:00:0$i 192.168.0.$i" unknown
- else
- if test $i = 3; then
- ip_addrs="192.168.0.$i fe80::ea2a:eaff:fe28:$i/64 192.169.0.$i"
- else
- ip_addrs="192.168.0.$i"
- fi
- ovn-nbctl lsp-set-addresses lp$i "f0:00:00:00:00:0$i $ip_addrs"
- ovn-nbctl lsp-set-port-security lp$i f0:00:00:00:00:0$i
- fi
-done
-ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1234' drop
-ovn-nbctl acl-add lsw0 from-lport 1000 'eth.type == 0x1235 && inport == "lp1"' drop
-ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1236 && outport == "lp3"' drop
-ovn-nbctl create Address_Set name=set1 addresses=\"f0:00:00:00:00:01\",\"f0:00:00:00:00:02\"
-ovn-nbctl acl-add lsw0 to-lport 1000 'eth.type == 0x1237 && eth.src == $set1 && outport == "lp3"' drop
-
-ovn-nbctl --wait=sb sync
-on_exit 'kill `cat ovn-trace.pid`'
-ovn-trace --detach --pidfile --no-chdir
-
-# test_packet INPORT DST SRC [-vlan] [-eth TYPE] OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 11 for vif11.
-test_packet() {
- local inport=$1 eth_dst=$2 eth_src=$3; shift; shift; shift
- uflow="inport==\"lp$inport\" && eth.dst==$eth_dst && eth.src==$eth_src"
- while :; do
- case $1 in # (
- -vlan) uflow="$uflow && vlan.vid == 1234"; shift ;; # (
- -eth) uflow="$uflow && eth.type == 0x$2"; shift; shift ;; # (
- *) break ;;
- esac
- done
- for outport; do
- echo "output(\"lp$outport\");"
- done > expout
-
- AT_CAPTURE_FILE([trace])
- AT_CHECK([ovs-appctl -t ovn-trace trace --all lsw0 "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout])
-}
-
-# test_arp INPORT SHA SPA TPA [REPLY_HA]
-#
-# Causes a packet to be received on INPORT. The packet is an ARP
-# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
- local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
-
- local request="inport == \"lp$inport\"
- && eth.dst == ff:ff:ff:ff:ff:ff && eth.src == $sha
- && arp.op == 1 && arp.sha == $sha && arp.spa == $spa
- && arp.tha == ff:ff:ff:ff:ff:ff && arp.tpa == $tpa"
-
- if test -z "$reply_ha"; then
- reply=
- local i
- for i in 1 2 3; do
- if test $i != $inport; then
- reply="${reply}output(\"lp$i\");
-"
- fi
- done
- else
- reply="\
-eth.dst = $sha;
-eth.src = $reply_ha;
-arp.op = 2;
-arp.tha = $sha;
-arp.sha = $reply_ha;
-arp.tpa = $spa;
-arp.spa = $tpa;
-output(\"lp$inport\");
-"
- fi
-
- AT_CAPTURE_FILE([trace])
- AT_CHECK_UNQUOTED([ovs-appctl -t ovn-trace trace --all lsw0 "$request" | tee trace | sed '1,/Minimal trace/d'], [0], [$reply])
-}
-
-# Send packets between all pairs of source and destination ports:
-#
-# 1. Unicast packets are delivered to exactly one logical switch port
-# (except that packets destined to their input ports are dropped).
-#
-# 2. Broadcast and multicast are delivered to all logical switch ports
-# except the input port.
-#
-# 3. When port security is turned on, the switch drops packets from the wrong
-# MAC address.
-#
-# 4. The switch drops all packets with a VLAN tag.
-#
-# 5. The switch drops all packets with a multicast source address. (This only
-# affects behavior when port security is turned off, since otherwise port
-# security would drop the packet anyway.)
-#
-# 6. The switch delivers packets with an unknown destination to logical
-# switch ports with "unknown" among their MAC addresses (and port
-# security disabled).
-#
-# 7. The switch drops unicast packets that violate an ACL.
-#
-# 8. The switch drops multicast and broadcast packets that violate an ACL.
-#
-# 9. OVN generates responses to ARP requests for known IPs, except for
-# requests from a port for the port's own IP.
-#
-# 10. No response to ARP requests for unknown IPs.
-
-for s in 1 2 3; do
- bcast=
- unknown=
- bacl2=
- bacl3=
- for d in 1 2 3; do
- echo
- echo "lp$s -> lp$d"
- if test $d != $s; then unicast=$d; else unicast=; fi
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s $unicast #1
-
- if test $d != $s && test $s = 1; then
- impersonate=$d
- else
- impersonate=
- fi
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:55 $impersonate #3
-
- if test $d != $s && test $s != 1; then acl2=$d; else acl2=; fi
- if test $d != $s && test $d != 3; then acl3=$d; else acl3=; fi
- if test $d = $s || ( (test $s = 1 || test $s = 2) && test $d = 3); then
- # Source of 1 or 2 and dest of 3 should be dropped
- # due to the 4th ACL that uses address_set(set1).
- acl4=
- else
- acl4=$d
- fi
-
- #7, acl1 to acl4:
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1234
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1235 $acl2
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1236 $acl3
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:0$s -eth 1237 $acl4
-
- test_packet $s f0:00:00:00:00:0$d f0:00:00:00:00:55 -vlan #4
- test_packet $s f0:00:00:00:00:0$d 01:00:00:00:00:0$s #5
-
- if test $d != $s && test $d = 1; then
- unknown="$unknown $d"
- fi
- bcast="$bcast $unicast"
- bacl2="$bacl2 $acl2"
- bacl3="$bacl3 $acl3"
-
- sip=192.168.0.$s
- tip=192.168.0.$d
- tip_unknown=11.11.11.11
- if test $d != $s; then reply_ha=f0:00:00:00:00:0$d; else reply_ha=; fi
- test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha #9
- test_arp $s f0:00:00:00:00:0$s $sip $tip_unknown #10
-
- if test $d = 3; then
- # lp3 has an additional ip 192.169.0.[123]3.
- tip=192.169.0.$d
- test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha #9
- fi
- done
-
- # Broadcast and multicast.
- test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s $bcast #2
- test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s $bcast #2
- if test $s = 1; then
- bcast_impersonate=$bcast
- else
- bcast_impersonate=
- fi
- test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:44 $bcast_impersonate #3
-
- test_packet $s f0:00:00:00:ff:ff f0:00:00:00:00:0$s $unknown #6
-
- #8, acl1 to acl3:
- test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s -eth 1234
- test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s -eth 1235 $bacl2
- test_packet $s ff:ff:ff:ff:ff:ff f0:00:00:00:00:0$s -eth 1236 $bacl3
-
- #8, acl1 to acl3:
- test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s -eth 1234
- test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s -eth 1235 $bacl2
- test_packet $s 01:00:00:00:00:00 f0:00:00:00:00:0$s -eth 1236 $bacl3
-done
-
-AT_CLEANUP
-
-# 2 hypervisors, 4 logical ports per HV
-# 2 locally attached networks (one flat, one vlan tagged over same device)
-# 2 ports per HV on each network
-AT_SETUP([ovn -- 2 HVs, 4 lports/HV, localnet ports])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# In this test cases we create 3 switches, all connected to same
-# physical network (through br-phys on each HV). Each switch has
-# VIF ports across 2 HVs. Each HV has 5 VIF ports. The first digit
-# of VIF port name indicates the hypervisor it is bound to, e.g.
-# lp23 means VIF 3 on hv2.
-#
-# Each switch's VLAN tag and their logical switch ports are:
-# - ls1:
-# - untagged
-# - ports: lp11, lp12, lp21, lp22
-#
-# - ls2:
-# - tagged with VLAN 101
-# - ports: lp13, lp14, lp23, lp24
-# - ls3:
-# - untagged
-# - ports: lp15, lp25
-#
-# Note: a localnet port is created for each switch to connect to
-# physical network.
-
-for i in 1 2 3; do
- ls_name=ls$i
- ovn-nbctl ls-add $ls_name
- ln_port_name=ln$i
- if test $i -eq 2; then
- ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
- else
- ovn-nbctl lsp-add $ls_name $ln_port_name
- fi
- ovn-nbctl lsp-set-addresses $ln_port_name unknown
- ovn-nbctl lsp-set-type $ln_port_name localnet
- ovn-nbctl lsp-set-options $ln_port_name network_name=phys
-done
-
-# lsp_to_ls LSP
-#
-# Prints the name of the logical switch that contains LSP.
-lsp_to_ls () {
- case $1 in dnl (
- lp?[[12]]) echo ls1 ;; dnl (
- lp?[[34]]) echo ls2 ;; dnl (
- lp?5) echo ls3 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-net_add n1
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-
- for j in 1 2 3 4 5; do
- ovs-vsctl add-port br-int vif$i$j -- \
- set Interface vif$i$j external-ids:iface-id=lp$i$j \
- options:tx_pcap=hv$i/vif$i$j-tx.pcap \
- options:rxq_pcap=hv$i/vif$i$j-rx.pcap \
- ofport-request=$i$j
-
- lsp_name=lp$i$j
- ls_name=$(lsp_to_ls $lsp_name)
-
- ovn-nbctl lsp-add $ls_name $lsp_name
- ovn-nbctl lsp-set-addresses $lsp_name f0:00:00:00:00:$i$j
- ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$j
-
- OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
- done
-done
-ovn-nbctl --wait=sb sync
-ovn-sbctl dump-flows
-
-OVN_POPULATE_ARP
-
-# XXX This is now the 3rd copy of these functions in this file ...
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- echo hv${1%?}
-}
-#
-# test_packet INPORT DST SRC ETHTYPE EOUT LOUT
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). INPORT is specified as
-# logical switch port numbers, e.g. 11 for vif11.
-#
-# EOUT is the end-to-end output port, that is, where the packet will end up
-# after possibly bouncing through one or more localnet ports. LOUT is the
-# logical output port, which might be a localnet port, as seen by ovn-trace
-# (which doesn't know what localnet ports are connected to and therefore can't
-# figure out the end-to-end answer).
-for i in 1 2; do
- for j in 1 2 3 4 5; do
- : > $i$j.expected
- done
-done
-test_packet() {
- local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6
- echo "$@"
-
- # First try tracing the packet.
- uflow="inport==\"lp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth"
- if test $lout != drop; then
- echo "output(\"$lout\");"
- fi > expout
- AT_CAPTURE_FILE([trace])
- AT_CHECK([ovn-trace --all $(lsp_to_ls lp$inport) "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout])
-
- # Then actually send a packet, for an end-to-end test.
- local packet=$(echo $dst$src | sed 's/://g')${eth}
- hv=`vif_to_hv $inport`
- vif=vif$inport
- as $hv ovs-appctl netdev-dummy/receive $vif $packet
- if test $eout != drop; then
- echo $packet >> ${eout#lp}.expected
- fi
-}
-
-# lp11 and lp21 are on the same network (phys, untagged)
-# and on different hypervisors
-test_packet 11 f0:00:00:00:00:21 f0:00:00:00:00:11 1121 lp21 lp21
-test_packet 21 f0:00:00:00:00:11 f0:00:00:00:00:21 2111 lp11 lp11
-
-# lp11 and lp12 are on the same network (phys, untagged)
-# and on the same hypervisor
-test_packet 11 f0:00:00:00:00:12 f0:00:00:00:00:11 1112 lp12 lp12
-test_packet 12 f0:00:00:00:00:11 f0:00:00:00:00:12 1211 lp11 lp11
-
-# lp13 and lp23 are on the same network (phys, VLAN 101)
-# and on different hypervisors
-test_packet 13 f0:00:00:00:00:23 f0:00:00:00:00:13 1323 lp23 lp23
-test_packet 23 f0:00:00:00:00:13 f0:00:00:00:00:23 2313 lp13 lp13
-
-# lp13 and lp14 are on the same network (phys, VLAN 101)
-# and on the same hypervisor
-test_packet 13 f0:00:00:00:00:14 f0:00:00:00:00:13 1314 lp14 lp14
-test_packet 14 f0:00:00:00:00:13 f0:00:00:00:00:14 1413 lp13 lp13
-
-# lp11 and lp15 are on the same network (phys, untagged),
-# same hypervisor, and on different switches
-test_packet 11 f0:00:00:00:00:15 f0:00:00:00:00:11 1115 lp15 ln1
-test_packet 15 f0:00:00:00:00:11 f0:00:00:00:00:15 1511 lp11 ln3
-
-# lp11 and lp25 are on the same network (phys, untagged),
-# different hypervisors, and on different switches
-test_packet 11 f0:00:00:00:00:25 f0:00:00:00:00:11 1125 lp25 ln1
-test_packet 25 f0:00:00:00:00:11 f0:00:00:00:00:25 2511 lp11 ln3
-
-# Ports that should not be able to communicate
-test_packet 11 f0:00:00:00:00:13 f0:00:00:00:00:11 1113 drop ln1
-test_packet 11 f0:00:00:00:00:23 f0:00:00:00:00:11 1123 drop ln1
-test_packet 21 f0:00:00:00:00:13 f0:00:00:00:00:21 2113 drop ln1
-test_packet 21 f0:00:00:00:00:23 f0:00:00:00:00:21 2123 drop ln1
-test_packet 13 f0:00:00:00:00:11 f0:00:00:00:00:13 1311 drop ln2
-test_packet 13 f0:00:00:00:00:21 f0:00:00:00:00:13 1321 drop ln2
-test_packet 23 f0:00:00:00:00:11 f0:00:00:00:00:23 2311 drop ln2
-test_packet 23 f0:00:00:00:00:21 f0:00:00:00:00:23 2321 drop ln2
-
-# Dump a bunch of info helpful for debugging if there's a failure.
-
-echo "------ OVN dump ------"
-ovn-nbctl show
-ovn-sbctl show
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv2 dump ------"
-as hv2 ovs-vsctl show
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2; do
- for j in 1 2 3 4 5; do
- OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected])
- done
-done
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- vtep: 3 HVs, 1 VIFs/HV, 1 GW, 1 LS])
-AT_KEYWORDS([vtep])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Configure the Northbound database
-ovn-nbctl ls-add lsw0
-
-ovn-nbctl lsp-add lsw0 lp1
-ovn-nbctl lsp-set-addresses lp1 f0:00:00:00:00:01
-
-ovn-nbctl lsp-add lsw0 lp2
-ovn-nbctl lsp-set-addresses lp2 f0:00:00:00:00:02
-
-ovn-nbctl lsp-add lsw0 lp-vtep
-ovn-nbctl lsp-set-type lp-vtep vtep
-ovn-nbctl lsp-set-options lp-vtep vtep-physical-switch=br-vtep vtep-logical-switch=lsw0
-ovn-nbctl lsp-set-addresses lp-vtep unknown
-
-# lpr, lr and lrp1 are used for the ARP request handling test only.
-ovn-nbctl lsp-add lsw0 lpr
-ovn-nbctl lr-add lr
-ovn-nbctl lrp-add lr lrp1 f0:00:00:00:00:f1 192.168.1.1/24
-ovn-nbctl set Logical_Switch_Port lpr type=router \
- options:router-port=lrp1 \
- addresses='"f0:00:00:00:00:f1 192.168.1.1"'
-
-
-net_add n1 # Network to connect hv1, hv2, and vtep
-net_add n2 # Network to connect vtep and hv3
-
-# Create hypervisor hv1 connected to n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1
-
-# Create hypervisor hv2 connected to n1
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv2/vif2-tx.pcap options:rxq_pcap=hv2/vif2-rx.pcap ofport-request=1
-
-
-# Start the vtep emulator with a leg in both networks
-sim_add vtep
-as vtep
-
-ovsdb-tool create "$ovs_base"/vtep/vtep.db "$abs_top_srcdir"/vtep/vtep.ovsschema || return 1
-ovs-appctl -t ovsdb-server ovsdb-server/add-db "$ovs_base"/vtep/vtep.db
-
-ovs-vsctl add-br br-phys
-net_attach n1 br-phys
-
-mac=`ovs-vsctl get Interface br-phys mac_in_use | sed s/\"//g`
-arp_table="$arp_table $sandbox,br-phys,192.168.0.3,$mac"
-ovs-appctl netdev-dummy/ip4addr br-phys 192.168.0.3/24 >/dev/null || return 1
-ovs-appctl ovs/route/add 192.168.0.3/24 br-phys >/dev/null || return 1
-
-ovs-vsctl add-br br-vtep
-net_attach n2 br-vtep
-
-vtep-ctl add-ps br-vtep
-vtep-ctl set Physical_Switch br-vtep tunnel_ips=192.168.0.3
-vtep-ctl add-ls lsw0
-
-start_daemon ovs-vtep br-vtep
-start_daemon ovn-controller-vtep --vtep-db=unix:"$ovs_base"/vtep/db.sock --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
-
-OVS_WAIT_UNTIL([vtep-ctl bind-ls br-vtep br-vtep_n2 0 lsw0])
-
-OVS_WAIT_UNTIL([test -n "`as vtep vtep-ctl get-replication-mode lsw0 |
- grep -- source`"])
-# It takes more time for the update to be processed by ovs-vtep.
-sleep 1
-
-# Add hv3 on the other side of the vtep
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-net_attach n2 br-phys
-
-ovs-vsctl add-port br-phys vif3 -- set Interface vif3 options:tx_pcap=hv3/vif3-tx.pcap options:rxq_pcap=hv3/vif3-rx.pcap ofport-request=1
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# test_packet INPORT DST SRC ETHTYPE OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 1 for vif1.
-for i in 1 2 3; do
- : > $i.expected
-done
-test_packet() {
- local inport=$1 packet=$2$3$4; shift; shift; shift; shift
- #hv=hv`echo $inport | sed 's/^\(.\).*/\1/'`
- hv=hv$inport
- vif=vif$inport
- as $hv ovs-appctl netdev-dummy/receive $vif $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-# Send packets between all pairs of source and destination ports:
-#
-# 1. Unicast packets are delivered to exactly one logical switch port
-# (except that packets destined to their input ports are dropped).
-#
-# 2. Broadcast and multicast are delivered to all logical switch ports
-# except the input port.
-#
-# 3. The switch delivers packets with an unknown destination to logical
-# switch ports with "unknown" among their MAC addresses (and port
-# security disabled).
-for s in 1 2 3; do
- bcast=
- unknown=
- for d in 1 2 3; do
- if test $d != $s; then unicast=$d; else unicast=; fi
- test_packet $s f0000000000$d f0000000000$s 00$s$d $unicast #1
-
- # The vtep (vif3) is the only one configured for "unknown"
- if test $d != $s && test $d = 3; then
- unknown="$unknown $d"
- fi
- bcast="$bcast $unicast"
- done
-
- # Broadcast and multicast.
- test_packet $s ffffffffffff f0000000000$s 0${s}ff $bcast #2
- test_packet $s 010000000000 f0000000000$s 0${s}ff $bcast #2
-
- test_packet $s f0000000ffff f0000000000$s 0${s}66 $unknown #3
-done
-
-# ARP request should not be responded to by logical switch router
-# type arp responder on HV1 and HV2 and should reach directly to
-# vif1 and vif2
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-sha=f00000000003
-spa=`ip_to_hex 192 168 1 2`
-tpa=`ip_to_hex 192 168 1 1`
-request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
-as hv3 ovs-appctl netdev-dummy/receive vif3 $request
-echo $request >> 1.expected
-echo $request >> 2.expected
-
-# dump information with counters
-echo "------ OVN dump ------"
-ovn-nbctl show
-ovn-sbctl show
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 show br-int
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv2 dump ------"
-as hv2 ovs-vsctl show
-as hv2 ovs-ofctl -O OpenFlow13 show br-int
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv3 dump ------"
-as hv3 ovs-vsctl show
-# note: hv3 has no logical port bind, thus it should not have br-int
-AT_CHECK([as hv3 ovs-ofctl -O OpenFlow13 show br-int], [1], [],
-[ovs-ofctl: br-int is not a bridge or a socket
-])
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- OVN_CHECK_PACKETS([hv$i/vif$i-tx.pcap], [$i.expected])
-done
-
-# Gracefully terminate daemons
-OVN_CLEANUP([hv1],[hv2],[vtep])
-OVN_CLEANUP_VSWITCH([hv3])
-
-AT_CLEANUP
-
-# Similar test to "hardware GW"
-AT_SETUP([ovn -- 3 HVs, 1 VIFs/HV, 1 software GW, 1 LS])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Configure the Northbound database
-ovn-nbctl ls-add lsw0
-
-ovn-nbctl lsp-add lsw0 lp1
-ovn-nbctl lsp-set-addresses lp1 f0:00:00:00:00:01
-
-ovn-nbctl lsp-add lsw0 lp2
-ovn-nbctl lsp-set-addresses lp2 f0:00:00:00:00:02
-
-ovn-nbctl lsp-add lsw0 lp-gw
-ovn-nbctl lsp-set-type lp-gw l2gateway
-ovn-nbctl lsp-set-options lp-gw network_name=physnet1 l2gateway-chassis=hv_gw
-ovn-nbctl lsp-set-addresses lp-gw unknown
-
-net_add n1 # Network to connect hv1, hv2, and gw
-net_add n2 # Network to connect gw and hv3
-
-# Create hypervisor hv1 connected to n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1
-
-# Create hypervisor hv2 connected to n1
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv2/vif2-tx.pcap options:rxq_pcap=hv2/vif2-rx.pcap ofport-request=1
-
-# Create hypervisor hv_gw connected to n1 and n2
-# connect br-phys bridge to n1; connect hv-gw bridge to n2
-sim_add hv_gw
-as hv_gw
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl add-br br-phys2
-net_attach n2 br-phys2
-ovs-vsctl set open . external_ids:ovn-bridge-mappings="physnet1:br-phys2"
-
-# Add hv3 on the other side of the GW
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-net_attach n2 br-phys
-ovs-vsctl add-port br-phys vif3 -- set Interface vif3 options:tx_pcap=hv3/vif3-tx.pcap options:rxq_pcap=hv3/vif3-rx.pcap ofport-request=1
-
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# test_packet INPORT DST SRC ETHTYPE OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as lport numbers, e.g. 1 for vif1.
-for i in 1 2 3; do
- : > $i.expected
-done
-test_packet() {
- local inport=$1 packet=$2$3$4; shift; shift; shift; shift
- #hv=hv`echo $inport | sed 's/^\(.\).*/\1/'`
- hv=hv$inport
- vif=vif$inport
- as $hv ovs-appctl netdev-dummy/receive $vif $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-# Send packets between all pairs of source and destination ports:
-#
-# 1. Unicast packets are delivered to exactly one lport (except that packets
-# destined to their input ports are dropped).
-#
-# 2. Broadcast and multicast are delivered to all lports except the input port.
-#
-# 3. The lswitch delivers packets with an unknown destination to lports with
-# "unknown" among their MAC addresses (and port security disabled).
-for s in 1 2 3 ; do
- bcast=
- unknown=
- for d in 1 2 3 ; do
- if test $d != $s; then unicast=$d; else unicast=; fi
- test_packet $s f0000000000$d f0000000000$s 00$s$d $unicast #1
-
- # The vtep (vif3) is the only one configured for "unknown"
- if test $d != $s && test $d = 3; then
- unknown="$unknown $d"
- fi
- bcast="$bcast $unicast"
- done
-
- test_packet $s ffffffffffff f0000000000$s 0${s}ff $bcast #2
- test_packet $s 010000000000 f0000000000$s 0${s}ff $bcast #3
- test_packet $s f0000000ffff f0000000000$s 0${s}66 $unknown #4
-done
-
-echo "------ ovn-nbctl show ------"
-ovn-nbctl show
-echo "------ ovn-sbctl show ------"
-ovn-sbctl show
-
-echo "------ hv1 ------"
-as hv1 ovs-vsctl show
-echo "------ hv1 br-int ------"
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-echo "------ hv1 br-phys ------"
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-phys
-
-echo "------ hv2 ------"
-as hv2 ovs-vsctl show
-echo "------ hv2 br-int ------"
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-echo "------ hv2 br-phys ------"
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-phys
-
-echo "------ hv_gw ------"
-as hv_gw ovs-vsctl show
-echo "------ hv_gw br-phys ------"
-as hv_gw ovs-ofctl -O OpenFlow13 dump-flows br-phys
-echo "------ hv_gw br-phys2 ------"
-as hv_gw ovs-ofctl -O OpenFlow13 dump-flows br-phys2
-
-echo "------ hv3 ------"
-as hv3 ovs-vsctl show
-echo "------ hv3 br-phys ------"
-as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-phys
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- OVN_CHECK_PACKETS([hv$i/vif$i-tx.pcap], [$i.expected])
-done
-AT_CLEANUP
-
-# 3 hypervisors, 3 logical switches with 3 logical ports each, 1 logical router
-AT_SETUP([ovn -- 3 HVs, 3 LS, 3 lports/LS, 1 LR])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-#
-# Three logical switches ls1, ls2, ls3.
-# One logical router lr0 connected to ls[123],
-# with nine subnets, three per logical switch:
-#
-# lrp11 on ls1 for subnet 192.168.11.0/24
-# lrp12 on ls1 for subnet 192.168.12.0/24
-# lrp13 on ls1 for subnet 192.168.13.0/24
-# ...
-# lrp33 on ls3 for subnet 192.168.33.0/24
-#
-# 27 VIFs, 9 per LS, 3 per subnet: lp[123][123][123], where the first two
-# digits are the subnet and the last digit distinguishes the VIF.
-for i in 1 2 3; do
- ovn-nbctl ls-add ls$i
- for j in 1 2 3; do
- for k in 1 2 3; do
- # Add "unknown" to MAC addresses for lp?11, so packets for
- # MAC-IP bindings discovered via ARP later have somewhere to go.
- if test $j$k = 11; then unknown=unknown; else unknown=; fi
-
- ovn-nbctl \
- -- lsp-add ls$i lp$i$j$k \
- -- lsp-set-addresses lp$i$j$k \
- "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k" $unknown
- done
- done
-done
-
-ovn-nbctl lr-add lr0
-for i in 1 2 3; do
- for j in 1 2 3; do
- ovn-nbctl lrp-add lr0 lrp$i$j 00:00:00:00:ff:$i$j 192.168.$i$j.254/24
- ovn-nbctl \
- -- lsp-add ls$i lrp$i$j-attachment \
- -- set Logical_Switch_Port lrp$i$j-attachment type=router \
- options:router-port=lrp$i$j \
- addresses='"00:00:00:00:ff:'$i$j'"'
- done
-done
-
-ovn-nbctl set Logical_Switch_Port lrp33-attachment \
- addresses='"00:00:00:00:ff:33 192.168.33.254"'
-
-# Physical network:
-#
-# Three hypervisors hv[123].
-# lp?1[123] spread across hv[123]: lp?11 on hv1, lp?12 on hv2, lp?13 on hv3.
-# lp?2[123] spread across hv[23]: lp?21 and lp?22 on hv2, lp?23 on hv3.
-# lp?3[123] all on hv3.
-
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- case $1 in dnl (
- ?11) echo 1 ;; dnl (
- ?12 | ?21 | ?22) echo 2 ;; dnl (
- ?13 | ?23 | ?3?) echo 3 ;;
- esac
-}
-
-# Given the name of a logical port, prints the name of its logical router
-# port, e.g. "vif_to_lrp 123" yields 12.
-vif_to_lrp() {
- echo ${1%?}
-}
-
-# Given the name of a logical port, prints the name of its logical
-# switch, e.g. "vif_to_ls 123" yields 1.
-vif_to_ls() {
- echo ${1%??}
-}
-
-net_add n1
-for i in 1 2 3; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-done
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- hv=`vif_to_hv $i$j$k`
- as hv$hv ovs-vsctl \
- -- add-port br-int vif$i$j$k \
- -- set Interface vif$i$j$k \
- external-ids:iface-id=lp$i$j$k \
- options:tx_pcap=hv$hv/vif$i$j$k-tx.pcap \
- options:rxq_pcap=hv$hv/vif$i$j$k-rx.pcap \
- ofport-request=$i$j$k
- done
- done
-done
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 123 for vif123.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- : > $i$j$k.expected
- done
- done
-done
-test_ip() {
- # This packet has bad checksums but logical L3 routing doesn't check.
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
- shift; shift; shift; shift; shift
- hv=hv`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
- in_ls=`vif_to_ls $inport`
- in_lrp=`vif_to_lrp $inport`
- for outport; do
- out_ls=`vif_to_ls $outport`
- if test $in_ls = $out_ls; then
- # Ports on the same logical switch receive exactly the same packet.
- echo $packet
- else
- # Routing decrements TTL and updates source and dest MAC
- # (and checksum).
- out_lrp=`vif_to_lrp $outport`
- echo f00000000${outport}00000000ff${out_lrp}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
- fi >> $outport.expected
- done
-}
-
-# test_arp INPORT SHA SPA TPA [REPLY_HA]
-#
-# Causes a packet to be received on INPORT. The packet is an ARP
-# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
- local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
- local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
- hv=hv`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $request
- as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
-
- # Expect to receive the broadcast ARP on the other logical switch ports if
- # IP address is not configured to the switch patch port.
- local i=`vif_to_ls $inport`
- local j k
- for j in 1 2 3; do
- for k in 1 2 3; do
- # 192.168.33.254 is configured to the switch patch port for lrp33,
- # so no ARP flooding expected for it.
- if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then
- echo $request >> $i$j$k.expected
- fi
- done
- done
-
- # Expect to receive the reply, if any.
- if test X$reply_ha != X; then
- lrp=`vif_to_lrp $inport`
- local reply=${sha}00000000ff${lrp}08060001080006040002${reply_ha}${tpa}${sha}${spa}
- echo $reply >> $inport.expected
- fi
-}
-
-as hv1 ovs-vsctl --columns=name,ofport list interface
-as hv1 ovn-sbctl list port_binding
-as hv1 ovn-sbctl list datapath_binding
-as hv1 ovn-sbctl dump-flows
-as hv1 ovs-ofctl dump-flows br-int
-
-# Send IP packets between all pairs of source and destination ports:
-#
-# 1. Unicast IP packets are delivered to exactly one logical switch port
-# (except that packets destined to their input ports are dropped).
-#
-# 2. Broadcast IP packets are delivered to all logical switch ports
-# except the input port.
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-for is in 1 2 3; do
- for js in 1 2 3; do
- for ks in 1 2 3; do
- bcast=
- s=$is$js$ks
- smac=f00000000$s
- sip=`ip_to_hex 192 168 $is$js $ks`
- for id in 1 2 3; do
- for jd in 1 2 3; do
- for kd in 1 2 3; do
- d=$id$jd$kd
- dip=`ip_to_hex 192 168 $id$jd $kd`
- if test $is = $id; then dmac=f00000000$d; else dmac=00000000ff$is$js; fi
- if test $d != $s; then unicast=$d; else unicast=; fi
-
- test_ip $s $smac $dmac $sip $dip $unicast #1
-
- if test $id = $is && test $d != $s; then bcast="$bcast $d"; fi
- done
- done
- done
- test_ip $s $smac ffffffffffff $sip ffffffff $bcast #2
- done
- done
-done
-
-: > mac_bindings.expected
-
-# 3. Send an IP packet from every logical port to every other subnet,
-# to an IP address that does not have a static IP-MAC binding.
-# This should generate a broadcast ARP request for the destination
-# IP address in the destination subnet.
-# Moreover generate an ARP reply for each of the IP addresses ARPed
-for is in 1 2 3; do
- for js in 1 2 3; do
- for ks in 1 2 3; do
- s=$is$js$ks
- smac=f00000000$s
- sip=`ip_to_hex 192 168 $is$js $ks`
- for id in 1 2 3; do
- for jd in 1 2 3; do
- if test $is$js = $id$jd; then
- continue
- fi
-
- # Send the packet.
- dmac=00000000ff$is$js
- # Calculate a 4th octet for the destination that is
- # unique per $s, avoids the .1 .2 .3 and .254 IP addresses
- # that have static MAC bindings, and fits in the range
- # 0-255.
- o4=`expr $is '*' 9 + $js '*' 3 + $ks + 10`
- dip=`ip_to_hex 192 168 $id$jd $o4`
- test_ip $s $smac $dmac $sip $dip
-
- # Every LP on the destination subnet's lswitch should
- # receive the ARP request.
- lrmac=00000000ff$id$jd
- lrip=`ip_to_hex 192 168 $id$jd 254`
- arp=ffffffffffff${lrmac}08060001080006040001${lrmac}${lrip}000000000000${dip}
- for jd2 in 1 2 3; do
- for kd in 1 2 3; do
- echo $arp >> $id$jd2$kd.expected
- done
- done
-
- hmac=8000000000$o4
- rmac=00000000ff$id$jd
- echo ${hmac}${rmac}08004500001c00000000"3f1101"00${sip}${dip}0035111100080000 >> ${id}11.expected
-
- host_mac=8000000000$o4
- lrmac=00000000ff$id$jd
-
- arp_reply=${lrmac}${host_mac}08060001080006040002${host_mac}${dip}${lrmac}${lrip}
-
- hv=hv`vif_to_hv ${id}${jd}1`
- as $hv ovs-appctl netdev-dummy/receive vif${id}${jd}1 $arp_reply
-
- host_ip_pretty=192.168.$id$jd.$o4
- host_mac_pretty=80:00:00:00:00:$o4
- echo lrp$id$jd,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
- done
- done
- done
- done
-done
-
-# Test router replies to ARP requests from all source ports:
-#
-# 4. Router replies to query for its MAC address from port's own IP address.
-#
-# 5. Router replies to query for its MAC address from any random IP address
-# in its subnet.
-#
-# 6. No reply to query for IP address other than router IP.
-#
-# 7. No reply to query from another subnet.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- smac=f00000000$i$j$k # Source MAC
- sip=`ip_to_hex 192 168 $i$j $k` # Source IP
- rip=`ip_to_hex 192 168 $i$j 254` # Router IP
- rmac=00000000ff$i$j # Router MAC
- otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet
- externalip=`ip_to_hex 1 2 3 4` # Some other IP not in subnet
-
- test_arp $i$j$k $smac $sip $rip $rmac #4
- test_arp $i$j$k $smac $otherip $rip $rmac #5
- test_arp $i$j$k $smac $sip $otherip #6
-
- # When rip is 192.168.33.254, ARP request from externalip won't be
- # filtered, because 192.168.33.254 is configured to switch peer port
- # for lrp33.
- lrp33_rsp=
- if test $i = 3 && test $j = 3; then
- lrp33_rsp=$rmac
- fi
- test_arp $i$j$k $smac $externalip $rip $lrp33_rsp #7
-
- # MAC binding should be learned from ARP request.
- host_mac_pretty=f0:00:00:00:0$i:$j$k
-
- host_ip_pretty=192.168.$i$j.$k
- echo lrp$i$j,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
-
- # mac_binding is learned and overwritten so only the last one remains.
- if test $k = 3; then
- # lrp33 will not learn from ARP request, because 192.168.33.254 is
- # configured to switch peer port for lrp33.
- if test $i != 3 || test $j != 3; then
- host_ip_pretty=192.168.$i$j.55
- echo lrp$i$j,$host_ip_pretty,$host_mac_pretty >> mac_bindings.expected
- fi
- fi
-
- done
- done
-done
-
-
-# Allow some time for packet forwarding.
-# XXX This can be improved.
-sleep 1
-
-# 8. Send an IP packet from every logical port to every other subnet. These
-# are the same packets already sent as #3, but now the destinations' IP-MAC
-# bindings have been discovered via ARP, so instead of provoking an ARP
-# request, these packets now get routed to their destinations (which don't
-# have static MAC bindings, so they go to the port we've designated as
-# accepting "unknown" MACs.)
-for is in 1 2 3; do
- for js in 1 2 3; do
- for ks in 1 2 3; do
- s=$is$js$ks
- smac=f00000000$s
- sip=`ip_to_hex 192 168 $is$js $ks`
- for id in 1 2 3; do
- for jd in 1 2 3; do
- if test $is$js = $id$jd; then
- continue
- fi
-
- # Send the packet.
- dmac=00000000ff$is$js
- # Calculate a 4th octet for the destination that is
- # unique per $s, avoids the .1 .2 .3 and .254 IP addresses
- # that have static MAC bindings, and fits in the range
- # 0-255.
- o4=`expr $is '*' 9 + $js '*' 3 + $ks + 10`
- dip=`ip_to_hex 192 168 $id$jd $o4`
- test_ip $s $smac $dmac $sip $dip
-
- # Expect the packet egress.
- host_mac=8000000000$o4
- outport=${id}11
- out_lrp=$id$jd
- echo ${host_mac}00000000ff${out_lrp}08004500001c00000000"3f1101"00${sip}${dip}0035111100080000 >> $outport.expected
- done
- done
- done
- done
-done
-
-ovn-sbctl -f csv -d bare --no-heading \
- -- --columns=logical_port,ip,mac list mac_binding > mac_bindings
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- OVN_CHECK_PACKETS([hv`vif_to_hv $i$j$k`/vif$i$j$k-tx.pcap],
- [$i$j$k.expected])
- done
- done
-done
-
-# Check the MAC bindings against those expected.
-AT_CHECK_UNQUOTED([sort < mac_bindings], [0], [`sort < mac_bindings.expected`
-])
-
-# Gracefully terminate daemons
-OVN_CLEANUP([hv1], [hv2], [hv3])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- IP relocation using GARP request])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-#
-# Two logical switches ls1, ls2.
-# One logical router lr0 connected to ls[12],
-# with 2 subnets, 1 per logical switch:
-#
-# lrp1 on ls1 for subnet 192.168.1.1/24
-# lrp2 on ls2 for subnet 192.168.2.1/24
-#
-# 4 VIFs, 2 per LS lp[12][12], first digit being LS.
-# VIFs' fixed IP addresses are 192.168.[12].1[12].
-#
-# There is a secondary IP 192.168.1.100 that is unknown in NB and learned
-# through ARP only, and it can move between lp11 and lp12.
-#
-ovn-nbctl lr-add lr0
-for i in 1 2 ; do
- ovn-nbctl ls-add ls$i
- ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.1/24
- ovn-nbctl \
- -- lsp-add ls$i lrp$i-attachment \
- -- set Logical_Switch_Port lrp$i-attachment type=router \
- options:router-port=lrp$i \
- addresses=router
- for j in 1 2; do
- ovn-nbctl \
- -- lsp-add ls$i lp$i$j \
- -- lsp-set-addresses lp$i$j \
- "f0:00:00:00:00:$i$j 192.168.$i.1$j"
- done
-done
-
-# Physical network:
-# 2 hypervisors hv[12], lp?1 on hv1, lp?2 on hv2.
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located, e.g. "vif_to_hv 12" yields 2.
-vif_to_hv() {
- echo ${1#?}
-}
-
-# Given the name of a logical port, prints the name of its logical router
-# port, e.g. "vif_to_lrp 12" yields 1.
-vif_to_lrp() {
- echo ${1%?}
-}
-
-# Given the name of a logical port, prints the name of its logical
-# switch, e.g. "vif_to_ls 12" yields 1.
-vif_to_ls() {
- echo ${1%?}
-}
-
-net_add n1
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-done
-for i in 1 2; do
- for j in 1 2; do
- hv=`vif_to_hv $i$j`
- as hv$hv ovs-vsctl \
- -- add-port br-int vif$i$j \
- -- set Interface vif$i$j \
- external-ids:iface-id=lp$i$j \
- options:tx_pcap=hv$hv/vif$i$j-tx.pcap \
- options:rxq_pcap=hv$hv/vif$i$j-rx.pcap \
- ofport-request=$i$j
- done
-done
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 12 for vif12.
-for i in 1 2; do
- for j in 1 2; do
- : > $i$j.expected
- done
-done
-test_ip() {
- # This packet has bad checksums but logical L3 routing doesn't check.
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
- shift; shift; shift; shift; shift
- hv=hv`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
- in_ls=`vif_to_ls $inport`
- in_lrp=`vif_to_lrp $inport`
- for outport; do
- out_ls=`vif_to_ls $outport`
- if test $in_ls = $out_ls; then
- # Ports on the same logical switch receive exactly the same packet.
- echo $packet
- else
- # Routing decrements TTL and updates source and dest MAC
- # (and checksum).
- out_lrp=`vif_to_lrp $outport`
- echo f000000000${outport}00000000ff0${out_lrp}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
- fi >> $outport.expected
- done
-}
-
-# test_arp INPORT SHA SPA TPA [REPLY_HA]
-#
-# Causes a packet to be received on INPORT. The packet is an ARP
-# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
- local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
- local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
- hv=hv`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $request
-
- # Expect to receive the broadcast ARP on the other logical switch ports if
- # IP address is not configured to the switch patch port.
- local i=`vif_to_ls $inport`
- local j
- for j in 1 2; do
- if test $i$j != $inport; then
- echo $request >> $i$j$k.expected
- fi
- done
-
- # Expect to receive the reply, if any.
- if test X$reply_ha != X; then
- lrp=`vif_to_lrp $inport`
- local reply=${sha}00000000ff0${lrp}08060001080006040002${reply_ha}${tpa}${sha}${spa}
- echo $reply >> $inport.expected
- fi
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# lp11 send GARP request to announce ownership of 192.168.1.100.
-
-sha=f00000000011
-spa=`ip_to_hex 192 168 1 100`
-tpa=$spa
-test_arp 11 $sha $spa $tpa
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="192.168.1.100" | wc -l` -gt 0])
-ovn-nbctl --wait=hv sync
-
-# Send an IP packet from lp21 to 192.168.1.100, which should go to lp11.
-
-smac=f00000000021
-dmac=00000000ff02
-sip=`ip_to_hex 192 168 2 11`
-dip=`ip_to_hex 192 168 1 100`
-test_ip 21 $smac $dmac $sip $dip 11
-
-# lp12 send GARP request to announce ownership of 192.168.1.100.
-
-sha=f00000000012
-test_arp 12 $sha $spa $tpa
-OVS_WAIT_UNTIL([ovn-sbctl find mac_binding ip="192.168.1.100" | grep f0:00:00:00:00:12])
-ovn-nbctl --wait=hv sync
-# give to the hv the time to send queued ip packets
-sleep 1
-
-# Send an IP packet from lp21 to 192.168.1.100, which should go to lp12.
-
-test_ip 21 $smac $dmac $sip $dip 12
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2; do
- for j in 1 2; do
- OVN_CHECK_PACKETS([hv`vif_to_hv $i$j`/vif$i$j-tx.pcap],
- [$i$j.expected])
- done
-done
-
-# Gracefully terminate daemons
-OVN_CLEANUP([hv1], [hv2])
-
-AT_CLEANUP
-
-# 3 hypervisors, one logical switch, 3 logical ports per hypervisor
-AT_SETUP([ovn -- portsecurity : 3 HVs, 1 LS, 3 lports/HV])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Create hypervisors hv[123].
-# Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3.
-# Add all of the vifs to a single logical switch lsw0.
-# Turn off port security on vifs vif[123]1
-# Turn on l2 port security on vifs vif[123]2
-# Turn of l2 and l3 port security on vifs vif[123]3
-# Make vif13, vif2[23], vif3[123] destinations for unknown MACs.
-ovn-nbctl ls-add lsw0
-net_add n1
-for i in 1 2 3; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-
- for j in 1 2 3; do
- ovs-vsctl add-port br-int vif$i$j -- set Interface vif$i$j external-ids:iface-id=lp$i$j options:tx_pcap=hv$i/vif$i$j-tx.pcap options:rxq_pcap=hv$i/vif$i$j-rx.pcap ofport-request=$i$j
- ovn-nbctl lsp-add lsw0 lp$i$j
- if test $j = 1; then
- ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" unknown
- elif test $j = 2; then
- ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j"
- ovn-nbctl lsp-set-port-security lp$i$j f0:00:00:00:00:$i$j
- else
- extra_addr="f0:00:00:00:0$i:$i$j fe80::ea2a:eaff:fe28:$i$j"
- ovn-nbctl lsp-set-addresses lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr"
- ovn-nbctl lsp-set-port-security lp$i$j "f0:00:00:00:00:$i$j 192.168.0.$i$j" "$extra_addr"
- fi
- done
-done
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- echo hv${1%?}
-}
-
-for i in 1 2 3; do
- for j in 1 2 3; do
- : > $i$j.expected
- done
-done
-
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-#
-# This shell function causes an ip packet to be received on INPORT.
-# The packet's content has Ethernet destination DST and source SRC
-# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits).
-# The OUTPORTs (zero or more) list the VIFs on which the packet should
-# be received. INPORT and the OUTPORTs are specified as logical switch
-# port numbers, e.g. 11 for vif11.
-test_ip() {
- # This packet has bad checksums but logical L3 routing doesn't check.
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
- shift; shift; shift; shift; shift
- hv=`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-# test_arp INPORT SHA SPA TPA DROP [REPLY_HA]
-#
-# Causes a packet to be received on INPORT. The packet is an ARP
-# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
- local inport=$1 smac=$2 sha=$3 spa=$4 tpa=$5 drop=$6 reply_ha=$7
- local request=ffffffffffff${smac}08060001080006040001${sha}${spa}ffffffffffff${tpa}
- hv=`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $request
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
- if test $drop != 1; then
- if test X$reply_ha = X; then
- # Expect to receive the broadcast ARP on the other logical switch ports
- # if no reply is expected.
- local i j
- for i in 1 2 3; do
- for j in 1 2 3; do
- if test $i$j != $inport; then
- echo $request >> $i$j.expected
- fi
- done
- done
- else
- # Expect to receive the reply, if any.
- local reply=${smac}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa}
- echo $reply >> $inport.expected
- fi
- fi
-}
-
-# test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-# This function is similar to test_ip() except that it sends
-# ipv6 packet
-test_ipv6() {
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}0000000000000000
- shift; shift; shift; shift; shift
- hv=`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-# test_icmpv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP ICMP_TYPE OUTPORT...
-# This function is similar to test_ipv6() except it specifies the ICMPv6 type
-# of the test packet
-test_icmpv6() {
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 icmp_type=$6
- local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}${icmp_type}00000000000000
- shift; shift; shift; shift; shift; shift
- hv=`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# no port security
-sip=`ip_to_hex 192 168 0 12`
-tip=`ip_to_hex 192 168 0 13`
-# the arp packet should be allowed even if lp[123]1 is
-# not configured with mac f00000000023 and ip 192.168.0.12
-for i in 1 2 3; do
- test_arp ${i}1 f00000000023 f00000000023 $sip $tip 0 f00000000013
- for j in 1 2 3; do
- if test $i != $j; then
- test_ip ${i}1 f000000000${i}1 f000000000${j}1 $sip $tip ${j}1
- fi
- done
-done
-
-# l2 port security
-sip=`ip_to_hex 192 168 0 12`
-tip=`ip_to_hex 192 168 0 13`
-
-# arp packet should be allowed since lp22 is configured with
-# mac f00000000022
-test_arp 22 f00000000022 f00000000022 $sip $tip 0 f00000000013
-
-# arp packet should not be allowed since lp32 is not configured with
-# mac f00000000021
-test_arp 32 f00000000021 f00000000021 $sip $tip 1
-
-# arp packet with sha set to f00000000021 should not be allowed
-# for lp12
-test_arp 12 f00000000012 f00000000021 $sip $tip 1
-
-# ip packets should be allowed and received since lp[123]2 do not
-# have l3 port security
-sip=`ip_to_hex 192 168 0 55`
-tip=`ip_to_hex 192 168 0 66`
-for i in 1 2 3; do
- for j in 1 2 3; do
- if test $i != $j; then
- test_ip ${i}2 f000000000${i}2 f000000000${j}2 $sip $tip ${j}2
- fi
- done
-done
-
-# ipv6 packets should be received by lp[123]2
-# lp[123]1 can send ipv6 traffic as there is no port security
-sip=fe800000000000000000000000000000
-tip=ff020000000000000000000000000000
-
-for i in 1 2 3; do
- test_ipv6 ${i}1 f000000000${i}1 f000000000${i}2 $sip $tip ${i}2
-done
-
-
-# l2 and l3 port security
-sip=`ip_to_hex 192 168 0 13`
-tip=`ip_to_hex 192 168 0 22`
-# arp packet should be allowed since lp13 is configured with
-# f00000000013 and 192.168.0.13
-test_arp 13 f00000000013 f00000000013 $sip $tip 0 f00000000022
-
-# the arp packet should be dropped because lp23 is not configured
-# with mac f00000000022
-sip=`ip_to_hex 192 168 0 13`
-tip=`ip_to_hex 192 168 0 22`
-test_arp 23 f00000000022 f00000000022 $sip $tip 1
-
-# the arp packet should be dropped because lp33 is not configured
-# with ip 192.168.0.55
-spa=`ip_to_hex 192 168 0 55`
-tpa=`ip_to_hex 192 168 0 22`
-test_arp 33 f00000000031 f00000000031 $spa $tpa 1
-
-# ip packets should not be received by lp[123]3 since
-# l3 port security is enabled
-sip=`ip_to_hex 192 168 0 55`
-tip=`ip_to_hex 192 168 0 66`
-for i in 1 2 3; do
- for j in 1 2 3; do
- test_ip ${i}2 f000000000${i}2 f000000000${j}3 $sip $tip
- done
-done
-
-# ipv6 packets should be dropped for lp[123]3 since
-# it is configured with only ipv4 address
-sip=fe800000000000000000000000000000
-tip=ff020000000000000000000000000000
-
-for i in 1 2 3; do
- test_ipv6 ${i}3 f000000000${i}3 f00000000022 $sip $tip
-done
-
-# ipv6 packets should not be received by lp[123]3 with mac f000000000$[123]3
-# lp[123]1 can send ipv6 traffic as there is no port security
-for i in 1 2 3; do
- test_ipv6 ${i}1 f000000000${i}1 f000000000${i}3 $sip $tip
-done
-
-# lp13 has extra port security with mac f0000000113 and ipv6 addr
-# fe80::ea2a:eaff:fe28:0012
-
-# ipv4 packet should be dropped for lp13 with mac f0000000113
-sip=`ip_to_hex 192 168 0 13`
-tip=`ip_to_hex 192 168 0 23`
-test_ip 13 f00000000113 f00000000023 $sip $tip
-
-# ipv6 packet should be received by lp[123]3 with mac f00000000${i}${i}3
-# and ip6.dst as fe80::ea2a:eaff:fe28:0${i}${i}3.
-# lp11 can send ipv6 traffic as there is no port security
-sip=ee800000000000000000000000000000
-for i in 1 2 3; do
- tip=fe80000000000000ea2aeafffe2800${i}3
- test_ipv6 11 f00000000011 f00000000${i}${i}3 $sip $tip ${i}3
-done
-
-
-# ipv6 packet should not be received by lp33 with mac f0000000333
-# and ip6.dst as fe80::ea2a:eaff:fe28:0023 as it is
-# configured with fe80::ea2a:eaff:fe28:0033
-# lp11 can send ipv6 traffic as there is no port security
-
-sip=ee800000000000000000000000000000
-tip=fe80000000000000ea2aeafffe280023
-test_ipv6 11 f00000000011 f00000000333 $sip $tip
-
-# ipv6 packet should be allowed for lp[123]3 with mac f0000000${i}${i}3
-# and ip6.src fe80::ea2a:eaff:fe28:0${i}${i}3 and ip6.src ::.
-# and should be dropped for any other ip6.src
-# lp21 can receive ipv6 traffic as there is no port security
-
-tip=ee800000000000000000000000000000
-for i in 1 2 3; do
- sip=fe80000000000000ea2aeafffe2800${i}3
- test_ipv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip $tip 21
-
- # Test ICMPv6 MLD reports (v1 and v2) and NS for DAD
- sip=00000000000000000000000000000000
- test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff020000000000000000000000160000 83 21
- test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff020000000000000000000000160000 8f 21
- test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff0200000000000000ea2aeafffe2800 87 21
- # Traffic to non-multicast traffic should be dropped
- test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip $tip 83
- # Traffic of other ICMPv6 types should be dropped
- test_icmpv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip ff020000000000000000000000160000 80
-
- # should be dropped
- sip=ae80000000000000ea2aeafffe2800aa
- test_ipv6 ${i}3 f00000000${i}${i}3 f00000000021 $sip $tip
-done
-
-# configure lsp13 to send and received IPv4 packets with an address range
-ovn-nbctl lsp-set-port-security lp13 "f0:00:00:00:00:13 192.168.0.13 20.0.0.4/24 10.0.0.0/24"
-
-sleep 2
-
-sip=`ip_to_hex 10 0 0 13`
-tip=`ip_to_hex 192 168 0 22`
-# arp packet with inner ip 10.0.0.13 should be allowed for lsp13
-test_arp 13 f00000000013 f00000000013 $sip $tip 0 f00000000022
-
-sip=`ip_to_hex 10 0 0 14`
-tip=`ip_to_hex 192 168 0 23`
-# IPv4 packet from lsp13 with src ip 10.0.0.14 destined to lsp23
-# with dst ip 192.168.0.23 should be allowed
-test_ip 13 f00000000013 f00000000023 $sip $tip 23
-
-sip=`ip_to_hex 192 168 0 33`
-tip=`ip_to_hex 10 0 0 15`
-# IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13
-# with dst ip 10.0.0.15 should be received by lsp13
-test_ip 33 f00000000033 f00000000013 $sip $tip 13
-
-sip=`ip_to_hex 192 168 0 33`
-tip=`ip_to_hex 20 0 0 4`
-# IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13
-# with dst ip 20.0.0.4 should be received by lsp13
-test_ip 33 f00000000033 f00000000013 $sip $tip 13
-
-sip=`ip_to_hex 192 168 0 33`
-tip=`ip_to_hex 20 0 0 5`
-# IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13
-# with dst ip 20.0.0.5 should not be received by lsp13
-test_ip 33 f00000000033 f00000000013 $sip $tip
-
-sip=`ip_to_hex 192 168 0 33`
-tip=`ip_to_hex 20 0 0 255`
-# IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13
-# with dst ip 20.0.0.255 should be received by lsp13
-test_ip 33 f00000000033 f00000000013 $sip $tip 13
-
-sip=`ip_to_hex 192 168 0 33`
-tip=`ip_to_hex 192 168 0 255`
-# IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13
-# with dst ip 192.168.0.255 should not be received by lsp13
-test_ip 33 f00000000033 f00000000013 $sip $tip
-
-sip=`ip_to_hex 192 168 0 33`
-tip=`ip_to_hex 224 0 0 4`
-# IPv4 packet from lsp33 with src ip 192.168.0.33 destined to lsp13
-# with dst ip 224.0.0.4 should be received by lsp13
-test_ip 33 f00000000033 f00000000013 $sip $tip 13
-
-#dump information including flow counters
-ovn-nbctl show
-ovn-sbctl dump-flows -- list multicast_group
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 show br-int
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv2 dump ------"
-as hv2 ovs-vsctl show
-as hv2 ovs-ofctl -O OpenFlow13 show br-int
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-echo "------ hv3 dump ------"
-as hv3 ovs-vsctl show
-as hv3 ovs-ofctl -O OpenFlow13 show br-int
-as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- for j in 1 2 3; do
- OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected])
- done
-done
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 HVs, 2 LS, 1 lport/LS, 2 peer LRs])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24
-# network. R1 has a switchs ls1 (191.168.1.0/24) connected to it.
-# R2 has ls2 (172.16.1.0/24) connected to it.
-
-ls1_lp1_mac="f0:00:00:01:02:03"
-rp_ls1_mac="00:00:00:01:02:03"
-rp_ls2_mac="00:00:00:01:02:04"
-ls2_lp1_mac="f0:00:00:01:02:04"
-
-ls1_lp1_ip="192.168.1.2"
-ls2_lp1_ip="172.16.1.2"
-
-ovn-nbctl lr-add R1
-ovn-nbctl lr-add R2
-
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-
-# Connect ls1 to R1
-ovn-nbctl lrp-add R1 ls1 $rp_ls1_mac 192.168.1.1/24
-
-ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \
- options:router-port=ls1 addresses=\"$rp_ls1_mac\"
-
-# Connect ls2 to R2
-ovn-nbctl lrp-add R2 ls2 $rp_ls2_mac 172.16.1.1/24
-
-ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \
- options:router-port=ls2 addresses=\"$rp_ls2_mac\"
-
-# Connect R1 to R2
-ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 20.0.0.1/24 peer=R2_R1
-ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 20.0.0.2/24 peer=R1_R2
-
-ovn-nbctl lr-route-add R1 "0.0.0.0/0" 20.0.0.2
-ovn-nbctl lr-route-add R2 "0.0.0.0/0" 20.0.0.1
-
-# Create logical port ls1-lp1 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "$ls1_lp1_mac $ls1_lp1_ip"
-
-# Create logical port ls2-lp1 in ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "$ls2_lp1_mac $ls2_lp1_ip"
-
-# Create two hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Packet to send.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && eth.dst==$rp_ls1_mac &&
- ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl show br-int
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump ----------"
-as hv2 ovs-ofctl show br-int
-as hv2 ovs-ofctl dump-flows br-int
-
-# Packet to Expect
-# The TTL should be decremented by 2.
-packet="eth.src==$rp_ls2_mac && eth.dst==$ls2_lp1_mac &&
- ip4 && ip.ttl==62 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $packet | ovstest test-ovn expr-to-packets > expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
-grep "reg0 == 172.16.1.2" | wc -l], [0], [1
-])
-
-# Disable the ls2-lp1 port.
-ovn-nbctl --wait=hv set logical_switch_port ls2-lp1 enabled=false
-
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_arp_resolve | \
-grep "reg0 == 172.16.1.2" | wc -l], [0], [0
-])
-
-# Generate the packet destined for ls2-lp1 and it should not be delivered.
-# Packet to send.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_lp1_mac && eth.dst==$rp_ls1_mac &&
- ip4 && ip.ttl==64 && ip4.src==$ls1_lp1_ip && ip4.dst==$ls2_lp1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-# The 2nd packet sent shound not be received.
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-
-AT_SETUP([ovn -- 1 HV, 1 LS, 2 lport/LS, 1 LR])
-AT_KEYWORDS([router-admin-state])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR - R1 has switch ls1 with two subnets attached to it (191.168.1.0/24
-# and 172.16.1.0/24) connected to it.
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add ls1
-
-# Connect ls1 to R1
-ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:03 192.168.1.1/24 172.16.1.1/24
-ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \
- options:router-port=ls1 addresses=\"00:00:00:01:02:03\"
-
-# Create logical port ls1-lp1 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
- -- lsp-set-addresses ls1-lp1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port ls1-lp2 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp2 \
- -- lsp-set-addresses ls1-lp2 "f0:00:00:01:02:04 172.16.1.2"
-
-# Create one hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int vif1 -- \
- set interface vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif2 -- \
- set interface vif2 external-ids:iface-id=ls1-lp2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=1
-
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Send ip packets between the two ports.
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Packet to send.
-src_mac="f00000010203"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
-
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-
-#Disable router R1
-ovn-nbctl set Logical_Router R1 enabled=false
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
-
-# Packet to Expect
-expect_src_mac="000000010203"
-expect_dst_mac="f00000010204"
-echo "${expect_dst_mac}${expect_src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-
-AT_SETUP([ovn -- 1 HV, 2 LSs, 1 lport/LS, 1 LR])
-AT_KEYWORDS([router-admin-state])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it,
-# and has switch ls2 (172.16.1.0/24) connected to it.
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-
-# Connect ls1 to R1
-ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 type=router \
- options:router-port=ls1 addresses=\"00:00:00:01:02:03\"
-
-# Connect ls2 to R1
-ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:04 172.16.1.1/24
-ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 type=router \
- options:router-port=ls2 addresses=\"00:00:00:01:02:04\"
-
-# Create logical port ls1-lp1 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port ls2-lp1 in ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "f0:00:00:01:02:04 172.16.1.2"
-
-# Create one hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int vif1 -- \
- set interface vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif2 -- \
- set interface vif2 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=1
-
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Send ip packets between the two ports.
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Packet to send.
-src_mac="f00000010203"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
-
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-#Disable router R1
-ovn-nbctl set Logical_Router R1 enabled=false
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-# Allow some time for the disabling of logical router R1 to propagate.
-# XXX This should be more systematic.
-sleep 1
-
-as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
-
-# Packet to Expect
-expect_src_mac="000000010204"
-expect_dst_mac="f00000010204"
-echo "${expect_dst_mac}${expect_src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 HVs, 3 LS, 1 lport/LS, 2 peer LRs, static routes])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24
-# network. R1 has switchess foo (192.168.1.0/24)
-# connected to it.
-# R2 has alice (172.16.1.0/24) and bob (172.16.2.0/24) connected to it.
-
-ovn-nbctl lr-add R1
-ovn-nbctl lr-add R2
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add bob
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \
- options:router-port=foo addresses=\"00:00:00:01:02:03\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:00:01:02:04 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:00:01:02:04\"
-
-# Connect bob to R2
-ovn-nbctl lrp-add R2 bob 00:00:00:01:02:05 172.16.2.1/24
-ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob type=router \
- options:router-port=bob addresses=\"00:00:00:01:02:05\"
-
-# Connect R1 to R2
-ovn-nbctl lrp-add R1 R1_R2 00:00:00:02:03:04 20.0.0.1/24 peer=R2_R1
-ovn-nbctl lrp-add R2 R2_R1 00:00:00:02:03:05 20.0.0.2/24 peer=R1_R2
-
-#install static routes
-ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
-ovn-nbctl lr-route-add R2 172.16.2.0/24 20.0.0.2 R1_R2
-ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port alice1 in alice
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
-
-# Create logical port bob1 in bob
-ovn-nbctl lsp-add bob bob1 \
--- lsp-set-addresses bob1 "f0:00:00:01:02:05 172.16.2.2"
-
-# Create two hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=alice1 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=bob1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send ip packets between foo1 and alice1
-src_mac="f00000010203"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-# Send ip packets between foo1 and bob1
-src_mac="f00000010203"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 2 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump ----------"
-as hv2 ovs-ofctl dump-flows br-int
-
-# Packet to Expect at bob1
-src_mac="000000010205"
-dst_mac="f00000010205"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 2 2`
-echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Packet to Expect at alice1
-src_mac="000000010204"
-dst_mac="f00000010204"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- send gratuitous arp on localnet])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-ovn-nbctl ls-add lsw0
-net_add n1
-sim_add hv
-as hv
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0
-
-ovn_attach n1 br-phys 192.168.0.1
-
-AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
-AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv/snoopvif-tx.pcap options:rxq_pcap=hv/snoopvif-rx.pcap])
-
-# Create a vif.
-AT_CHECK([ovn-nbctl lsp-add lsw0 localvif1])
-AT_CHECK([ovn-nbctl lsp-set-addresses localvif1 "f0:00:00:00:00:01 192.168.1.2"])
-AT_CHECK([ovn-nbctl lsp-set-port-security localvif1 "f0:00:00:00:00:01"])
-
-# Create a localnet port.
-AT_CHECK([ovn-nbctl lsp-add lsw0 ln_port])
-AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
-AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
-AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
-
-AT_CHECK([ovs-vsctl add-port br-int localvif1 -- set Interface localvif1 external_ids:iface-id=localvif1])
-
-# Wait for packet to be received.
-echo "fffffffffffff0000000000108060001080006040001f00000000001c0a80102000000000000c0a80102" > expected
-OVN_CHECK_PACKETS([hv/snoopvif-tx.pcap], [expected])
-
-# Check GARP packet when restart openflow connection.
-as hv
-OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
-
-OVS_WAIT_UNTIL([grep -c "waiting 4 seconds before reconnect" hv/ovn-controller.log])
-
-as hv
-start_daemon ovs-vswitchd --enable-dummy=system -vvconn -vofproto_dpif -vunixctl
-
-# Wait for packet to be received.
-echo "fffffffffffff0000000000108060001080006040001f00000000001c0a80102000000000000c0a80102" > expected
-OVN_CHECK_PACKETS([hv/snoopvif-tx.pcap], [expected])
-
-# Delete the localnet ports.
-AT_CHECK([ovs-vsctl del-port localvif1])
-AT_CHECK([ovn-nbctl lsp-del ln_port])
-
-OVN_CLEANUP([hv])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 HVs, 3 LRs connected via LS, static routes])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24)
-# connected to it. R2 has alice (172.16.1.0/24) and R3 has bob (10.32.1.0/24)
-# connected to it.
-
-ovn-nbctl lr-add R1
-ovn-nbctl lr-add R2
-ovn-nbctl lr-add R3
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add bob
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \
- options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect bob to R3
-ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 10.32.1.1/24
-ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
- type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Connect R3 to join
-ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24
-ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
- type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
-
-#install static routes
-ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
-ovn-nbctl lr-route-add R1 10.32.1.0/24 20.0.0.3
-
-ovn-nbctl lr-route-add R2 192.168.1.0/24 20.0.0.1
-ovn-nbctl lr-route-add R2 10.32.1.0/24 20.0.0.3
-
-ovn-nbctl lr-route-add R3 192.168.1.0/24 20.0.0.1
-ovn-nbctl lr-route-add R3 172.16.1.0/24 20.0.0.2
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port alice1 in alice
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
-
-# Create logical port bob1 in bob
-ovn-nbctl lsp-add bob bob1 \
--- lsp-set-addresses bob1 "f0:00:00:01:02:05 10.32.1.2"
-
-# Create two hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=alice1 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=bob1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send ip packets between foo1 and alice1
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet
-
-# Send ip packets between foo1 and bob1
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 10 32 1 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl show br-int
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump ----------"
-as hv2 ovs-ofctl show br-int
-as hv2 ovs-ofctl dump-flows br-int
-echo "----------------------------"
-
-# Packet to Expect at bob1
-src_mac="000003010203"
-dst_mac="f00000010205"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 10 32 1 2`
-echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Packet to Expect at alice1
-src_mac="000002010203"
-dst_mac="f00000010204"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-echo "${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- dhcpv4 : 1 HV, 2 LS, 2 LSPs/LS])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add ls1
-
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4"
-
-ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4"
-
-ovn-nbctl lsp-add ls1 ls1-lp2 \
--- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
-
-ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
-
-ovn-nbctl ls-add ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4"
-ovn-nbctl lsp-set-port-security ls2-lp1 "f0:00:00:00:00:03 30.0.0.6 40.0.0.4"
-ovn-nbctl lsp-add ls2 ls2-lp2 \
--- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 30.0.0.7"
-ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 30.0.0.7"
-
-d1="$(ovn-nbctl create DHCP_Options cidr=10.0.0.0/24 \
-options="\"server_id\"=\"10.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:01\" \
-\"lease_time\"=\"3600\" \"router\"=\"10.0.0.1\"")"
-
-ovn-nbctl lsp-set-dhcpv4-options ls1-lp1 ${d1}
-ovn-nbctl lsp-set-dhcpv4-options ls1-lp2 ${d1}
-
-d2="$(ovn-nbctl create DHCP_Options cidr=30.0.0.0/24 \
-options="\"server_id\"=\"30.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:02\" \
-\"lease_time\"=\"3600\"")"
-
-ovn-nbctl lsp-set-dhcpv4-options ls2-lp2 ${d2}
-
-net_add n1
-sim_add hv1
-
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-ovs-vsctl -- add-port br-int hv1-vif3 -- \
- set interface hv1-vif3 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=hv1/vif3-tx.pcap \
- options:rxq_pcap=hv1/vif3-rx.pcap \
- ofport-request=3
-
-ovs-vsctl -- add-port br-int hv1-vif4 -- \
- set interface hv1-vif4 external-ids:iface-id=ls2-lp2 \
- options:tx_pcap=hv1/vif4-tx.pcap \
- options:rxq_pcap=hv1/vif4-rx.pcap \
- ofport-request=4
-
-OVN_POPULATE_ARP
-
-sleep 2
-
-as hv1 ovs-vsctl show
-
-# This shell function sends a DHCP request packet
-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP REQUEST_IP ...
-test_dhcp() {
- local inport=$1 src_mac=$2 dhcp_type=$3 ciaddr=$4 offer_ip=$5 request_ip=$6 use_ip=$7
- shift; shift; shift; shift; shift; shift; shift;
- if test $use_ip != 0; then
- src_ip=$1
- dst_ip=$2
- shift; shift;
- else
- src_ip=`ip_to_hex 0 0 0 0`
- dst_ip=`ip_to_hex 255 255 255 255`
- fi
- if test $request_ip != 0; then
- ip_len=0120
- udp_len=010b
- else
- ip_len=011a
- udp_len=0106
- fi
- local request=ffffffffffff${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip}
- # udp header and dhcp header
- request=${request}00440043${udp_len}0000
- request=${request}010106006359aa7600000000${ciaddr}000000000000000000000000${src_mac}
- # client hardware padding
- request=${request}00000000000000000000
- # server hostname
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- # boot file name
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- # dhcp magic cookie
- request=${request}63825363
- # dhcp message type
- request=${request}3501${dhcp_type}
- # dhcp unknown option
- request=${request}d70701020304050607
- # dhcp pad option
- request=${request}00
- if test $request_ip != 0; then
- # dhcp requested ip
- request=${request}3204${request_ip}
- fi
- # dhcp end option
- request=${request}ff
-
- for port in $inport "$@"; do
- : >> $port.expected
- done
- if test $offer_ip != 0; then
- local srv_mac=$1 srv_ip=$2 dhcp_reply_type=$3 expected_dhcp_opts=$4
- # total IP length will be the IP length of the request packet
- # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2)
- ip_len=`expr 280 + ${#expected_dhcp_opts} / 2`
- udp_len=`expr $ip_len - 20`
- ip_len=$(printf "%x" $ip_len)
- udp_len=$(printf "%x" $udp_len)
- # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len
- local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
- # udp header and dhcp header.
- # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
- reply=${reply}004300440${udp_len}0000020106006359aa7600000000${ciaddr}
- # your ip address; 0 for NAK
- if test $dhcp_reply_type = 06; then
- reply=${reply}00000000
- else
- reply=${reply}${offer_ip}
- fi
- # next server ip address, relay agent ip address, client mac address
- reply=${reply}0000000000000000${src_mac}
- # client hardware padding
- reply=${reply}00000000000000000000
- # server hostname
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- # boot file name
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- # dhcp magic cookie
- reply=${reply}63825363
- reply=${reply}3501${dhcp_reply_type}${expected_dhcp_opts}00000000ff00000000
- echo $reply >> $inport.expected
- else
- for outport; do
- echo $request >> $outport.expected
- done
- fi
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-AT_CAPTURE_FILE([ofctl_monitor0.log])
-as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-# Send DHCPDISCOVER.
-offer_ip=`ip_to_hex 10 0 0 4`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=`ip_to_hex 0 0 0 0`
-request_ip=0
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000001 01 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 1.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-cat 1.expected | cut -c -48 > expout
-AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 1.expected | cut -c 53- > expout
-AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
-
-# ovs-ofctl also resumes the packets and this causes other ports to receive
-# the DHCP request packet. So reset the pcap files so that its easier to test.
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with the offered IP
-# address in the Requested IP Address option.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=`ip_to_hex 0 0 0 0`
-request_ip=$offer_ip
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPREQUEST in the SELECTING/INIT-REBOOT state with a mismatched IP in
-# the Requested IP Address option, expect a DHCPNAK.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=`ip_to_hex 0 0 0 0`
-request_ip=`ip_to_hex 10 0 0 7`
-expected_dhcp_opts=""
-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 3.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send Invalid DHCPv4 packet on ls1-lp2. It should be received by ovn-controller
-# but should be resumed without the reply.
-# ls1-lp1 (vif1-tx.pcap) should receive the DHCPv4 request packet twice,
-# one from ovn-controller and the other from "ovs-ofctl resume."
-ciaddr=`ip_to_hex 0 0 0 0`
-offer_ip=0
-request_ip=0
-test_dhcp 2 f00000000002 08 $ciaddr $offer_ip $request_ip 0 1 1
-
-# NXT_RESUMEs should be 4.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-# vif1-tx.pcap should have received the DHCPv4 (invalid) request packet
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPv4 packet on ls2-lp1. It doesn't have any DHCPv4 options defined.
-# ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
-
-ciaddr=`ip_to_hex 0 0 0 0`
-test_dhcp 3 f00000000003 01 $ciaddr 0 0 4 0
-
-# Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
-# this lport.
-ciaddr=`ip_to_hex 0 0 0 0`
-test_dhcp 4 f00000000004 01 $ciaddr 0 0 3 0
-
-# NXT_RESUMEs should be 4.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-#OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [3.expected])
-#OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [4.expected])
-
-# Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
-# and ip4.dst set to 10.0.0.1.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=$offer_ip
-request_ip=0
-src_ip=$offer_ip
-dst_ip=$server_ip
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 5.
-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPREQUEST in the RENEWING/REBINDING state with ip4.src set to 10.0.0.6
-# and ip4.dst set to 255.255.255.255.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=$offer_ip
-request_ip=0
-src_ip=$offer_ip
-dst_ip=`ip_to_hex 255 255 255 255`
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 6.
-OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPREQUEST in the RENEWING/REBINDING state with a mismatched IP in the
-# ciaddr, expect a DHCPNAK.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=`ip_to_hex 10 0 0 7`
-request_ip=0
-src_ip=$offer_ip
-dst_ip=`ip_to_hex 255 255 255 255`
-expected_dhcp_opts=""
-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 7.
-OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPREQUEST in the RENEWING/REBINDING state without a specifyied ciaddr,
-# expect a DHCPNAK.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-ciaddr=`ip_to_hex 0 0 0 0`
-request_ip=0
-src_ip=$offer_ip
-dst_ip=`ip_to_hex 255 255 255 255`
-expected_dhcp_opts=""
-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
-
-# NXT_RESUMEs should be 8.
-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Send DHCPREQUEST with ip4.src set to 10.0.0.6 and ip4.dst set to 10.0.0.4.
-# The packet should not be received by ovn-controller.
-ciaddr=`ip_to_hex 0 0 0 0`
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 4`
-test_dhcp 2 f00000000002 03 $ciaddr 0 0 1 $src_ip $dst_ip 1
-
-# NXT_RESUMEs should be 8.
-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-# vif1-tx.pcap should have received the DHCPv4 request packet
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- dhcpv6 : 1 HV, 2 LS, 5 LSPs])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4"
-
-ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4"
-
-ovn-nbctl lsp-add ls1 ls1-lp2 \
--- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 ae70::5"
-
-ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 ae70::5"
-
-ovn-nbctl lsp-add ls1 ls1-lp3 \
--- lsp-set-addresses ls1-lp3 "f0:00:00:00:00:22 ae70::22"
-
-ovn-nbctl lsp-set-port-security ls1-lp3 "f0:00:00:00:00:22 ae70::22"
-
-d1="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64" \
-options="\"server_id\"=\"00:00:00:10:00:01\"")"
-
-ovn-nbctl lsp-set-dhcpv6-options ls1-lp1 ${d1}
-ovn-nbctl lsp-set-dhcpv6-options ls1-lp2 ${d1}
-
-d2="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64" \
-options="\"dhcpv6_stateless\"=\"true\" \"server_id\"=\"00:00:00:10:00:01\"")"
-
-ovn-nbctl lsp-set-dhcpv6-options ls1-lp3 ${d2}
-
-ovn-nbctl ls-add ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "f0:00:00:00:00:03 be70::3"
-ovn-nbctl lsp-set-port-security ls2-lp1 "f0:00:00:00:00:03 be70::3"
-ovn-nbctl lsp-add ls2 ls2-lp2 \
--- lsp-set-addresses ls2-lp2 "f0:00:00:00:00:04 be70::4"
-ovn-nbctl lsp-set-port-security ls2-lp2 "f0:00:00:00:00:04 be70::4"
-
-net_add n1
-sim_add hv1
-
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-ovs-vsctl -- add-port br-int hv1-vif3 -- \
- set interface hv1-vif3 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=hv1/vif3-tx.pcap \
- options:rxq_pcap=hv1/vif3-rx.pcap \
- ofport-request=3
-
-ovs-vsctl -- add-port br-int hv1-vif4 -- \
- set interface hv1-vif4 external-ids:iface-id=ls2-lp2 \
- options:tx_pcap=hv1/vif4-tx.pcap \
- options:rxq_pcap=hv1/vif4-rx.pcap \
- ofport-request=4
-
-ovs-vsctl -- add-port br-int hv1-vif5 -- \
- set interface hv1-vif5 external-ids:iface-id=ls1-lp3 \
- options:tx_pcap=hv1/vif5-tx.pcap \
- options:rxq_pcap=hv1/vif5-rx.pcap \
- ofport-request=5
-
-OVN_POPULATE_ARP
-
-sleep 2
-
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-# This shell function sends a DHCPv6 request packet
-# test_dhcpv6 INPORT SRC_MAC SRC_LLA DHCPv6_MSG_TYPE OFFER_IP OUTPORT...
-# The OUTPORTs (zero or more) list the VIFs on which the original DHCPv6
-# packet should be received twice (one from ovn-controller and the other
-# from the "ovs-ofctl monitor br-int resume"
-test_dhcpv6() {
- local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5
- if test $msg_code != 0b; then
- req_len=2a
- else
- req_len=1a
- fi
- local request=ffffffffffff${src_mac}86dd0000000000${req_len}1101${src_lla}
- # dst ip ff02::1:2
- request=${request}ff020000000000000000000000010002
- # udp header and dhcpv6 header
- request=${request}0222022300${req_len}ffff${msg_code}010203
- # Client identifier
- request=${request}0001000a00030001${src_mac}
- # Add IA-NA (Identity Association for Non Temporary Address) if msg_code
- # is not 11 (information request packet)
- if test $msg_code != 0b; then
- request=${request}0003000c0102030400000e1000001518
- fi
- shift; shift; shift; shift; shift;
- if test $offer_ip != 0; then
- local server_mac=000000100001
- local server_lla=fe80000000000000020000fffe100001
- local reply_code=07
- if test $msg_code = 01; then
- reply_code=02
- fi
- local msg_len=54
- if test $offer_ip = 1; then
- msg_len=28
- fi
- local reply=${src_mac}${server_mac}86dd0000000000${msg_len}1101${server_lla}${src_lla}
- # udp header and dhcpv6 header
- reply=${reply}0223022200${msg_len}ffff${reply_code}010203
- # Client identifier
- reply=${reply}0001000a00030001${src_mac}
- # IA-NA
- if test $offer_ip != 1; then
- reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}ffffffffffffffff
- fi
- # Server identifier
- reply=${reply}0002000a00030001${server_mac}
- echo $reply | trim_zeros >> $inport.expected
- else
- for outport; do
- echo $request | trim_zeros >> $outport.expected
- done
- fi
-
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-AT_CAPTURE_FILE([ofctl_monitor0.log])
-as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-src_mac=f00000000001
-src_lla=fe80000000000000f20000fffe000001
-offer_ip=ae700000000000000000000000000004
-test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip
-
-# NXT_RESUMEs should be 1.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros > 1.packets
-# cat 1.expected | trim_zeros > expout
-cat 1.expected | cut -c -120 > expout
-AT_CHECK([cat 1.packets | cut -c -120], [0], [expout])
-# Skipping the UDP checksum
-cat 1.expected | cut -c 125- > expout
-AT_CHECK([cat 1.packets | cut -c 125-], [0], [expout])
-
-rm 1.expected
-
-# Send invalid packet on ls1-lp2. ovn-controller should resume the packet
-# without any modifications and the packet should be received by ls1-lp1.
-# ls1-lp1 will receive the packet twice, one from the ovn-controller after the
-# resume and the other from ovs-ofctl monitor resume.
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-
-src_mac=f00000000002
-src_lla=fe80000000000000f20000fffe000002
-offer_ip=ae700000000000000000000000000005
-# Set invalid msg_type
-
-test_dhcpv6 2 $src_mac $src_lla 10 0 1 1
-
-# NXT_RESUMEs should be 2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-# vif2-tx.pcap should not have received the DHCPv6 reply packet
-rm 2.packets
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > 2.packets
-AT_CHECK([cat 2.packets], [0], [])
-
-# vif1-tx.pcap should have received the DHCPv6 (invalid) request packet
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | trim_zeros > 1.packets
-cat 1.expected > expout
-AT_CHECK([cat 1.packets], [0], [expout])
-
-# Send DHCPv6 packet on ls2-lp1. native DHCPv6 is disabled on this port.
-# There should be no DHCPv6 reply from ovn-controller and the request packet
-# should be received by ls2-lp2.
-
-src_mac=f00000000003
-src_lla=fe80000000000000f20000fffe000003
-test_dhcpv6 3 $src_mac $src_lla 01 0 4
-
-# NXT_RESUMEs should be 2 only.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-# vif3-tx.pcap should not have received the DHCPv6 reply packet
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap | trim_zeros > 3.packets
-AT_CHECK([cat 3.packets], [0], [])
-
-# vif4-tx.pcap should have received the DHCPv6 request packet
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif4-tx.pcap | trim_zeros > 4.packets
-cat 4.expected > expout
-AT_CHECK([cat 4.packets], [0], [expout])
-
-# Send DHCPv6 packet on ls1-lp3. native DHCPv6 works as stateless mode for this port.
-# The DHCPv6 reply shouldn't contain offer_ip.
-src_mac=f00000000022
-src_lla=fe80000000000000f20000fffe000022
-reset_pcap_file hv1-vif5 hv1/vif5
-test_dhcpv6 5 $src_mac $src_lla 01 1 5
-
-# NXT_RESUMEs should be 3.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap | trim_zeros > 5.packets
-# Skipping the UDP checksum
-cat 5.expected | cut -c 1-120,125- > expout
-AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout])
-
-# Send DHCPv6 information request (code 11) on ls1-lp3. The DHCPv6 reply
-# shouldn't contain offer_ip
-src_mac=f00000000022
-src_lla=fe80000000000000f20000fffe000022
-reset_pcap_file hv1-vif5 hv1/vif5
-rm -f 5.expected
-test_dhcpv6 5 $src_mac $src_lla 0b 1 5
-
-# NXT_RESUMEs should be 4.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif5-tx.pcap |
-trim_zeros > 5.packets
-# Skipping the UDP checksum
-cat 5.expected | cut -c 1-120,125- > expout
-AT_CHECK([cat 5.packets | cut -c 1-120,125- ], [0], [expout])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 HVs, 2 LRs connected via LS, gateway router])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# Two LRs - R1 and R2 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24)
-# connected to it. R2 has alice (172.16.1.0/24) connected to it.
-# R2 is a gateway router.
-
-
-
-# Create two hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=alice1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl create Logical_Router name=R2 options:chassis="hv2"
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-
-#install static routes
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
-R1 static_routes @lrt
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
-R2 static_routes @lrt
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port alice1 in alice
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
-
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 2
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send ip packets between foo1 and alice1
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-ovn-sbctl list encap
-echo "---------------------"
-
-# Packet to Expect at alice1
-src_mac="000002010203"
-dst_mac="f00000010204"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
-
-
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet
-
-echo "------ hv1 dump after packet 1 ----------"
-as hv1 ovs-ofctl show br-int
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump after packet 1 ----------"
-as hv2 ovs-ofctl show br-int
-as hv2 ovs-ofctl dump-flows br-int
-echo "----------------------------"
-
-echo $expected > expected
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Delete the router and re-create it. Things should work as before.
-ovn-nbctl lr-del R2
-ovn-nbctl create Logical_Router name=R2 options:chassis="hv2"
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
-R2 static_routes @lrt
-
-# Wait for ovn-controller to catch up.
-sleep 1
-
-# Send the packet again.
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-echo "------ hv1 dump after packet 2 ----------"
-as hv1 ovs-ofctl show br-int
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump after packet 2 ----------"
-as hv2 ovs-ofctl show br-int
-as hv2 ovs-ofctl dump-flows br-int
-echo "----------------------------"
-
-echo $expected >> expected
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- icmp_reply: 1 HVs, 2 LSs, 1 lport/LS, 1 LR])
-AT_KEYWORDS([router-icmp-reply])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it,
-# and has switch ls2 (172.16.1.0/24) connected to it.
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-
-# Connect ls1 to R1
-ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24
-ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \
- type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\"
-
-# Connect ls2 to R1
-ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24
-ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \
- type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\"
-
-# Create logical port ls1-lp1 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2"
-
-# Create logical port ls2-lp1 in ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2"
-
-# Create one hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int vif1 -- \
- set interface vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif2 -- \
- set interface vif2 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=1
-
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-for i in 1 2; do
- : > vif$i.expected
-done
-# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM]
-#
-# Causes a packet to be received on INPORT. The packet is an ICMPv4
-# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and
-# ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are
-# provided, then it should be the ip and icmp checksums of the packet
-# responded; otherwise, no reply is expected.
-# In the absence of an ip checksum calculation helpers, this relies
-# on the caller to provide the checksums for the ip and icmp headers.
-# XXX This should be more systematic.
-#
-# INPORT is an lport number, e.g. 11 for vif11.
-# ETH_SRC and ETH_DST are each 12 hex digits.
-# IPV4_SRC and IPV4_DST are each 8 hex digits.
-# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits.
-# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits.
-test_ipv4_icmp_request() {
- local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 ip_chksum=$6 icmp_chksum=$7
- local exp_ip_chksum=$8 exp_icmp_chksum=$9
- shift; shift; shift; shift; shift; shift; shift
- shift; shift
-
- # Use ttl to exercise section 4.2.2.9 of RFC1812
- local ip_ttl=01
- local icmp_id=5fbf
- local icmp_seq=0001
- local icmp_data=$(seq 1 56 | xargs printf "%02x")
- local icmp_type_code_request=0800
- local icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data}
- local packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload}
-
- as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet
- if test X$exp_icmp_chksum != X; then
- # Expect to receive the reply, if any. In same port where packet was sent.
- # Note: src and dst fields are expected to be reversed.
- local icmp_type_code_response=0000
- local reply_icmp_ttl=fe
- local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data}
- local reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload}
- echo $reply >> vif$inport.expected
- fi
-}
-
-# Send ping packet to router's ip addresses, from each of the 2 logical ports.
-rtr_l1_ip=$(ip_to_hex 192 168 1 1)
-rtr_l2_ip=$(ip_to_hex 172 16 1 1)
-l1_ip=$(ip_to_hex 192 168 1 2)
-l2_ip=$(ip_to_hex 172 16 1 2)
-
-# Ping router ip address that is on same subnet as the logical port
-test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l1_ip 0000 8510 02ff 8d10
-test_ipv4_icmp_request 2 000000010204 0000000102f2 $l2_ip $rtr_l2_ip 0000 8510 02ff 8d10
-
-# Ping router ip address that is on the other side of the logical ports
-test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 8510 02ff 8d10
-test_ipv4_icmp_request 2 000000010204 0000000102f2 $l2_ip $rtr_l1_ip 0000 8510 02ff 8d10
-
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-# Now check the packets actually received against the ones expected.
-for inport in 1 2; do
- OVN_CHECK_PACKETS([hv1/vif${inport}-tx.pcap], [vif$inport.expected])
-done
-
-OVN_CLEANUP([hv1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- policy-based routing: 1 HVs, 2 LSs, 1 lport/LS, 1 LR])
-AT_KEYWORDS([policy-based-routing])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it,
-# and has switch ls2 (172.16.1.0/24) connected to it.
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-ovn-nbctl ls-add ls3
-
-# Connect ls1 to R1
-ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24
-ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \
- type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\"
-
-# Connect ls2 to R1
-ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24
-ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \
- type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\"
-
-# Connect ls3 to R1
-ovn-nbctl lrp-add R1 ls3 00:00:00:01:02:f3 20.20.1.1/24
-ovn-nbctl lsp-add ls3 rp-ls3 -- set Logical_Switch_Port rp-ls3 \
- type=router options:router-port=ls3 addresses=\"00:00:00:01:02:f3\"
-
-# Create logical port ls1-lp1 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2"
-
-# Create logical port ls2-lp1 in ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2"
-
-# Create logical port ls3-lp1 in ls3
-ovn-nbctl lsp-add ls3 ls3-lp1 \
--- lsp-set-addresses ls3-lp1 "00:00:00:01:02:05 20.20.1.2"
-
-# Create one hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add pbr-hv
-as pbr-hv
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-
-ovs-vsctl -- add-port br-int vif1 -- \
- set interface vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=pbr-hv/vif1-tx.pcap \
- options:rxq_pcap=pbr-hv/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif2 -- \
- set interface vif2 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=pbr-hv/vif2-tx.pcap \
- options:rxq_pcap=pbr-hv/vif2-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif3 -- \
- set interface vif3 external-ids:iface-id=ls3-lp1 \
- options:tx_pcap=pbr-hv/vif3-tx.pcap \
- options:rxq_pcap=pbr-hv/vif3-rx.pcap \
- ofport-request=1
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ls1_ro_mac=00:00:00:01:02:f1
-ls1_ro_ip=192.168.1.1
-
-ls2_ro_mac=00:00:00:01:02:f2
-ls2_ro_ip=172.16.1.1
-
-ls3_ro_mac=00:00:00:01:02:f3
-
-ls1_p1_mac=00:00:00:01:02:03
-ls1_p1_ip=192.168.1.2
-
-ls2_p1_mac=00:00:00:01:02:04
-ls2_p1_ip=172.16.1.2
-
-ls3_p1_mac=00:00:00:01:02:05
-
-# Create a drop policy
-ovn-nbctl lr-policy-add R1 10 "ip4.src==192.168.1.0/24 && ip4.dst==172.16.1.0/24" drop
-
-# Check logical flow
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | grep "192.168.1.0" | wc -l], [0], [dnl
-1
-])
-
-# Send packet.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_p1_mac && eth.dst==$ls1_ro_mac &&
- ip4 && ip.ttl==64 && ip4.src==$ls1_p1_ip && ip4.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-
-as pbr-hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Check if packet hit the drop policy
-AT_CHECK([ovs-ofctl dump-flows br-int | \
- grep "nw_src=192.168.1.0/24,nw_dst=172.16.1.0/24 actions=drop" | \
- grep "priority=10" | \
- grep "n_packets=1" | wc -l], [0], [dnl
-1
-])
-
-# Expected to drop the packet.
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" pbr-hv/vif2-tx.pcap > vif2.packets
-rcvd_packet=`cat vif2.packets`
-AT_FAIL_IF([rcvd_packet = ""])
-
-# Override drop policy with allow
-ovn-nbctl lr-policy-add R1 20 "ip4.src==192.168.1.0/24 && ip4.dst==172.16.1.0/24" allow
-
-# Check logical flow
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | grep "192.168.1.0" | wc -l], [0], [dnl
-2
-])
-
-# Send packet.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_p1_mac && eth.dst==$ls1_ro_mac &&
- ip4 && ip.ttl==64 && ip4.src==$ls1_p1_ip && ip4.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-as pbr-hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Check if packet hit the allow policy
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | \
- grep "192.168.1.0" | \
- grep "priority=20" | wc -l], [0], [dnl
-1
-])
-
-# Expected packet has TTL decreased by 1
-expected="eth.src==$ls2_ro_mac && eth.dst==$ls2_p1_mac &&
- ip4 && ip.ttl==63 && ip4.src==$ls1_p1_ip && ip4.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $expected | ovstest test-ovn expr-to-packets > expected
-
-OVN_CHECK_PACKETS([pbr-hv/vif2-tx.pcap], [expected])
-
-# Override allow policy with reroute
-ovn-nbctl lr-policy-add R1 30 "ip4.src==192.168.1.0/24 && ip4.dst==172.16.1.0/24" reroute 20.20.1.2
-
-# Check logical flow
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | \
- grep "192.168.1.0" | \
- grep "priority=30" | wc -l], [0], [dnl
-1
-])
-
-# Send packet.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_p1_mac && eth.dst==$ls1_ro_mac &&
- ip4 && ip.ttl==64 && ip4.src==$ls1_p1_ip && ip4.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-as pbr-hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-echo "southbound flows"
-
-ovn-sbctl dump-flows | grep lr_in_policy
-echo "ovs flows"
-ovs-ofctl dump-flows br-int
-# Check if packet hit the allow policy
-AT_CHECK([ovs-ofctl dump-flows br-int | \
- grep "nw_src=192.168.1.0/24,nw_dst=172.16.1.0/24" | \
- grep "priority=30" | \
- grep "n_packets=1" | wc -l], [0], [dnl
-1
-])
-echo "packet hit reroute policy"
-
-# Expected packet has TTL decreased by 1
-expected="eth.src==$ls3_ro_mac && eth.dst==$ls3_p1_mac &&
- ip4 && ip.ttl==63 && ip4.src==$ls1_p1_ip && ip4.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $expected | ovstest test-ovn expr-to-packets > 3.expected
-
-OVN_CHECK_PACKETS([pbr-hv/vif3-tx.pcap], [3.expected])
-
-OVN_CLEANUP([pbr-hv])
-AT_CLEANUP
-
-AT_SETUP([ovn -- policy-based routing IPv6: 1 HVs, 3 LSs, 1 lport/LS, 1 LR])
-AT_KEYWORDS([policy-based-routing])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it,
-# and has switch ls2 (172.16.1.0/24) connected to it.
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-ovn-nbctl ls-add ls3
-
-# Connect ls1 to R1
-ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 2001::1/64
-ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \
- type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\"
-
-# Connect ls2 to R1
-ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 2002::1/64
-ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \
- type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\"
-
-# Connect ls3 to R1
-ovn-nbctl lrp-add R1 ls3 00:00:00:01:02:f3 2003::1/64
-ovn-nbctl lsp-add ls3 rp-ls3 -- set Logical_Switch_Port rp-ls3 \
- type=router options:router-port=ls3 addresses=\"00:00:00:01:02:f3\"
-
-# Create logical port ls1-lp1 in ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 2001::2"
-
-# Create logical port ls2-lp1 in ls2
-ovn-nbctl lsp-add ls2 ls2-lp1 \
--- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 2002::2"
-
-# Create logical port ls3-lp1 in ls3
-ovn-nbctl lsp-add ls3 ls3-lp1 \
--- lsp-set-addresses ls3-lp1 "00:00:00:01:02:05 2003::2"
-
-# Create one hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add pbr-hv
-as pbr-hv
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-
-ovs-vsctl -- add-port br-int vif1 -- \
- set interface vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=pbr-hv/vif1-tx.pcap \
- options:rxq_pcap=pbr-hv/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif2 -- \
- set interface vif2 external-ids:iface-id=ls2-lp1 \
- options:tx_pcap=pbr-hv/vif2-tx.pcap \
- options:rxq_pcap=pbr-hv/vif2-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int vif3 -- \
- set interface vif3 external-ids:iface-id=ls3-lp1 \
- options:tx_pcap=pbr-hv/vif3-tx.pcap \
- options:rxq_pcap=pbr-hv/vif3-rx.pcap \
- ofport-request=1
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ls1_ro_mac=00:00:00:01:02:f1
-ls1_ro_ip=2001::1
-
-ls2_ro_mac=00:00:00:01:02:f2
-ls2_ro_ip=2002::1
-
-ls3_ro_mac=00:00:00:01:02:f3
-
-ls1_p1_mac=00:00:00:01:02:03
-ls1_p1_ip=2001::2
-
-ls2_p1_mac=00:00:00:01:02:04
-ls2_p1_ip=2002::2
-
-ls3_p1_mac=00:00:00:01:02:05
-
-# Create a drop policy
-ovn-nbctl lr-policy-add R1 10 "ip6.src==2001::/64 && ip6.dst==2002::/64" drop
-
-# Check logical flow
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | grep "2001" | wc -l], [0], [dnl
-1
-])
-
-# Send packet.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_p1_mac && eth.dst==$ls1_ro_mac &&
- ip6 && ip.ttl==64 && ip6.src==$ls1_p1_ip && ip6.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-
-as pbr-hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Check if packet hit the drop policy
-AT_CHECK([ovs-ofctl dump-flows br-int | \
- grep "ipv6_src=2001::/64,ipv6_dst=2002::/64 actions=drop" | \
- grep "priority=10" | \
- grep "n_packets=1" | wc -l], [0], [dnl
-1
-])
-
-# Expected to drop the packet.
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" pbr-hv/vif2-tx.pcap > vif2.packets
-rcvd_packet=`cat vif2.packets`
-AT_FAIL_IF([rcvd_packet = ""])
-
-# Override drop policy with allow
-ovn-nbctl lr-policy-add R1 20 "ip6.src==2001::/64 && ip6.dst==2002::/64" allow
-
-# Check logical flow
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | grep "2001" | wc -l], [0], [dnl
-2
-])
-
-# Send packet.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_p1_mac && eth.dst==$ls1_ro_mac &&
- ip6 && ip.ttl==64 && ip6.src==$ls1_p1_ip && ip6.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-as pbr-hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Check if packet hit the allow policy
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | \
- grep "2001" | \
- grep "priority=20" | wc -l], [0], [dnl
-1
-])
-
-# Expected packet has TTL decreased by 1
-expected="eth.src==$ls2_ro_mac && eth.dst==$ls2_p1_mac &&
- ip6 && ip.ttl==63 && ip6.src==$ls1_p1_ip && ip6.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $expected | ovstest test-ovn expr-to-packets > expected
-
-OVN_CHECK_PACKETS([pbr-hv/vif2-tx.pcap], [expected])
-
-# Override allow policy with reroute
-ovn-nbctl lr-policy-add R1 30 "ip6.src==2001::/64 && ip6.dst==2002::/64" reroute 2003::2
-
-# Check logical flow
-AT_CHECK([ovn-sbctl dump-flows | grep lr_in_policy | \
- grep "2001" | \
- grep "priority=30" | wc -l], [0], [dnl
-1
-])
-
-# Send packet.
-packet="inport==\"ls1-lp1\" && eth.src==$ls1_p1_mac && eth.dst==$ls1_ro_mac &&
- ip6 && ip.ttl==64 && ip6.src==$ls1_p1_ip && ip6.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-as pbr-hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-echo "southbound flows"
-
-ovn-sbctl dump-flows | grep lr_in_policy
-echo "ovs flows"
-ovs-ofctl dump-flows br-int
-# Check if packet hit the allow policy
-AT_CHECK([ovs-ofctl dump-flows br-int | \
- grep "ipv6_src=2001::/64,ipv6_dst=2002::/64" | \
- grep "priority=30" | \
- grep "n_packets=1" | wc -l], [0], [dnl
-1
-])
-echo "packet hit reroute policy"
-
-# Expected packet has TTL decreased by 1
-expected="eth.src==$ls3_ro_mac && eth.dst==$ls3_p1_mac &&
- ip6 && ip.ttl==63 && ip6.src==$ls1_p1_ip && ip6.dst==$ls2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $expected | ovstest test-ovn expr-to-packets > 3.expected
-
-OVN_CHECK_PACKETS([pbr-hv/vif3-tx.pcap], [3.expected])
-
-OVN_CLEANUP([pbr-hv])
-AT_CLEANUP
-
-# 1 hypervisor, 1 port
-# make sure that the port state is properly set to up and back down
-# when created and deleted.
-AT_SETUP([ovn -- port state up and down])
-ovn_start
-
-ovn-nbctl ls-add ls1
-ovn-nbctl lsp-add ls1 lp1
-ovn-nbctl lsp-set-addresses lp1 unknown
-
-net_add n1
-sim_add hv1
-as hv1 ovs-vsctl add-br br-phys
-as hv1 ovn_attach n1 br-phys 192.168.0.1
-
-as hv1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xup])
-
-as hv1 ovs-vsctl del-port br-int vif1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xdown])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-# 1 hypervisor, 1 port
-# make sure that the OF rules created to support a datapath are added/cleared
-# when logical switch is created and removed.
-AT_SETUP([ovn -- datapath rules added/removed])
-AT_KEYWORDS([cleanup])
-ovn_start
-
-net_add n1
-sim_add hv1
-as hv1 ovs-vsctl add-br br-phys
-as hv1 ovn_attach n1 br-phys 192.168.0.1
-
-# This shell function checks if OF rules in br-int have clauses
-# related to OVN datapaths. The caller determines if it should find
-# a match in the output, or not.
-#
-# EXPECT_DATAPATH param determines whether flows that refer to
-# datapath to should be present or not. 0 means
-# they should not be.
-# STAGE_INFO param is a simple string to help identify the stage
-# in the test when this function was invoked.
-test_datapath_in_of_rules() {
- local expect_datapath=$1 stage_info=$2
- echo "------ ovn-nbctl show ${stage_info} ------"
- ovn-nbctl show
- echo "------ ovn-sbctl show ${stage_info} ------"
- ovn-sbctl show
- echo "------ OF rules ${stage_info} ------"
- AT_CHECK([ovs-ofctl dump-flows br-int], [0], [stdout])
- # if there is a datapath mentioned in the output, check for the
- # magic keyword that represents one, based on the exit status of
- # a quiet grep
- if test $expect_datapath != 0; then
- AT_CHECK([grep -q -i 'metadata=' stdout], [0], [ignore-nolog])
- else
- AT_CHECK([grep -q -i 'metadata=' stdout], [1], [ignore-nolog])
- fi
-}
-
-test_datapath_in_of_rules 0 "before ls+port create"
-
-ovn-nbctl ls-add ls1
-ovn-nbctl lsp-add ls1 lp1
-ovn-nbctl lsp-set-addresses lp1 unknown
-
-as hv1 ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xup])
-
-test_datapath_in_of_rules 1 "after port is bound"
-
-as hv1 ovs-vsctl del-port br-int vif1
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xdown])
-
-ovn-nbctl lsp-set-addresses lp1
-ovn-nbctl lsp-del lp1
-ovn-nbctl ls-del ls1
-
-# wait for earlier changes to take effect
-AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])
-
-# ensure OF rules are no longer present. There used to be a bug here.
-test_datapath_in_of_rules 0 "after lport+ls removal"
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- nd_na ])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-#TODO: since patch port for IPv6 logical router port is not ready not,
-# so we are not going to test vifs on different lswitches cases. Try
-# to update for that once relevant stuff implemented.
-
-# In this test cases we create 1 lswitch, it has 2 VIF ports attached
-# with. NS packet we test, from one VIF for another VIF, will be replied
-# by local ovn-controller, but not by target VIF.
-
-# Create hypervisors and logical switch lsw0.
-ovn-nbctl ls-add lsw0
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-
-# Add vif1 to hv1 and lsw0, turn on l2 port security on vif1.
-ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=hv1/vif1-tx.pcap options:rxq_pcap=hv1/vif1-rx.pcap ofport-request=1
-ovn-nbctl lsp-add lsw0 lp1
-ovn-nbctl lsp-set-addresses lp1 "fa:16:3e:94:05:98 192.168.0.3 fd81:ce49:a948:0:f816:3eff:fe94:598"
-ovn-nbctl lsp-set-port-security lp1 "fa:16:3e:94:05:98 192.168.0.3 fd81:ce49:a948:0:f816:3eff:fe94:598"
-
-# Add vif2 to hv1 and lsw0, turn on l2 port security on vif2.
-ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=hv1/vif2-tx.pcap options:rxq_pcap=hv1/vif2-rx.pcap ofport-request=2
-ovn-nbctl lsp-add lsw0 lp2
-ovn-nbctl lsp-set-addresses lp2 "fa:16:3e:a1:f9:ae 192.168.0.4 fd81:ce49:a948:0:f816:3eff:fea1:f9ae"
-ovn-nbctl lsp-set-port-security lp2 "fa:16:3e:a1:f9:ae 192.168.0.4 fd81:ce49:a948:0:f816:3eff:fea1:f9ae"
-
-# Add ACL rule for ICMPv6 on lsw0
-ovn-nbctl acl-add lsw0 from-lport 1002 'ip6 && icmp6' allow-related
-ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp1" && ip6 && icmp6' allow-related
-ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp2" && ip6 && icmp6' allow-related
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- echo hv1${1%?}
-}
-for i in 1 2; do
- : > $i.expected
-done
-
-# Complete Neighbor Solicitation packet and Neighbor Advertisement packet
-# vif1 -> NS -> vif2. vif1 <- NA <- ovn-controller.
-# vif2 will not receive NS packet, since ovn-controller will reply for it.
-ns_packet=3333ffa1f9aefa163e94059886dd6000000000203afffd81ce49a9480000f8163efffe940598fd81ce49a9480000f8163efffea1f9ae8700e01160000000fd81ce49a9480000f8163efffea1f9ae0101fa163e940598
-na_packet=fa163e940598fa163ea1f9ae86dd6000000000203afffd81ce49a9480000f8163efffea1f9aefd81ce49a9480000f8163efffe9405988800e9ed60000000fd81ce49a9480000f8163efffea1f9ae0201fa163ea1f9ae
-
-as hv1 ovs-appctl netdev-dummy/receive vif1 $ns_packet
-echo $na_packet >> 1.expected
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 show br-int
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
-for i in 1 2; do
- OVN_CHECK_PACKETS([hv1/vif$i-tx.pcap], [$i.expected])
-done
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- address sets modification/removal smoke test])
-ovn_start
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-
-row=`ovn-nbctl create Address_Set name=set1 addresses=\"1.1.1.1\"`
-ovn-nbctl set Address_Set $row name=set1 addresses=\"1.1.1.1,1.1.1.2\"
-ovn-nbctl destroy Address_Set $row
-
-sleep 1
-
-# A bug previously existed in the address set support code
-# that caused ovn-controller to crash after an address set
-# was updated and then removed. This test case ensures
-# that ovn-controller is at least still running after
-# creating, updating, and deleting an address set.
-AT_CHECK([ovs-appctl -t ovn-controller version], [0], [ignore])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- ipam])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Add a port to a switch that does not have a subnet set, then set the
-# subnet which should result in an address being allocated for the port.
-ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="0a:00:00:00:00:00"
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 p0 -- lsp-set-addresses p0 dynamic
-ovn-nbctl --wait=sb add Logical-Switch sw0 other_config subnet=192.168.1.0/24
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p0 dynamic_addresses], [0],
- ["0a:00:00:a8:01:03 192.168.1.2"
-])
-
-# Add 9 more ports to sw0, addresses should all be unique.
-for n in `seq 1 9`; do
- ovn-nbctl --wait=sb lsp-add sw0 "p$n" -- lsp-set-addresses "p$n" dynamic
-done
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p1 dynamic_addresses], [0],
- ["0a:00:00:a8:01:04 192.168.1.3"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p2 dynamic_addresses], [0],
- ["0a:00:00:a8:01:05 192.168.1.4"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p3 dynamic_addresses], [0],
- ["0a:00:00:a8:01:06 192.168.1.5"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p4 dynamic_addresses], [0],
- ["0a:00:00:a8:01:07 192.168.1.6"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p5 dynamic_addresses], [0],
- ["0a:00:00:a8:01:08 192.168.1.7"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p6 dynamic_addresses], [0],
- ["0a:00:00:a8:01:09 192.168.1.8"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p7 dynamic_addresses], [0],
- ["0a:00:00:a8:01:0a 192.168.1.9"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p8 dynamic_addresses], [0],
- ["0a:00:00:a8:01:0b 192.168.1.10"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p9 dynamic_addresses], [0],
- ["0a:00:00:a8:01:0c 192.168.1.11"
-])
-
-# Trying similar tests with a second switch. MAC addresses should be unique
-# across both switches but IP's only need to be unique within the same switch.
-ovn-nbctl ls-add sw1
-ovn-nbctl lsp-add sw1 p10 -- lsp-set-addresses p10 dynamic
-ovn-nbctl --wait=sb add Logical-Switch sw1 other_config subnet=192.168.1.0/24
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p10 dynamic_addresses], [0],
- ["0a:00:00:a8:01:0d 192.168.1.2"
-])
-
-for n in `seq 11 19`; do
- ovn-nbctl --wait=sb lsp-add sw1 "p$n" -- lsp-set-addresses "p$n" dynamic
-done
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p11 dynamic_addresses], [0],
- ["0a:00:00:a8:01:0e 192.168.1.3"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p12 dynamic_addresses], [0],
- ["0a:00:00:a8:01:0f 192.168.1.4"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p13 dynamic_addresses], [0],
- ["0a:00:00:a8:01:10 192.168.1.5"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p14 dynamic_addresses], [0],
- ["0a:00:00:a8:01:11 192.168.1.6"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p15 dynamic_addresses], [0],
- ["0a:00:00:a8:01:12 192.168.1.7"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p16 dynamic_addresses], [0],
- ["0a:00:00:a8:01:13 192.168.1.8"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p17 dynamic_addresses], [0],
- ["0a:00:00:a8:01:14 192.168.1.9"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p18 dynamic_addresses], [0],
- ["0a:00:00:a8:01:15 192.168.1.10"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p19 dynamic_addresses], [0],
- ["0a:00:00:a8:01:16 192.168.1.11"
-])
-
-# Change a port's address to test for multiple ip's for a single address entry
-# and addresses set by the user.
-ovn-nbctl lsp-set-addresses p0 "0a:00:00:a8:01:17 192.168.1.2 192.168.1.12 192.168.1.14"
-ovn-nbctl --wait=sb lsp-add sw0 p20 -- lsp-set-addresses p20 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p20 dynamic_addresses], [0],
- ["0a:00:00:a8:01:18 192.168.1.13"
-])
-
-# Test for logical router port address management.
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw0 \
-network="192.168.1.1/24" mac=\"0a:00:00:a8:01:19\" \
--- add Logical_Router R1 ports @lrp -- lsp-add sw0 rp-sw0 \
--- set Logical_Switch_Port rp-sw0 type=router options:router-port=sw0
-ovn-nbctl --wait=sb lsp-add sw0 p21 -- lsp-set-addresses p21 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p21 dynamic_addresses], [0],
- ["0a:00:00:a8:01:1a 192.168.1.15"
-])
-
-# Test for address reuse after logical port is deleted.
-ovn-nbctl lsp-del p0
-ovn-nbctl --wait=sb lsp-add sw0 p23 -- lsp-set-addresses p23 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p23 dynamic_addresses], [0],
- ["0a:00:00:a8:01:03 192.168.1.2"
-])
-
-# Test for multiple addresses to one logical port.
-ovn-nbctl lsp-add sw0 p25 -- lsp-set-addresses p25 \
-"0a:00:00:a8:01:1b 192.168.1.12" "0a:00:00:a8:01:1c 192.168.1.14"
-ovn-nbctl --wait=sb lsp-add sw0 p26 -- lsp-set-addresses p26 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p26 dynamic_addresses], [0],
- ["0a:00:00:a8:01:17 192.168.1.16"
-])
-
-# Test for exhausting subnet address space.
-ovn-nbctl ls-add sw2 -- add Logical-Switch sw2 other_config subnet=172.16.1.0/30
-ovn-nbctl --wait=sb lsp-add sw2 p27 -- lsp-set-addresses p27 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p27 dynamic_addresses], [0],
- ["0a:00:00:10:01:03 172.16.1.2"
-])
-
-ovn-nbctl --wait=sb lsp-add sw2 p28 -- lsp-set-addresses p28 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p28 dynamic_addresses], [0],
- ["0a:00:00:00:00:01"
-])
-
-# Test that address management does not add duplicate MAC for lsp/lrp peers.
-ovn-nbctl create Logical_Router name=R2
-ovn-nbctl ls-add sw3
-ovn-nbctl lsp-add sw3 p29 -- lsp-set-addresses p29 \
-"0a:00:00:a8:01:18"
-ovn-nbctl -- --id=@lrp create Logical_Router_port name=sw3 \
-network="192.168.2.1/24" mac=\"0a:00:00:a8:01:18\" \
--- add Logical_Router R2 ports @lrp -- lsp-add sw3 rp-sw3 \
--- set Logical_Switch_Port rp-sw3 type=router options:router-port=sw3
-ovn-nbctl --wait=sb lsp-add sw0 p30 -- lsp-set-addresses p30 dynamic
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p30 dynamic_addresses], [0],
- ["0a:00:00:a8:01:1d 192.168.1.17"
-])
-
-# Test static MAC address with dynamically allocated IP
-ovn-nbctl --wait=sb lsp-add sw0 p31 -- lsp-set-addresses p31 \
-"fe:dc:ba:98:76:54 dynamic"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
- ["fe:dc:ba:98:76:54 192.168.1.18"
-])
-
-# Update the static MAC address with dynamically allocated IP and check
-# if the MAC address is updated in 'Logical_Switch_Port.dynamic_adddresses'
-ovn-nbctl --wait=sb lsp-set-addresses p31 "fe:dc:ba:98:76:55 dynamic"
-
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
- ["fe:dc:ba:98:76:55 192.168.1.18"
-])
-
-ovn-nbctl --wait=sb lsp-set-addresses p31 "dynamic"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
- ["0a:00:00:a8:01:1e 192.168.1.18"
-])
-
-ovn-nbctl --wait=sb lsp-set-addresses p31 "fe:dc:ba:98:76:56 dynamic"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p31 dynamic_addresses], [0],
- ["fe:dc:ba:98:76:56 192.168.1.18"
-])
-
-
-# Test the exclude_ips from the IPAM list
-ovn-nbctl --wait=sb set logical_switch sw0 \
-other_config:exclude_ips="192.168.1.19 192.168.1.21 192.168.1.23..192.168.1.50"
-
-ovn-nbctl --wait=sb lsp-add sw0 p32 -- lsp-set-addresses p32 \
-"dynamic"
-# 192.168.1.20 should be assigned as 192.168.1.19 is excluded.
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p32 dynamic_addresses], [0],
- ["0a:00:00:a8:01:1e 192.168.1.20"
-])
-
-ovn-nbctl --wait=sb lsp-add sw0 p33 -- lsp-set-addresses p33 \
-"dynamic"
-# 192.168.1.22 should be assigned as 192.168.1.21 is excluded.
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p33 dynamic_addresses], [0],
- ["0a:00:00:a8:01:1f 192.168.1.22"
-])
-
-ovn-nbctl --wait=sb lsp-add sw0 p34 -- lsp-set-addresses p34 \
-"dynamic"
-# 192.168.1.51 should be assigned as 192.168.1.23-192.168.1.50 is excluded.
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p34 dynamic_addresses], [0],
- ["0a:00:00:a8:01:34 192.168.1.51"
-])
-
-# Now clear the exclude_ips list. 192.168.1.19 should be assigned.
-ovn-nbctl --wait=sb set Logical-switch sw0 other_config:exclude_ips="invalid"
-ovn-nbctl --wait=sb lsp-add sw0 p35 -- lsp-set-addresses p35 \
-"dynamic"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p35 dynamic_addresses], [0],
- ["0a:00:00:a8:01:20 192.168.1.19"
-])
-
-# Set invalid data in exclude_ips list. It should be ignored.
-ovn-nbctl --wait=sb set Logical-switch sw0 other_config:exclude_ips="182.168.1.30"
-ovn-nbctl --wait=sb lsp-add sw0 p36 -- lsp-set-addresses p36 \
-"dynamic"
-# 192.168.1.21 should be assigned as that's the next free one.
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p36 dynamic_addresses], [0],
- ["0a:00:00:a8:01:21 192.168.1.21"
-])
-
-# Clear the dynamic addresses assignment request.
-ovn-nbctl --wait=sb clear logical_switch_port p36 addresses
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p36 dynamic_addresses], [0],
- [[[]]
-])
-
-# Set IPv6 prefix
-ovn-nbctl --wait=sb set Logical-switch sw0 other_config:ipv6_prefix="aef0::"
-ovn-nbctl --wait=sb lsp-add sw0 p37 -- lsp-set-addresses p37 \
-"dynamic"
-
-# With prefix aef0 and mac 0a:00:00:00:00:26, the dynamic IPv6 should be
-# - aef0::800:ff:fe00:26 (EUI64)
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p37 dynamic_addresses], [0],
- ["0a:00:00:a8:01:21 192.168.1.21 aef0::800:ff:fea8:121"
-])
-
-ovn-nbctl --wait=sb ls-add sw4
-ovn-nbctl --wait=sb set Logical-switch sw4 other_config:ipv6_prefix="bef0::" \
--- set Logical-switch sw4 other_config:subnet=192.168.2.0/30
-ovn-nbctl --wait=sb lsp-add sw4 p38 -- lsp-set-addresses p38 \
-"dynamic"
-
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p38 dynamic_addresses], [0],
- ["0a:00:00:a8:02:03 192.168.2.2 bef0::800:ff:fea8:203"
-])
-
-ovn-nbctl --wait=sb lsp-add sw4 p39 -- lsp-set-addresses p39 \
-"f0:00:00:00:10:12 dynamic"
-
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p39 dynamic_addresses], [0],
- ["f0:00:00:00:10:12 bef0::f200:ff:fe00:1012"
-])
-
-# Test the case where IPv4 addresses are exhausted and IPv6 prefix is set
-# p40 should not have an IPv4 address since the pool is exhausted
-ovn-nbctl --wait=sb lsp-add sw4 p40 -- lsp-set-addresses p40 \
-"dynamic"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p40 dynamic_addresses], [0],
- ["0a:00:00:00:00:02 bef0::800:ff:fe00:2"
-])
-
-# Test dynamic changes on switch ports.
-#
-ovn-nbctl --wait=sb ls-add sw5
-ovn-nbctl --wait=sb lsp-add sw5 p41 -- lsp-set-addresses p41 \
-"dynamic"
-# p41 will start with nothing
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- [[[]]
-])
-
-# Set a subnet. Now p41 should have an ipv4 address, too
-ovn-nbctl --wait=sb add Logical-Switch sw5 other_config subnet=192.168.1.0/24
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["0a:00:00:a8:01:22 192.168.1.2"
-])
-
-# Clear the other_config. The IPv4 address should be gone
-ovn-nbctl --wait=sb clear Logical-Switch sw5 other_config
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- [[[]]
-])
-
-# Set an IPv6 prefix. Now p41 should have an IPv6 address.
-ovn-nbctl --wait=sb set Logical-Switch sw5 other_config:ipv6_prefix="aef0::"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["0a:00:00:00:00:03 aef0::800:ff:fe00:3"
-])
-
-# Change the MAC address to a static one. The IPv6 address should update.
-ovn-nbctl --wait=sb lsp-set-addresses p41 "f0:00:00:00:10:2b dynamic"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["f0:00:00:00:10:2b aef0::f200:ff:fe00:102b"
-])
-
-# Change the IPv6 prefix. The IPv6 address should update.
-ovn-nbctl --wait=sb set Logical-Switch sw5 other_config:ipv6_prefix="bef0::"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["f0:00:00:00:10:2b bef0::f200:ff:fe00:102b"
-])
-
-# Clear the other_config. The IPv6 address should be gone
-ovn-nbctl --wait=sb clear Logical-Switch sw5 other_config
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- [[[]]
-])
-
-# Set the subnet again. Now p41 should get the IPv4 address again.
-ovn-nbctl --wait=sb add Logical-Switch sw5 other_config subnet=192.168.1.0/24
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["f0:00:00:00:10:2b 192.168.1.2"
-])
-
-# Add an excluded IP address that conflicts with p41. p41 should update.
-ovn-nbctl --wait=sb add Logical-Switch sw5 other_config \
-exclude_ips="192.168.1.2"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["f0:00:00:00:10:2b 192.168.1.3"
-])
-
-# Add static ip address
-ovn-nbctl --wait=sb lsp-set-addresses p41 "dynamic 192.168.1.100"
-ovn-nbctl list Logical-Switch-Port p41
-ovn-nbctl --wait=sb lsp-add sw5 p42 -- lsp-set-addresses p42 \
-"dynamic 192.168.1.101"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p41 dynamic_addresses], [0],
- ["0a:00:00:a8:01:65 192.168.1.100"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p42 dynamic_addresses], [0],
- ["0a:00:00:a8:01:66 192.168.1.101"
-])
-
-# define a mac address prefix
-ovn-nbctl ls-add sw6
-ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="00:11:22:33:44:55"
-ovn-nbctl --wait=sb set Logical-Switch sw6 other_config:subnet=192.168.100.0/24
-for n in $(seq 1 3); do
- ovn-nbctl --wait=sb lsp-add sw6 "p5$n" -- lsp-set-addresses "p5$n" dynamic
-done
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p51 dynamic_addresses], [0],
- ["00:11:22:a8:64:03 192.168.100.2"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p52 dynamic_addresses], [0],
- ["00:11:22:a8:64:04 192.168.100.3"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p53 dynamic_addresses], [0],
- ["00:11:22:a8:64:05 192.168.100.4"
-])
-
-# verify configuration order does not break IPAM/MACAM
-ovn-nbctl ls-add sw7
-for n in $(seq 1 3); do
- ovn-nbctl --wait=sb lsp-add sw7 "p7$n" -- lsp-set-addresses "p7$n" dynamic
-done
-ovn-nbctl --wait=sb set Logical-Switch sw7 other_config:ipv6_prefix="bef0::"
-p71_addr=$(ovn-nbctl get Logical-Switch-Port p71 dynamic_addresses)
-p72_addr=$(ovn-nbctl get Logical-Switch-Port p72 dynamic_addresses)
-p73_addr=$(ovn-nbctl get Logical-Switch-Port p73 dynamic_addresses)
-AT_CHECK([test "$p71_addr" != "$p72_addr"], [0], [])
-AT_CHECK([test "$p71_addr" != "$p73_addr"], [0], [])
-AT_CHECK([test "$p72_addr" != "$p73_addr"], [0], [])
-
-# request to assign mac only
-#
-ovn-nbctl ls-add sw8
-ovn-nbctl --wait=sb set Logical-Switch sw8 other_config:mac_only=true
-for n in $(seq 1 3); do
- ovn-nbctl --wait=sb lsp-add sw8 "p8$n" -- lsp-set-addresses "p8$n" dynamic
-done
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p81 dynamic_addresses], [0],
- ["00:11:22:00:00:06"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p82 dynamic_addresses], [0],
- ["00:11:22:00:00:07"
-])
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p83 dynamic_addresses], [0],
- ["00:11:22:00:00:08"
-])
-
-# clear mac_prefix and check it is allocated in a random manner
-ovn-nbctl --wait=hv remove NB_Global . options mac_prefix
-ovn-nbctl ls-add sw9
-ovn-nbctl --wait=sb set Logical-Switch sw9 other_config:mac_only=true
-ovn-nbctl --wait=sb lsp-add sw9 p91 -- lsp-set-addresses p91 dynamic
-
-mac_prefix=$(ovn-nbctl --wait=sb get NB_Global . options:mac_prefix | tr -d \")
-port_addr=$(ovn-nbctl get Logical-Switch-Port p91 dynamic_addresses | tr -d \")
-AT_CHECK([test "$port_addr" = "${mac_prefix}:00:00:09"], [0], [])
-
-ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="00:11:22"
-ovn-nbctl ls-add sw10
-ovn-nbctl --wait=sb set Logical-Switch sw10 other_config:ipv6_prefix="ae01::"
-ovn-nbctl --wait=sb lsp-add sw10 p101 -- lsp-set-addresses p101 "dynamic ae01::1"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p101 dynamic_addresses], [0],
- ["00:11:22:00:00:0a ae01::1"
-])
-
-ovn-nbctl --wait=sb set Logical-Switch sw10 other_config:subnet=192.168.110.0/24
-ovn-nbctl --wait=sb lsp-add sw10 p102 -- lsp-set-addresses p102 "dynamic 192.168.110.10 ae01::2"
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p102 dynamic_addresses], [0],
- ["00:11:22:a8:6e:0b 192.168.110.10 ae01::2"
-])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as northd-backup
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- ipam connectivity])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl lr-add R1
-
-# Test for a ping using dynamically allocated addresses.
-ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="0a:00:00:00:00:00"
-ovn-nbctl ls-add foo -- add Logical_Switch foo other_config subnet=192.168.1.0/24
-ovn-nbctl ls-add alice -- add Logical_Switch alice other_config subnet=192.168.2.0/24
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \
- options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect alice to R1
-ovn-nbctl lrp-add R1 alice 00:00:00:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice type=router \
- options:router-port=alice addresses=\"00:00:00:01:02:04\"
-
-# Create logical port foo1 in foo
-ovn-nbctl --wait=sb lsp-add foo foo1 \
--- lsp-set-addresses foo1 "dynamic"
-AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo1 dynamic_addresses='"0a:00:00:a8:01:03 192.168.1.2"'], [0])
-
-# Create logical port alice1 in alice
-ovn-nbctl --wait=sb lsp-add alice alice1 \
--- lsp-set-addresses alice1 "dynamic"
-AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port alice1 dynamic_addresses='"0a:00:00:a8:02:03 192.168.2.2"'])
-
-# Create logical port foo2 in foo
-ovn-nbctl --wait=sb lsp-add foo foo2 \
--- lsp-set-addresses foo2 "dynamic"
-AT_CHECK([ovn-nbctl --timeout=10 wait-until Logical-Switch-Port foo2 dynamic_addresses='"0a:00:00:a8:01:04 192.168.1.3"'])
-
-# Create a hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=foo2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-ovs-vsctl -- add-port br-int hv1-vif3 -- \
- set interface hv1-vif3 external-ids:iface-id=alice1 \
- options:tx_pcap=hv1/vif3-tx.pcap \
- options:rxq_pcap=hv1/vif3-rx.pcap \
- ofport-request=3
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send ip packets between foo1 and foo2
-src_mac="0a0000a80103"
-dst_mac="0a0000a80104"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 1 3`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-# Send ip packets between foo1 and alice1
-src_mac="0a0000a80103"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 2 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
-# Packet to Expect at foo2
-src_mac="0a0000a80103"
-dst_mac="0a0000a80104"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 1 3`
-expected=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > received1.packets
-echo $expected > expout
-AT_CHECK([cat received1.packets], [0], [expout])
-
-# Packet to Expect at alice1
-src_mac="000000010204"
-dst_mac="0a0000a80203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 2 2`
-expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > received2.packets
-echo $expected > expout
-AT_CHECK([cat received2.packets], [0], [expout])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- ovs-vswitchd restart])
-AT_KEYWORDS([vswitchd])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add ls1
-
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4"
-
-ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4"
-
-net_add n1
-sim_add hv1
-
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-OVN_POPULATE_ARP
-sleep 2
-
-as hv1 ovs-vsctl show
-
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-total_flows=`as hv1 ovs-ofctl dump-flows br-int | wc -l`
-
-echo "Total flows before vswitchd restart = " $total_flows
-
-# Code taken from ovs-save utility
-save_flows () {
- echo "ovs-ofctl add-flows br-int - << EOF" > restore_flows.sh
- as hv1 ovs-ofctl dump-flows "br-int" | sed -e '/NXST_FLOW/d' \
- -e 's/\(idle\|hard\)_age=[^,]*,//g' >> restore_flows.sh
- echo "EOF" >> restore_flows.sh
-}
-
-restart_vswitchd () {
- restore_flows=$1
-
- if test $restore_flows = true; then
- save_flows
- fi
-
- as hv1
- OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
-
- if test $restore_flows = true; then
- as hv1
- ovs-vsctl --no-wait set open_vswitch . other_config:flow-restore-wait="true"
- fi
-
- as hv1
- start_daemon ovs-vswitchd --enable-dummy=system -vvconn -vofproto_dpif -vunixctl
- ovs-ofctl dump-flows br-int
-
- if test $restore_flows = true; then
- sh ./restore_flows.sh
- echo "Flows after restore"
- as hv1
- ovs-ofctl dump-flows br-int
- ovs-vsctl --no-wait --if-exists remove open_vswitch . other_config \
- flow-restore-wait="true"
- fi
-}
-
-# Save the flows, restart vswitchd and restore the flows
-restart_vswitchd true
-OVS_WAIT_UNTIL([
- total_flows_after_restart=`as hv1 ovs-ofctl dump-flows br-int | wc -l`
- echo "Total flows after vswitchd restart = " $total_flows_after_restart
- test "${total_flows}" = "${total_flows_after_restart}"
-])
-
-# Restart vswitchd without restoring
-restart_vswitchd false
-OVS_WAIT_UNTIL([
- total_flows_after_restart=`as hv1 ovs-ofctl dump-flows br-int | wc -l`
- echo "Total flows after vswitchd restart = " $total_flows_after_restart
- test "${total_flows}" = "${total_flows_after_restart}"
-])
-
-OVN_CLEANUP([hv1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- send arp for nexthop])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Topology: Two LSs - ls1 and ls2 are connected via router r0
-
-# Create logical switches
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-
-# Create router
-ovn-nbctl create Logical_Router name=lr0
-
-# Add router ls1p1 port to gateway router
-ovn-nbctl lrp-add lr0 lrp-ls1lp1 f0:00:00:00:00:01 192.168.0.1/24
-ovn-nbctl lsp-add ls1 ls1lp1 -- set Logical_Switch_Port ls1lp1 \
- type=router options:router-port=lrp-ls1lp1 \
- addresses='"f0:00:00:00:00:01 192.168.0.1"'
-
-# Add router ls2p2 port to gateway router
-ovn-nbctl lrp-add lr0 lrp-ls2lp1 f0:00:00:00:00:02 192.168.1.1/24
-ovn-nbctl lsp-add ls2 ls2lp1 -- set Logical_Switch_Port ls2lp1 \
- type=router options:router-port=lrp-ls2lp1 \
- addresses='"f0:00:00:00:00:02 192.168.1.1"'
-
-# Set default gateway (nexthop) to 192.168.1.254
-ovn-nbctl lr-route-add lr0 "0.0.0.0/0" 192.168.1.254 lrp-ls2lp1
-
-# Create logical port ls1lp2 in ls1
-ovn-nbctl lsp-add ls1 ls1lp2 \
--- lsp-set-addresses ls1lp2 "f0:00:00:00:00:03 192.168.0.2"
-
-# Create logical port ls2lp2 in ls2
-ovn-nbctl lsp-add ls2 ls2lp2 \
--- lsp-set-addresses ls2lp2 "f0:00:00:00:00:04 192.168.1.10"
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-ls1lp2 -- \
- set interface hv1-ls1lp2 external-ids:iface-id=ls1lp2 \
- options:tx_pcap=hv1/ls1lp2-tx.pcap \
- options:rxq_pcap=hv1/ls1lp2-rx.pcap \
- ofport-request=1
-ovs-vsctl -- add-port br-int hv1-ls2lp2 -- \
- set interface hv1-ls2lp2 external-ids:iface-id=ls2lp2 \
- options:tx_pcap=hv1/ls2lp2-tx.pcap \
- options:rxq_pcap=hv1/ls2lp2-rx.pcap \
- ofport-request=2
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-ovn-sbctl list encap
-echo "---------------------"
-
-echo "------Flows dump-----"
-as hv1
-ovs-ofctl dump-flows
-echo "---------------------"
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-src_mac="f00000000003"
-dst_mac="f00000000001"
-src_ip=`ip_to_hex 192 168 0 2`
-dst_ip=`ip_to_hex 8 8 8 8`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-# Send IP packet destined to 8.8.8.8 from lsp1lp2
-as hv1 ovs-appctl netdev-dummy/receive hv1-ls1lp2 $packet
-
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-# ARP packet should be received with Target IP Address set to 192.168.1.254 and
-# not 8.8.8.8
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ls2lp2-tx.pcap | trim_zeros > packets
-expected="fffffffffffff0000000000208060001080006040001f00000000002c0a80101000000000000c0a801fe"
-echo $expected > expout
-AT_CHECK([cat packets], [0], [expout])
-cat packets
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- send gratuitous arp for nat ips in localnet])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-# Create logical switch
-ovn-nbctl ls-add ls0
-# Create gateway router
-ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
-# Add router port to gateway router
-ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24
-ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \
- type=router options:router-port=lrp0 addresses='"f0:00:00:00:00:01"'
-# Add nat-address option
-ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="f0:00:00:00:00:01 192.168.0.2"
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0
-
-ovn_attach n1 br-phys 192.168.0.1
-
-AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
-AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
-
-# Create a localnet port.
-AT_CHECK([ovn-nbctl lsp-add ls0 ln_port])
-AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
-AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
-AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
-
-# Wait until the patch ports are created in hv1 to connect br-int to br-eth0
-OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-vsctl show | \
-grep "Port patch-br-int-to-ln_port" | wc -l`])
-
-# Wait for packet to be received.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 50])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001"
-echo $expected > expout
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002"
-echo $expected >> expout
-AT_CHECK([sort packets], [0], [expout])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- send gratuitous arp with nat-addresses router in localnet])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-# Create logical switch
-ovn-nbctl ls-add ls0
-# Create gateway router
-ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
-# Add router port to gateway router
-ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24
-ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \
- type=router options:router-port=lrp0 addresses='"f0:00:00:00:00:01"'
-# Add nat-address option
-ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
-# Add NAT rules
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.1 10.0.0.0/24])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 192.168.0.2 10.0.0.1])
-# Add load balancers
-AT_CHECK([ovn-nbctl lb-add lb0 192.168.0.3:80 10.0.0.2:80,10.0.0.3:80])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
-AT_CHECK([ovn-nbctl lb-add lb1 192.168.0.3:8080 10.0.0.2:8080,10.0.0.3:8080])
-AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl \
- -- add-br br-phys \
- -- add-br br-eth0
-
-ovn_attach n1 br-phys 192.168.0.1
-
-AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
-AT_CHECK([ovs-vsctl add-port br-eth0 snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
-
-# Create a localnet port.
-AT_CHECK([ovn-nbctl lsp-add ls0 ln_port])
-AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
-AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
-AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
-
-# Wait until the patch ports are created to connect br-int to br-eth0
-OVS_WAIT_UNTIL([test 1 = `ovs-vsctl show | \
-grep "Port patch-br-int-to-ln_port" | wc -l`])
-
-ovn-sbctl list port_binding lrp0-rp
-echo "*****"
-ovn-nbctl list logical_switch_port lrp0-rp
-ovn-nbctl list logical_router_port lrp0
-ovn-nbctl show
-# Wait for packet to be received.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 50])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001"
-echo $expected > expout
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002"
-echo $expected >> expout
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80003000000000000c0a80003"
-echo $expected >> expout
-AT_CHECK([sort packets], [0], [expout])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- delete mac bindings])
-ovn_start
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl -- add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-# Create logical switch ls0
-ovn-nbctl ls-add ls0
-# Create ports lp0, lp1 in ls0
-ovn-nbctl lsp-add ls0 lp0
-ovn-nbctl lsp-add ls0 lp1
-ovn-nbctl lsp-set-addresses lp0 "f0:00:00:00:00:01 192.168.0.1"
-ovn-nbctl lsp-set-addresses lp1 "f0:00:00:00:00:02 192.168.0.2"
-dp_uuid=`ovn-sbctl find datapath | grep uuid | cut -f2 -d ":" | cut -f2 -d " "`
-ovn-sbctl create MAC_Binding ip=10.0.0.1 datapath=$dp_uuid logical_port=lp0 mac="mac1"
-ovn-sbctl create MAC_Binding ip=10.0.0.1 datapath=$dp_uuid logical_port=lp1 mac="mac2"
-ovn-sbctl find MAC_Binding
-# Delete port lp0 and check that its MAC_Binding is deleted.
-ovn-nbctl lsp-del lp0
-ovn-sbctl find MAC_Binding
-OVS_WAIT_UNTIL([test `ovn-sbctl find MAC_Binding logical_port=lp0 | wc -l` = 0])
-# Delete logical switch ls0 and check that its MAC_Binding is deleted.
-ovn-nbctl ls-del ls0
-ovn-sbctl find MAC_Binding
-OVS_WAIT_UNTIL([test `ovn-sbctl find MAC_Binding | wc -l` = 0])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- conntrack zone allocation])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# 2 logical switches "foo" (192.168.1.0/24) and "bar" (172.16.1.0/24)
-# connected to a router R1.
-# foo has foo1 to act as a client.
-# bar has bar1, bar2, bar3 to act as servers.
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-for i in foo1 bar1 bar2 bar3; do
- ovs-vsctl -- add-port br-int $i -- \
- set interface $i external-ids:iface-id=$i \
- options:tx_pcap=hv1/$i-tx.pcap \
- options:rxq_pcap=hv1/$i-rx.pcap
-done
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 172.16.1.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port bar1, bar2 and bar3 in bar
-for i in `seq 1 3`; do
- ip=`expr $i + 1`
- ovn-nbctl lsp-add bar bar$i \
- -- lsp-set-addresses bar$i "f0:00:0a:01:02:$i 172.16.1.$ip"
-done
-
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=0 | grep REG13 | wc -l` -eq 4])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- tag allocation])
-ovn_start
-
-AT_CHECK([ovn-nbctl ls-add ls0])
-AT_CHECK([ovn-nbctl lsp-add ls0 parent1])
-AT_CHECK([ovn-nbctl lsp-add ls0 parent2])
-AT_CHECK([ovn-nbctl ls-add ls1])
-
-dnl When a tag is provided, no allocation is done
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c0 parent1 3])
-AT_CHECK([ovn-nbctl lsp-get-tag c0], [0], [3
-])
-dnl The same 'tag' gets created in southbound database.
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c0"], [0], [3
-])
-
-dnl Allocate tags and see it getting created in both NB and SB
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c1 parent1 0])
-AT_CHECK([ovn-nbctl lsp-get-tag c1], [0], [1
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c1"], [0], [1
-])
-
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c2 parent1 0])
-AT_CHECK([ovn-nbctl lsp-get-tag c2], [0], [2
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c2"], [0], [2
-])
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c3 parent1 0])
-AT_CHECK([ovn-nbctl lsp-get-tag c3], [0], [4
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c3"], [0], [4
-])
-
-dnl A different parent.
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c4 parent2 0])
-AT_CHECK([ovn-nbctl lsp-get-tag c4], [0], [1
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c4"], [0], [1
-])
-
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c5 parent2 0])
-AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c5"], [0], [2
-])
-
-dnl Delete a logical port and create a new one.
-AT_CHECK([ovn-nbctl --wait=sb lsp-del c1])
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c6 parent1 0])
-AT_CHECK([ovn-nbctl lsp-get-tag c6], [0], [1
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c6"], [0], [1
-])
-
-dnl Restart northd to see that the same allocation remains.
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-start_daemon ovn-northd \
- --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock \
- --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
-
-dnl Create a switch to make sure that ovn-northd has run through the main loop.
-AT_CHECK([ovn-nbctl --wait=sb ls-add ls-dummy])
-AT_CHECK([ovn-nbctl lsp-get-tag c0], [0], [3
-])
-AT_CHECK([ovn-nbctl lsp-get-tag c6], [0], [1
-])
-AT_CHECK([ovn-nbctl lsp-get-tag c2], [0], [2
-])
-AT_CHECK([ovn-nbctl lsp-get-tag c3], [0], [4
-])
-AT_CHECK([ovn-nbctl lsp-get-tag c4], [0], [1
-])
-AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2
-])
-
-dnl Create a switch port with a tag that has already been allocated.
-dnl It should go through fine with a duplicate tag.
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c7 parent2 2])
-AT_CHECK([ovn-nbctl lsp-get-tag c7], [0], [2
-])
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="c7"], [0], [2
-])
-AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2
-])
-
-AT_CHECK([ovn-nbctl ls-add ls2])
-dnl When there is no parent_name provided (for say, 'localnet'), 'tag_request'
-dnl gets copied to 'tag'
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls2 local0 "" 25])
-AT_CHECK([ovn-nbctl lsp-get-tag local0], [0], [25
-])
-dnl The same 'tag' gets created in southbound database.
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
-logical_port="local0"], [0], [25
-])
-dnl If 'tag_request' is 0 for localnet, nothing gets written to 'tag'
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls2 local1 "" 0])
-AT_CHECK([ovn-nbctl lsp-get-tag local1])
-dnl change the tag_request.
-AT_CHECK([ovn-nbctl --wait=sb set logical_switch_port local1 tag_request=50])
-AT_CHECK([ovn-nbctl lsp-get-tag local1], [0], [50
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- lsp deletion and broadcast-flow deletion on localnet])
-ovn_start
-ovn-nbctl ls-add lsw0
-net_add n1
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
- ovs-vsctl add-br br-eth0
- AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-eth0])
-done
-
-# Create a localnet port.
-AT_CHECK([ovn-nbctl lsp-add lsw0 ln_port])
-AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
-AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
-AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
-
-
-# Create 3 vifs.
-AT_CHECK([ovn-nbctl lsp-add lsw0 localvif1])
-AT_CHECK([ovn-nbctl lsp-set-addresses localvif1 "f0:00:00:00:00:01 192.168.1.1"])
-AT_CHECK([ovn-nbctl lsp-set-port-security localvif1 "f0:00:00:00:00:01"])
-AT_CHECK([ovn-nbctl lsp-add lsw0 localvif2])
-AT_CHECK([ovn-nbctl lsp-set-addresses localvif2 "f0:00:00:00:00:02 192.168.1.2"])
-AT_CHECK([ovn-nbctl lsp-set-port-security localvif2 "f0:00:00:00:00:02"])
-AT_CHECK([ovn-nbctl lsp-add lsw0 localvif3])
-AT_CHECK([ovn-nbctl lsp-set-addresses localvif3 "f0:00:00:00:00:03 192.168.1.3"])
-AT_CHECK([ovn-nbctl lsp-set-port-security localvif3 "f0:00:00:00:00:03"])
-
-# Bind the localvif1 to hv1.
-as hv1
-AT_CHECK([ovs-vsctl add-port br-int localvif1 -- set Interface localvif1 external_ids:iface-id=localvif1])
-
-# On hv1, check that there are no flows outputting bcast to tunnel
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0])
-
-# On hv2, check that no flow outputs bcast to tunnel to hv1.
-as hv2
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0])
-
-# Now bind vif2 on hv2.
-AT_CHECK([ovs-vsctl add-port br-int localvif2 -- set Interface localvif2 external_ids:iface-id=localvif2])
-
-# At this point, the broadcast flow on vif2 should be deleted.
-# because, there is now a localnet vif bound (table=32 programming logic)
-OVS_WAIT_UNTIL([test `ovs-ofctl dump-flows br-int table=32 | ofctl_strip | grep output | wc -l` -eq 0])
-
-# Verify that the local net patch port exists on hv2.
-OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1])
-
-# Now bind vif3 on hv2.
-AT_CHECK([ovs-vsctl add-port br-int localvif3 -- set Interface localvif3 external_ids:iface-id=localvif3])
-
-# Verify that the local net patch port still exists on hv2
-OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1])
-
-# Delete localvif2
-AT_CHECK([ovn-nbctl lsp-del localvif2])
-
-# Verify that the local net patch port still exists on hv2,
-# because, localvif3 is still bound.
-OVS_WAIT_UNTIL([test `ovs-vsctl show | grep "Port patch-br-int-to-ln_port" | wc -l` -eq 1])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-
-AT_SETUP([ovn -- ACL logging])
-AT_KEYWORDS([ovn])
-ovn_start
-
-net_add n1
-
-sim_add hv
-as hv
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-for i in lp1 lp2; do
- ovs-vsctl -- add-port br-int $i -- \
- set interface $i external-ids:iface-id=$i \
- options:tx_pcap=hv/$i-tx.pcap \
- options:rxq_pcap=hv/$i-rx.pcap
-done
-
-lp1_mac="f0:00:00:00:00:01"
-lp1_ip="192.168.1.2"
-
-lp2_mac="f0:00:00:00:00:02"
-lp2_ip="192.168.1.3"
-
-ovn-nbctl ls-add lsw0
-ovn-nbctl --wait=sb lsp-add lsw0 lp1
-ovn-nbctl --wait=sb lsp-add lsw0 lp2
-ovn-nbctl lsp-set-addresses lp1 $lp1_mac
-ovn-nbctl lsp-set-addresses lp2 $lp2_mac
-ovn-nbctl --wait=sb sync
-
-ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==80' drop
-ovn-nbctl --log --severity=alert --name=drop-flow acl-add lsw0 to-lport 1000 'tcp.dst==81' drop
-
-ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==82' allow
-ovn-nbctl --log --severity=info --name=allow-flow acl-add lsw0 to-lport 1000 'tcp.dst==83' allow
-
-ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==84' allow-related
-ovn-nbctl --log acl-add lsw0 to-lport 1000 'tcp.dst==85' allow-related
-
-ovn-nbctl acl-add lsw0 to-lport 1000 'tcp.dst==86' reject
-ovn-nbctl --wait=hv --log --severity=alert --name=reject-flow acl-add lsw0 to-lport 1000 'tcp.dst==87' reject
-
-ovn-sbctl dump-flows
-
-
-# Send packet that should be dropped without logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4360 && tcp.dst==80"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should be dropped with logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4361 && tcp.dst==81"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should be allowed without logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4362 && tcp.dst==82"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should be allowed with logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4363 && tcp.dst==83"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should allow related flows without logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4364 && tcp.dst==84"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should allow related flows with logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4365 && tcp.dst==85"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should be rejected without logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4366 && tcp.dst==86"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Send packet that should be rejected with logging.
-packet="inport==\"lp1\" && eth.src==$lp1_mac && eth.dst==$lp2_mac &&
- ip4 && ip.ttl==64 && ip4.src==$lp1_ip && ip4.dst==$lp2_ip &&
- tcp && tcp.flags==2 && tcp.src==4367 && tcp.dst==87"
-as hv ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-OVS_WAIT_UNTIL([ test 4 = $(grep -c 'acl_log' hv/ovn-controller.log) ])
-
-AT_CHECK([grep 'acl_log' hv/ovn-controller.log | sed 's/.*name=/name=/'], [0], [dnl
-name="drop-flow", verdict=drop, severity=alert: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4361,tp_dst=81,tcp_flags=syn
-name="allow-flow", verdict=allow, severity=info: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4363,tp_dst=83,tcp_flags=syn
-name="<unnamed>", verdict=allow, severity=info: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4365,tp_dst=85,tcp_flags=syn
-name="reject-flow", verdict=reject, severity=alert: tcp,vlan_tci=0x0000,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,nw_src=192.168.1.2,nw_dst=192.168.1.3,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=4367,tp_dst=87,tcp_flags=syn
-])
-
-OVN_CLEANUP([hv])
-AT_CLEANUP
-
-
-AT_SETUP([ovn -- ACL rate-limited logging])
-AT_KEYWORDS([ovn])
-ovn_start
-
-net_add n1
-
-sim_add hv
-as hv
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-for i in lp1 lp2; do
- ovs-vsctl -- add-port br-int $i -- \
- set interface $i external-ids:iface-id=$i \
- options:tx_pcap=hv/$i-tx.pcap \
- options:rxq_pcap=hv/$i-rx.pcap
-done
-
-lp1_mac="f0:00:00:00:00:01"
-lp1_ip="192.168.1.2"
-
-lp2_mac="f0:00:00:00:00:02"
-lp2_ip="192.168.1.3"
-
-ovn-nbctl ls-add lsw0
-ovn-nbctl --wait=sb lsp-add lsw0 lp1
-ovn-nbctl --wait=sb lsp-add lsw0 lp2
-ovn-nbctl lsp-set-addresses lp1 $lp1_mac
-ovn-nbctl lsp-set-addresses lp2 $lp2_mac
-ovn-nbctl --wait=sb sync
-
-
-# Add an ACL that rate-limits logs at 10 per second.
-ovn-nbctl meter-add http-rl1 drop 10 pktps
-ovn-nbctl --log --severity=alert --meter=http-rl1 --name=http-acl1 acl-add lsw0 to-lport 1000 'tcp.dst==80' drop
-
-# Add an ACL that rate-limits logs at 5 per second.
-ovn-nbctl meter-add http-rl2 drop 5 pktps
-ovn-nbctl --log --severity=alert --meter=http-rl2 --name=http-acl2 acl-add lsw0 to-lport 1000 'tcp.dst==81' allow
-
-# Add an ACL that doesn't rate-limit logs.
-ovn-nbctl --log --severity=alert --name=http-acl3 acl-add lsw0 to-lport 1000 'tcp.dst==82' drop
-ovn-nbctl --wait=hv sync
-
-# For each ACL, send 100 packets.
-for i in `seq 1 100`; do
- ovs-appctl netdev-dummy/receive lp1 'in_port(1),eth(src=f0:00:00:00:00:01,dst=f0:00:00:00:00:02),eth_type(0x0800),ipv4(src=192.168.1.2,dst=192.168.1.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=7777,dst=80)'
-
- ovs-appctl netdev-dummy/receive lp1 'in_port(1),eth(src=f0:00:00:00:00:01,dst=f0:00:00:00:00:02),eth_type(0x0800),ipv4(src=192.168.1.2,dst=192.168.1.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=7777,dst=81)'
-
- ovs-appctl netdev-dummy/receive lp1 'in_port(1),eth(src=f0:00:00:00:00:01,dst=f0:00:00:00:00:02),eth_type(0x0800),ipv4(src=192.168.1.2,dst=192.168.1.3,proto=6,tos=0,ttl=64,frag=no),tcp(src=7777,dst=82)'
-done
-
-# The rate at which packets are sent is highly system-dependent, so we
-# can't count on precise drop counts. To work around that, we just
-# check that exactly 100 "http-acl3" actions were logged and that there
-# were more "http-acl1" actions than "http-acl2" ones.
-OVS_WAIT_UNTIL([ test 100 = $(grep -c 'http-acl3' hv/ovn-controller.log) ])
-
-# On particularly slow or overloaded systems, the transmission rate may
-# be lower than the configured meter rate. To prevent false test
-# failures, we check the duration count of the meter, and if it's
-# greater than nine seconds, just skip the test.
-d_secs=$(as hv ovs-ofctl -O OpenFlow13 meter-stats br-int | grep "meter:1" | sed 's/.* duration:\([[0-9]]\{1,\}\)\.[[0-9]]\+s .*/\1/')
-
-echo "Meter duration: $d_secs"
-AT_SKIP_IF([test $d_secs -gt 9])
-
-# Print some information that may help debugging.
-as hv ovs-appctl -t ovn-controller meter-table-list
-as hv ovs-ofctl -O OpenFlow13 meter-stats br-int
-
-n_acl1=$(grep -c 'http-acl1' hv/ovn-controller.log)
-n_acl2=$(grep -c 'http-acl2' hv/ovn-controller.log)
-n_acl3=$(grep -c 'http-acl3' hv/ovn-controller.log)
-
-AT_CHECK([ test $n_acl3 -gt $n_acl1 ], [0], [])
-AT_CHECK([ test $n_acl1 -gt $n_acl2 ], [0], [])
-
-OVN_CLEANUP([hv])
-AT_CLEANUP
-
-
-AT_SETUP([ovn -- DSCP marking and meter check])
-AT_KEYWORDS([ovn])
-ovn_start
-
-ovn-nbctl ls-add lsw0
-ovn-nbctl --wait=sb lsp-add lsw0 lp1
-ovn-nbctl --wait=sb lsp-add lsw0 lp2
-ovn-nbctl --wait=sb lsp-add lsw0 lp3
-ovn-nbctl lsp-set-addresses lp1 f0:00:00:00:00:01
-ovn-nbctl lsp-set-addresses lp2 f0:00:00:00:00:02
-ovn-nbctl lsp-set-addresses lp3 f0:00:00:00:00:03
-ovn-nbctl lsp-set-port-security lp1 f0:00:00:00:00:01
-ovn-nbctl lsp-set-port-security lp2 f0:00:00:00:00:02
-ovn-nbctl --wait=sb sync
-net_add n1
-sim_add hv
-as hv
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1 options:tx_pcap=vif1-tx.pcap options:rxq_pcap=vif1-rx.pcap ofport-request=1
-ovs-vsctl add-port br-int vif2 -- set Interface vif2 external-ids:iface-id=lp2 options:tx_pcap=vif2-tx.pcap options:rxq_pcap=vif2-rx.pcap ofport-request=2
-
-AT_CAPTURE_FILE([trace])
-ovn_trace () {
- ovn-trace --all "$@" | tee trace | sed '1,/Minimal trace/d'
-}
-
-# Extracts nw_tos from the final flow from ofproto/trace output and prints
-# it on stdout. Prints "none" if no nw_tos was included.
-get_final_nw_tos() {
- if flow=$(grep '^Final flow:' stdout); then :; else
- # The output didn't have a final flow.
- return 99
- fi
-
- tos=$(echo "$flow" | sed -n 's/.*nw_tos=\([[0-9]]\{1,\}\).*/\1/p')
- case $tos in
- '') echo none ;;
- *) echo $tos ;;
- esac
-}
-
-# check_tos TOS
-#
-# Checks that a packet from 1.1.1.1 to 1.1.1.2 gets its DSCP set to TOS.
-check_tos() {
- # First check with ovn-trace for logical flows.
- echo "checking for tos $1"
- (if test $1 != 0; then echo "ip.dscp = $1;"; fi;
- echo 'output("lp2");') > expout
- AT_CHECK_UNQUOTED([ovn_trace lsw0 'inport == "lp1" && eth.src == f0:00:00:00:00:01 && eth.dst == f0:00:00:00:00:02 && ip4.src == 1.1.1.1 && ip4.dst == 1.1.1.2'], [0], [expout])
-
- # Then re-check with ofproto/trace for a physical packet.
- AT_CHECK([ovs-appctl ofproto/trace br-int 'in_port=1,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02,dl_type=0x800,nw_src=1.1.1.1,nw_dst=1.1.1.2'], [0], [stdout-nolog])
- AT_CHECK_UNQUOTED([get_final_nw_tos], [0], [`expr $1 \* 4`
-])
-}
-
-# check at L2
-AT_CHECK([ovn_trace lsw0 'inport == "lp1" && eth.src == f0:00:00:00:00:01 && eth.dst == f0:00:00:00:00:02'], [0], [output("lp2");
-])
-AT_CHECK([ovs-appctl ofproto/trace br-int 'in_port=1,dl_src=f0:00:00:00:00:01,dl_dst=f0:00:00:00:00:02'], [0], [stdout-nolog])
-AT_CHECK([get_final_nw_tos], [0], [none
-])
-
-# check at L3 without dscp marking
-check_tos 0
-
-# Mark DSCP with a valid value
-qos_id=$(ovn-nbctl --wait=hv -- --id=@lp1-qos create QoS priority=100 action=dscp=48 match="inport\=\=\"lp1\"\ &&\ is_chassis_resident(\"lp1\")" direction="from-lport" -- set Logical_Switch lsw0 qos_rules=@lp1-qos)
-AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [1
-])
-check_tos 48
-
-# check at hv without qos meter
-AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l], [0], [0
-])
-
-# Update the meter rate
-ovn-nbctl --wait=hv set QoS $qos_id bandwidth=rate=100
-
-# check at hv with a qos meter table
-AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep rate=100 | wc -l], [0], [1
-])
-AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l], [0], [1
-])
-
-# Update the DSCP marking
-ovn-nbctl --wait=hv set QoS $qos_id action=dscp=63
-check_tos 63
-
-# Update the meter rate
-ovn-nbctl --wait=hv set QoS $qos_id bandwidth=rate=4294967295,burst=4294967295
-
-# check at hv with a qos meter table
-AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep burst_size=4294967295 | wc -l], [0], [1
-])
-AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l], [0], [1
-])
-
-ovn-nbctl --wait=hv set QoS $qos_id match="outport\=\=\"lp2\"" direction="to-lport"
-check_tos 63
-
-# Disable DSCP marking
-ovn-nbctl --wait=hv qos-del lsw0
-AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [0
-])
-check_tos 0
-
-# check at hv without qos meter
-AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l], [0], [0
-])
-
-# check meter with chassis not resident
-ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp3" && is_chassis_resident("lp3")' rate=11123 burst=111230
-AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [1
-])
-
-# check no meter table
-AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l], [0], [0
-])
-AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep rate=11123 | wc -l], [0], [0
-])
-
-OVN_CLEANUP([hv])
-AT_CLEANUP
-
-AT_SETUP([ovn -- read-only sb db:ptcp access])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-
-: > .$1.db.~lock~
-ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn/ovn-sb.ovsschema
-
-# Add read-only remote to sb ovsdb-server
-AT_CHECK(
- [ovsdb-tool transact ovn-sb.db \
- ['["OVN_Southbound",
- {"op": "insert",
- "table": "SB_Global",
- "row": {
- "connections": ["set", [["named-uuid", "xyz"]]]}},
- {"op": "insert",
- "table": "Connection",
- "uuid-name": "xyz",
- "row": {"target": "ptcp:0:127.0.0.1",
- "read_only": true}}]']], [0], [ignore], [ignore])
-
-start_daemon ovsdb-server --remote=punix:ovn-sb.sock --remote=db:OVN_Southbound,SB_Global,connections ovn-sb.db
-
-PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
-
-# read-only accesses should succeed
-AT_CHECK([ovn-sbctl --db=tcp:127.0.0.1:$TCP_PORT list SB_Global], [0], [stdout], [ignore])
-AT_CHECK([ovn-sbctl --db=tcp:127.0.0.1:$TCP_PORT list Connection], [0], [stdout], [ignore])
-
-# write access should fail
-AT_CHECK([ovn-sbctl --db=tcp:127.0.0.1:$TCP_PORT chassis-add ch vxlan 1.2.4.8], [1], [ignore],
-[ovn-sbctl: transaction error: {"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}
-])
-
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-AT_CLEANUP
-
-AT_SETUP([ovn -- read-only sb db:pssl access])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
-PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
-AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\"
-\\]"])
-
-: > .$1.db.~lock~
-ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn/ovn-sb.ovsschema
-
-# Add read-only remote to sb ovsdb-server
-AT_CHECK(
- [ovsdb-tool transact ovn-sb.db \
- ['["OVN_Southbound",
- {"op": "insert",
- "table": "SB_Global",
- "row": {
- "connections": ["set", [["named-uuid", "xyz"]]]}},
- {"op": "insert",
- "table": "Connection",
- "uuid-name": "xyz",
- "row": {"target": "pssl:0:127.0.0.1",
- "read_only": true}}]']], [0], [ignore], [ignore])
-
-start_daemon ovsdb-server --remote=punix:ovn-sb.sock \
- --remote=db:OVN_Southbound,SB_Global,connections \
- --private-key="$PKIDIR/testpki-privkey2.pem" \
- --certificate="$PKIDIR/testpki-cert2.pem" \
- --ca-cert="$PKIDIR/testpki-cacert.pem" \
- ovn-sb.db
-
-PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
-
-# read-only accesses should succeed
-AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- list SB_Global], [0], [stdout], [ignore])
-AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- list Connection], [0], [stdout], [ignore])
-
-# write access should fail
-AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- chassis-add ch vxlan 1.2.4.8], [1], [ignore],
-[ovn-sbctl: transaction error: {"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}
-])
-
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-AT_CLEANUP
-
-AT_SETUP([ovn -- nb connection/ssl commands])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
-PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
-AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\"
-\\]"])
-
-: > .$1.db.~lock~
-ovsdb-tool create ovn-nb.db "$abs_top_srcdir"/ovn/ovn-nb.ovsschema
-
-# Start nb db server using db connection/ssl entries (unpopulated initially)
-start_daemon ovsdb-server --remote=punix:ovnnb_db.sock \
- --remote=db:OVN_Northbound,NB_Global,connections \
- --private-key=db:OVN_Northbound,SSL,private_key \
- --certificate=db:OVN_Northbound,SSL,certificate \
- --ca-cert=db:OVN_Northbound,SSL,ca_cert \
- ovn-nb.db
-
-# Populate SSL configuration entries in nb db
-AT_CHECK(
- [ovn-nbctl set-ssl $PKIDIR/testpki-privkey.pem \
- $PKIDIR/testpki-cert.pem \
- $PKIDIR/testpki-cacert.pem], [0], [stdout], [ignore])
-
-# Populate a passive SSL connection in nb db
-AT_CHECK([ovn-nbctl set-connection pssl:0:127.0.0.1], [0], [stdout], [ignore])
-
-PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
-
-# Verify SSL connetivity to nb db server
-AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- list NB_Global],
- [0], [stdout], [ignore])
-AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- list Connection],
- [0], [stdout], [ignore])
-AT_CHECK([ovn-nbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- get-connection],
- [0], [stdout], [ignore])
-
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-AT_CLEANUP
-
-AT_SETUP([ovn -- sb connection/ssl commands])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
-PKIDIR="$(cd $abs_top_builddir/tests && pwd)"
-AT_SKIP_IF([expr "$PKIDIR" : ".*[ '\"
-\\]"])
-
-: > .$1.db.~lock~
-ovsdb-tool create ovn-sb.db "$abs_top_srcdir"/ovn/ovn-sb.ovsschema
-
-# Start sb db server using db connection/ssl entries (unpopulated initially)
-start_daemon ovsdb-server --remote=punix:ovnsb_db.sock \
- --remote=db:OVN_Southbound,SB_Global,connections \
- --private-key=db:OVN_Southbound,SSL,private_key \
- --certificate=db:OVN_Southbound,SSL,certificate \
- --ca-cert=db:OVN_Southbound,SSL,ca_cert \
- ovn-sb.db
-
-# Populate SSL configuration entries in sb db
-AT_CHECK(
- [ovn-sbctl set-ssl $PKIDIR/testpki-privkey.pem \
- $PKIDIR/testpki-cert.pem \
- $PKIDIR/testpki-cacert.pem], [0], [stdout], [ignore])
-
-# Populate a passive SSL connection in sb db
-AT_CHECK([ovn-sbctl set-connection pssl:0:127.0.0.1], [0], [stdout], [ignore])
-
-PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
-
-# Verify SSL connetivity to sb db server
-AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- list SB_Global],
- [0], [stdout], [ignore])
-AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- list Connection],
- [0], [stdout], [ignore])
-AT_CHECK([ovn-sbctl --db=ssl:127.0.0.1:$TCP_PORT \
- --private-key=$PKIDIR/testpki-privkey.pem \
- --certificate=$PKIDIR/testpki-cert.pem \
- --ca-cert=$PKIDIR/testpki-cacert.pem \
- get-connection],
- [0], [stdout], [ignore])
-
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-AT_CLEANUP
-
-AT_SETUP([ovn -- nested containers])
-ovn_start
-
-# Physical network:
-# 2 HVs. HV1 has 2 VMs - "VM1" and "bar3". HV2 has 1 VM - "VM2"
-
-# Logical network:
-# 3 Logical switches - "mgmt" (172.16.1.0/24), "foo" (192.168.1.0/24)
-# and "bar" (192.168.2.0/24). They are all connected to router R1.
-
-ovn-nbctl lr-add R1
-ovn-nbctl ls-add mgmt
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-
-# Connect mgmt to R1
-ovn-nbctl lrp-add R1 mgmt 00:00:00:01:02:02 172.16.1.1/24
-ovn-nbctl lsp-add mgmt rp-mgmt -- set Logical_Switch_Port rp-mgmt type=router \
- options:router-port=mgmt addresses=\"00:00:00:01:02:02\"
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \
- options:router-port=foo addresses=\"00:00:00:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:00:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar type=router \
- options:router-port=bar addresses=\"00:00:00:01:02:04\"
-
-# "mgmt" has VM1 and VM2 connected
-ovn-nbctl lsp-add mgmt vm1 \
--- lsp-set-addresses vm1 "f0:00:00:01:02:03 172.16.1.2"
-
-ovn-nbctl lsp-add mgmt vm2 \
--- lsp-set-addresses vm2 "f0:00:00:01:02:04 172.16.1.3"
-
-# "foo1" and "foo2" are containers belonging to switch "foo"
-# "foo1" has "VM1" as parent_port and "foo2" has "VM2" as parent_port.
-ovn-nbctl lsp-add foo foo1 vm1 1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:05 192.168.1.2"
-
-ovn-nbctl lsp-add foo foo2 vm2 2 \
--- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3"
-
-# "bar1" and "bar2" are containers belonging to switch "bar"
-# "bar1" has "VM1" as parent_port and "bar2" has "VM2" as parent_port.
-ovn-nbctl lsp-add bar bar1 vm1 2 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:07 192.168.2.2"
-
-ovn-nbctl lsp-add bar bar2 vm2 1 \
--- lsp-set-addresses bar2 "f0:00:00:01:02:08 192.168.2.3"
-
-# bar3 is a standalone VM belonging to switch "bar"
-ovn-nbctl lsp-add bar bar3 \
--- lsp-set-addresses bar3 "f0:00:00:01:02:09 192.168.2.4"
-
-# Create two hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int vm1 -- \
- set interface vm1 external-ids:iface-id=vm1 \
- options:tx_pcap=hv1/vm1-tx.pcap \
- options:rxq_pcap=hv1/vm1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int bar3 -- \
- set interface bar3 external-ids:iface-id=bar3 \
- options:tx_pcap=hv1/bar3-tx.pcap \
- options:rxq_pcap=hv1/bar3-rx.pcap \
- ofport-request=2
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int vm2 -- \
- set interface vm2 external-ids:iface-id=vm2 \
- options:tx_pcap=hv2/vm2-tx.pcap \
- options:rxq_pcap=hv2/vm2-rx.pcap \
- ofport-request=1
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send ip packets between foo1 and foo2 (same switch, different HVs and
-# different VLAN tags).
-src_mac="f00000010205"
-dst_mac="f00000010206"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 1 3`
-packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at foo2
-packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-echo $packet > expected
-OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected])
-
-# Send ip packets between foo1 and bar2 (different switch, different HV)
-src_mac="f00000010205"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 2 3`
-packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at bar2
-src_mac="000000010204"
-dst_mac="f00000010208"
-packet=${dst_mac}${src_mac}8100000108004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-echo $packet >> expected
-OVN_CHECK_PACKETS([hv2/vm2-tx.pcap], [expected])
-
-# Send ip packets between foo1 and bar1
-# (different switch, loopback to same vm but different tag)
-src_mac="f00000010205"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 2 2`
-packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at bar1
-src_mac="000000010204"
-dst_mac="f00000010207"
-packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-echo $packet > expected1
-OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
-
-# Send ip packets between bar1 and bar3
-# (same switch. But one is container and another is a standalone VM)
-src_mac="f00000010207"
-dst_mac="f00000010209"
-src_ip=`ip_to_hex 192 168 2 2`
-dst_ip=`ip_to_hex 192 168 2 3`
-packet=${dst_mac}${src_mac}8100000208004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at bar3
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-echo $packet > expected
-OVN_CHECK_PACKETS([hv1/bar3-tx.pcap], [expected])
-
-# Send ip packets between foo1 and vm1.
-(different switch, container to the VM hosting it.)
-src_mac="f00000010205"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 2`
-packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at vm1
-src_mac="000000010202"
-dst_mac="f00000010203"
-packet=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-echo $packet >> expected1
-OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
-
-# Send packets from vm1 to bar1.
-(different switch, A hosting VM to a container inside it)
-src_mac="f00000010203"
-dst_mac="000000010202"
-src_ip=`ip_to_hex 172 16 1 2`
-dst_ip=`ip_to_hex 192 168 2 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at vm1
-src_mac="000000010204"
-dst_mac="f00000010207"
-packet=${dst_mac}${src_mac}8100000208004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-echo $packet >> expected1
-OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
-
-# Send broadcast packet from foo1. foo1 should not receive the same packet.
-src_mac="f00000010205"
-dst_mac="ffffffffffff"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 255 255 255 255`
-packet=${dst_mac}${src_mac}8100000108004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive vm1 $packet
-
-# expected packet at VM1
-OVN_CHECK_PACKETS([hv1/vm1-tx.pcap], [expected1])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 3 HVs, 3 LRs connected via LS, source IP based routes])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and bar
-# (192.168.2.0/24) connected to it.
-#
-# R2 and R3 are gateway routers.
-# R2 has alice (172.16.1.0/24) and R3 has bob (172.16.1.0/24)
-# connected to it. Note how both alice and bob have the same subnet behind it.
-# We are trying to simulate external network via those 2 switches. In real
-# world the switch ports of these switches will have addresses set as "unknown"
-# to make them learning switches. Or those switches will be "localnet" ones.
-
-# Create three hypervisors and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=bar1 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=alice1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl -- add-port br-int hv3-vif1 -- \
- set interface hv3-vif1 external-ids:iface-id=bob1 \
- options:tx_pcap=hv3/vif1-tx.pcap \
- options:rxq_pcap=hv3/vif1-rx.pcap \
- ofport-request=1
-
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl create Logical_Router name=R2 options:chassis="hv2"
-ovn-nbctl create Logical_Router name=R3 options:chassis="hv3"
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add bob
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \
- options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar type=router \
- options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect bob to R3
-ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24
-ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
- type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Connect R3 to join
-ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24
-ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
- type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
-
-# Install static routes with source ip address as the policy for routing.
-# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3.
-ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2
-ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3
-
-# Install static routes with destination ip address as the policy for routing.
-ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-
-ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port bar1 in bar
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2"
-
-# Create logical port alice1 in alice
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.3"
-
-# Create logical port bob1 in bob
-ovn-nbctl lsp-add bob bob1 \
--- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4"
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-# Send ip packets between foo1 and bar1
-# (East-west traffic should flow normally)
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 2 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-# Send ip packets between foo1 and alice1
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 3`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-as hv1 ovs-appctl ofproto/trace br-int in_port=1 $packet
-
-# Send ip packets between bar1 and bob1
-src_mac="f00000010204"
-dst_mac="000001010204"
-src_ip=`ip_to_hex 192 168 2 2`
-dst_ip=`ip_to_hex 172 16 1 4`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif2 $packet
-#as hv1 ovs-appctl ofproto/trace br-int in_port=2 $packet
-
-# Packet to expect at bar1
-src_mac="000001010204"
-dst_mac="f00000010204"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 192 168 2 2`
-expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-echo $expected > expected
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
-
-# Packet to Expect at alice1
-src_mac="000002010203"
-dst_mac="f00000010205"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 3`
-expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
-echo $expected > expected
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Packet to Expect at bob1
-src_mac="000003010203"
-dst_mac="f00000010206"
-src_ip=`ip_to_hex 192 168 2 2`
-dst_ip=`ip_to_hex 172 16 1 4`
-expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
-echo $expected > expected
-OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- dns lookup : 1 HV, 2 LS, 2 LSPs/LS])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add ls1
-
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 aef0::4"
-
-ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 aef0::4"
-
-ovn-nbctl lsp-add ls1 ls1-lp2 \
--- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
-
-ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6 20.0.0.4"
-
-DNS1=`ovn-nbctl create DNS records={}`
-DNS2=`ovn-nbctl create DNS records={}`
-
-ovn-nbctl set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4"
-ovn-nbctl set DNS $DNS1 records:vm2.ovn.org="10.0.0.6 20.0.0.4"
-ovn-nbctl set DNS $DNS2 records:vm3.ovn.org="40.0.0.4"
-
-ovn-nbctl set Logical_switch ls1 dns_records="$DNS1"
-
-net_add n1
-sim_add hv1
-
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-OVN_POPULATE_ARP
-sleep 2
-as hv1 ovs-vsctl show
-
-echo "*************************"
-ovn-sbctl list DNS
-echo "*************************"
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-# set_dns_params host_name
-# Sets the dns_req_data and dns_resp_data
-set_dns_params() {
- local hname=$1
- local ttl=00000e10
- an_count=0001
- type=0001
- case $hname in
- vm1)
- # vm1.ovn.org
- query_name=03766d31036f766e036f726700
- # IPv4 address - 10.0.0.4
- expected_dns_answer=${query_name}00010001${ttl}00040a000004
- ;;
- vm2)
- # vm2.ovn.org
- query_name=03766d32036f766e036f726700
- # IPv4 address - 10.0.0.6
- expected_dns_answer=${query_name}00010001${ttl}00040a000006
- # IPv4 address - 20.0.0.4
- expected_dns_answer=${expected_dns_answer}${query_name}00010001${ttl}000414000004
- an_count=0002
- ;;
- vm3)
- # vm3.ovn.org
- query_name=03766d33036f766e036f726700
- # IPv4 address - 40.0.0.4
- expected_dns_answer=${query_name}00010001${ttl}000428000004
- ;;
- vm1_ipv6_only)
- # vm1.ovn.org
- query_name=03766d31036f766e036f726700
- # IPv6 address - aef0::4
- type=001c
- expected_dns_answer=${query_name}${type}0001${ttl}0010aef00000000000000000000000000004
- ;;
- vm1_ipv4_v6)
- # vm1.ovn.org
- query_name=03766d31036f766e036f726700
- type=00ff
- an_count=0002
- # IPv4 address - 10.0.0.4
- # IPv6 address - aef0::4
- expected_dns_answer=${query_name}00010001${ttl}00040a000004
- expected_dns_answer=${expected_dns_answer}${query_name}001c0001${ttl}0010
- expected_dns_answer=${expected_dns_answer}aef00000000000000000000000000004
- ;;
- vm1_invalid_type)
- # vm1.ovn.org
- query_name=03766d31036f766e036f726700
- # IPv6 address - aef0::4
- type=0002
- ;;
- vm1_incomplete)
- # set type to none
- type=''
- esac
- # TTL - 3600
- local dns_req_header=010201200001000000000000
- local dns_resp_header=010281200001${an_count}00000000
- dns_req_data=${dns_req_header}${query_name}${type}0001
- dns_resp_data=${dns_resp_header}${query_name}${type}0001${expected_dns_answer}
-}
-
-# This shell function sends a DNS request packet
-# test_dns INPORT SRC_MAC DST_MAC SRC_IP DST_IP DNS_QUERY EXPEC
-test_dns() {
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 dns_reply=$6
- local dns_query_data=$7
- shift; shift; shift; shift; shift; shift; shift;
- # Packet size => IPv4 header (20) + UDP header (8) +
- # DNS data (header + query)
- ip_len=`expr 28 + ${#dns_query_data} / 2`
- udp_len=`expr $ip_len - 20`
- ip_len=$(printf "%x" $ip_len)
- udp_len=$(printf "%x" $udp_len)
- local request=${dst_mac}${src_mac}0800450000${ip_len}0000000080110000
- request=${request}${src_ip}${dst_ip}9234003500${udp_len}0000
- # dns data
- request=${request}${dns_query_data}
-
- if test $dns_reply != 0; then
- local dns_reply=$1
- ip_len=`expr 28 + ${#dns_reply} / 2`
- udp_len=`expr $ip_len - 20`
- ip_len=$(printf "%x" $ip_len)
- udp_len=$(printf "%x" $udp_len)
- local reply=${src_mac}${dst_mac}0800450000${ip_len}0000000080110000
- reply=${reply}${dst_ip}${src_ip}0035923400${udp_len}0000${dns_reply}
- echo $reply >> $inport.expected
- else
- for outport; do
- echo $request >> $outport.expected
- done
- fi
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
-}
-
-test_dns6() {
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 dns_reply=$6
- local dns_query_data=$7
- shift; shift; shift; shift; shift; shift; shift;
- # Packet size => UDP header (8) +
- # DNS data (header + query)
- ip_len=`expr 8 + ${#dns_query_data} / 2`
- udp_len=$ip_len
- ip_len=$(printf "%x" $ip_len)
- udp_len=$(printf "%x" $udp_len)
- local request=${dst_mac}${src_mac}86dd6000000000${ip_len}11ff${src_ip}${dst_ip}
- request=${request}9234003500${udp_len}0000
- #dns data
- request=${request}${dns_query_data}
-
- if test $dns_reply != 0; then
- local dns_reply=$1
- ip_len=`expr 8 + ${#dns_reply} / 2`
- udp_len=$ip_len
- ip_len=$(printf "%x" $ip_len)
- udp_len=$(printf "%x" $udp_len)
- local reply=${src_mac}${dst_mac}86dd6000000000${ip_len}11ff${dst_ip}${src_ip}
- reply=${reply}0035923400${udp_len}0000${dns_reply}
- echo $reply >> $inport.expected
- else
- for outport; do
- echo $request >> $outport.expected
- done
- fi
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif$inport $request
-}
-
-AT_CAPTURE_FILE([ofctl_monitor0.log])
-as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
-
-set_dns_params vm2
-src_ip=`ip_to_hex 10 0 0 4`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=1
-test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-
-# NXT_RESUMEs should be 1.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-cat 1.expected | cut -c -48 > expout
-AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 1.expected | cut -c 53- > expout
-AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-set_dns_params vm1
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=1
-test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-
-# NXT_RESUMEs should be 2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Clear the query name options for ls1-lp2
-ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
-
-set_dns_params vm2
-src_ip=`ip_to_hex 10 0 0 4`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=0
-test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data
-
-# NXT_RESUMEs should be 3.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-AT_CHECK([cat 1.packets], [0], [])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Clear the query name for ls1-lp1
-# Since ls1 has no query names configued,
-# ovn-northd should not add the DNS flows.
-ovn-nbctl --wait=hv remove DNS $DNS1 records vm1.ovn.org
-
-set_dns_params vm1
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=0
-test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
-
-# NXT_RESUMEs should be 3 only.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-AT_CHECK([cat 2.packets], [0], [])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Test IPv6 (AAAA records) using IPv4 packet.
-# Add back the DNS options for ls1-lp1.
-ovn-nbctl --wait=hv set DNS $DNS1 records:vm1.ovn.org="10.0.0.4 aef0::4"
-
-set_dns_params vm1_ipv6_only
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=1
-test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-
-# NXT_RESUMEs should be 4.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Test both IPv4 (A) and IPv6 (AAAA records) using IPv4 packet.
-set_dns_params vm1_ipv4_v6
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=1
-test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-
-# NXT_RESUMEs should be 5.
-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-cat 2.expected | cut -c -48 > expout
-AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 2.expected | cut -c 53- > expout
-AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Invalid type.
-set_dns_params vm1_invalid_type
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=0
-test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
-
-# NXT_RESUMEs should be 6.
-OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-AT_CHECK([cat 2.packets], [0], [])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Incomplete DNS packet.
-set_dns_params vm1_incomplete
-src_ip=`ip_to_hex 10 0 0 6`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=0
-test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
-
-# NXT_RESUMEs should be 7.
-OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-AT_CHECK([cat 2.packets], [0], [])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Add one more DNS record to the ls1.
-ovn-nbctl --wait=hv set Logical_switch ls1 dns_records="$DNS1 $DNS2"
-
-set_dns_params vm3
-src_ip=`ip_to_hex 10 0 0 4`
-dst_ip=`ip_to_hex 10 0 0 1`
-dns_reply=1
-test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-
-# NXT_RESUMEs should be 8.
-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-cat 1.expected | cut -c -48 > expout
-AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat 1.expected | cut -c 53- > expout
-AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-# Try DNS query over IPv6
-set_dns_params vm1
-src_ip=aef00000000000000000000000000004
-dst_ip=aef00000000000000000000000000001
-dns_reply=1
-test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
-
-# NXT_RESUMEs should be 9.
-OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-# Skipping the UDP checksum.
-cat 1.expected | cut -c 1-120,125- > expout
-AT_CHECK([cat 1.packets | cut -c 1-120,125-], [0], [expout])
-
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-rm -f 1.expected
-rm -f 2.expected
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4 HV, 1 LS, 1 LR, packet test with HA distributed router gateway port])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add gw1
-as gw1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-
-sim_add gw2
-as gw2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.4
-
-sim_add ext1
-as ext1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl -- add-port br-int ext1-vif1 -- \
- set interface ext1-vif1 external-ids:iface-id=outside1 \
- options:tx_pcap=ext1/vif1-tx.pcap \
- options:rxq_pcap=ext1/vif1-rx.pcap \
- ofport-request=1
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add outside
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect alice to R1 as distributed router gateway port on gw1
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24
-
-ovn-nbctl \
- --id=@gc0 create Gateway_Chassis name=alice_gw1 \
- chassis_name=gw1 \
- priority=20 -- \
- --id=@gc1 create Gateway_Chassis name=alice_gw2 \
- chassis_name=gw2 \
- priority=10 -- \
- set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'
-
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port outside1 in outside
-ovn-nbctl lsp-add outside outside1 \
--- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.3"
-
-# Create localnet port in alice
-ovn-nbctl lsp-add alice ln-alice
-ovn-nbctl lsp-set-addresses ln-alice unknown
-ovn-nbctl lsp-set-type ln-alice localnet
-ovn-nbctl lsp-set-options ln-alice network_name=phys
-
-# Create localnet port in outside
-ovn-nbctl lsp-add outside ln-outside
-ovn-nbctl lsp-set-addresses ln-outside unknown
-ovn-nbctl lsp-set-type ln-outside localnet
-ovn-nbctl lsp-set-options ln-outside network_name=phys
-
-# Create bridge-mappings on gw1, gw2 and ext1, hv1 doesn't need
-# mapping to the external network, is the one generating packets
-as gw1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-as gw2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-as ext1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 2
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-test_ip_packet()
-{
- local active_gw=$1
- local backup_gw=$2
- local backup_vswitchd_dead=$3
-
- # Send ip packet between foo1 and outside1
- src_mac="f00000010203" # foo1 mac
- dst_mac="000001010203" # rp-foo mac (internal router leg)
- src_ip=`ip_to_hex 192 168 1 2`
- dst_ip=`ip_to_hex 172 16 1 3`
- packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
- # ARP request packet to expect at outside1
- #arp_request=ffffffffffff${src_mac}08060001080006040001${src_mac}${src_ip}000000000000${dst_ip}
-
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
- # Send ARP reply from outside1 back to the router
- # XXX: note, we could avoid this if we plug this port into a netns
- # and setup the IP address into the port, so the kernel would simply reply
- src_mac="000002010203"
- reply_mac="f00000010204"
- dst_ip=`ip_to_hex 172 16 1 3`
- src_ip=`ip_to_hex 172 16 1 1`
- arp_reply=${src_mac}${reply_mac}08060001080006040002${reply_mac}${dst_ip}${src_mac}${src_ip}
-
- as ext1 ovs-appctl netdev-dummy/receive ext1-vif1 $arp_reply
-
- OVS_WAIT_UNTIL([
- test `as $active_gw ovs-ofctl dump-flows br-int | grep table=66 | \
-grep actions=mod_dl_dst:f0:00:00:01:02:04 | wc -l` -eq 1
- ])
-
- # Packet to Expect at ext1 chassis, outside1 port
- src_mac="000002010203"
- dst_mac="f00000010204"
- src_ip=`ip_to_hex 192 168 1 2`
- dst_ip=`ip_to_hex 172 16 1 3`
- expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
- echo $expected > ext1-vif1.expected
- exp_gw_ip_garp=ffffffffffff00000201020308060001080006040001000002010203ac100101000000000000ac100101
- echo $exp_gw_ip_garp >> ext1-vif1.expected
- as $active_gw reset_pcap_file br-phys_n1 $active_gw/br-phys_n1
-
- if test $backup_vswitchd_dead != 1; then
- # Reset the file only if vswitchd in backup gw is alive
- as $backup_gw reset_pcap_file br-phys_n1 $backup_gw/br-phys_n1
- fi
- as ext1 reset_pcap_file ext1-vif1 ext1/vif1
-
- # Resend packet from foo1 to outside1
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
- sleep 1
-
- OVN_CHECK_PACKETS([ext1/vif1-tx.pcap], [ext1-vif1.expected])
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $active_gw/br-phys_n1-tx.pcap > packets
- cat packets | grep $expected > exp
- # Its possible that $active_gw/br-phys_n1-tx.pcap may have received multiple
- # garp packets. So consider only the first packet.
- cat packets | grep $exp_gw_ip_garp | head -1 >> exp
- AT_CHECK([cat exp], [0], [expout])
- rm -f expout
- if test $backup_vswitchd_dead != 1; then
- # Check for backup gw only if vswitchd is alive
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $backup_gw/br-phys_n1-tx.pcap > packets
- AT_CHECK([grep $expected packets | sort], [0], [])
- fi
-}
-
-test_ip_packet gw1 gw2 0
-
-ovn-nbctl --timeout=3 --wait=hv \
- --id=@gc0 create Gateway_Chassis name=alice_gw1 \
- chassis_name=gw1 \
- priority=10 -- \
- --id=@gc1 create Gateway_Chassis name=alice_gw2 \
- chassis_name=gw2 \
- priority=20 -- \
- set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'
-
-test_ip_packet gw2 gw1 0
-
-# Get the claim count of both gw1 and gw2.
-gw1_claim_ct=`grep "cr-alice: Claiming" gw1/ovn-controller.log | wc -l`
-gw2_claim_ct=`grep "cr-alice: Claiming" gw2/ovn-controller.log | wc -l`
-
-# Stop ovs-vswitchd in gw2. gw1 should claim the gateway port.
-as gw2
-OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
-
-# gw1 should claim the cr-alice and the claim count of gw1 should be
-# incremented by 1.
-gw1_claim_ct=$((gw1_claim_ct+1))
-
-OVS_WAIT_UNTIL([test $gw1_claim_ct = `cat gw1/ovn-controller.log \
-| grep -c "cr-alice: Claiming"`])
-
-AT_CHECK([test $gw2_claim_ct = `cat gw2/ovn-controller.log | \
-grep -c "cr-alice: Claiming"`])
-
-test_ip_packet gw1 gw2 1
-
-as gw2
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-OVN_CLEANUP([hv1],[gw1],[ext1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 4 HV, 3 LS, 2 LR, packet test with HA distributed router gateway port])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add gw1
-as gw1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-
-sim_add gw2
-as gw2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.4
-
-sim_add ext1
-as ext1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl -- add-port br-int ext1-vif1 -- \
- set interface ext1-vif1 external-ids:iface-id=outside1 \
- options:tx_pcap=ext1/vif1-tx.pcap \
- options:rxq_pcap=ext1/vif1-rx.pcap \
- ofport-request=1
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R0
-ovn-nbctl create Logical_Router name=R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add join
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add outside
-
-#Connect foo to R0
-ovn-nbctl lrp-add R0 R0-foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo foo-R0 -- set Logical_Switch_Port foo-R0 \
- type=router options:router-port=R0-foo \
- -- lsp-set-addresses foo-R0 router
-
-#Connect R0 to join
-ovn-nbctl lrp-add R0 R0-join 00:00:0d:01:02:03 100.60.1.1/24
-ovn-nbctl lsp-add join join-R0 -- set Logical_Switch_Port join-R0 \
- type=router options:router-port=R0-join \
- -- lsp-set-addresses join-R0 router
-
-#Connect join to R1
-ovn-nbctl lrp-add R1 R1-join 00:00:0e:01:02:03 100.60.1.2/24
-ovn-nbctl lsp-add join join-R1 -- set Logical_Switch_Port join-R1 \
- type=router options:router-port=R1-join \
- -- lsp-set-addresses join-R1 router
-
-#add route rules
-ovn-nbctl lr-route-add R0 0.0.0.0/0 100.60.1.2
-ovn-nbctl lr-route-add R1 192.168.0.0/16 100.60.1.1
-
-# Connect alice to R1 as distributed router gateway port on gw1
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24
-
-ovn-nbctl \
- --id=@gc0 create Gateway_Chassis name=alice_gw1 \
- chassis_name=gw1 \
- priority=20 -- \
- --id=@gc1 create Gateway_Chassis name=alice_gw2 \
- chassis_name=gw2 \
- priority=10 -- \
- set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'
-
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port outside1 in outside
-ovn-nbctl lsp-add outside outside1 \
--- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.3"
-
-# Create localnet port in alice
-ovn-nbctl lsp-add alice ln-alice
-ovn-nbctl lsp-set-addresses ln-alice unknown
-ovn-nbctl lsp-set-type ln-alice localnet
-ovn-nbctl lsp-set-options ln-alice network_name=phys
-
-# Create localnet port in outside
-ovn-nbctl lsp-add outside ln-outside
-ovn-nbctl lsp-set-addresses ln-outside unknown
-ovn-nbctl lsp-set-type ln-outside localnet
-ovn-nbctl lsp-set-options ln-outside network_name=phys
-
-# Create bridge-mappings on gw1, gw2 and ext1, hv1 doesn't need
-# mapping to the external network, is the one generating packets
-as gw1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-as gw2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-as ext1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])
-
-# hv1 should be in 'ref_chassis' of the ha_chasssi_group as logical
-# switch 'foo' can reach the router 'R1' (which has gw router port)
-# via foo1 -> foo -> R0 -> join -> R1
-hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$hv1_ch_uuid" = "$ref_ch_list"])
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 2
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-test_ip_packet()
-{
- local active_gw=$1
- local backup_gw=$2
-
- # Send ip packet between foo1 and outside1
- src_mac="f00000010203" # foo1 mac
- dst_mac="000001010203" # foo-R0 mac (internal router leg)
- src_ip=`ip_to_hex 192 168 1 2`
- dst_ip=`ip_to_hex 172 16 1 3`
- packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
- # ARP request packet to expect at outside1
- #arp_request=ffffffffffff${src_mac}08060001080006040001${src_mac}${src_ip}000000000000${dst_ip}
-
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
- # Send ARP reply from outside1 back to the router
- # XXX: note, we could avoid this if we plug this port into a netns
- # and setup the IP address into the port, so the kernel would simply reply
- src_mac="000002010203"
- reply_mac="f00000010204"
- dst_ip=`ip_to_hex 172 16 1 3`
- src_ip=`ip_to_hex 172 16 1 1`
- arp_reply=${src_mac}${reply_mac}08060001080006040002${reply_mac}${dst_ip}${src_mac}${src_ip}
-
- as ext1 ovs-appctl netdev-dummy/receive ext1-vif1 $arp_reply
-
- OVS_WAIT_UNTIL([
- test `as $active_gw ovs-ofctl dump-flows br-int | grep table=66 | \
-grep actions=mod_dl_dst:f0:00:00:01:02:04 | wc -l` -eq 1
- ])
-
- # Packet to Expect at ext1 chassis, outside1 port
- src_mac="000002010203"
- dst_mac="f00000010204"
- src_ip=`ip_to_hex 192 168 1 2`
- dst_ip=`ip_to_hex 172 16 1 3`
- expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
- echo $expected > ext1-vif1.expected
- exp_gw_ip_garp=ffffffffffff00000201020308060001080006040001000002010203ac100101000000000000ac100101
- echo $exp_gw_ip_garp >> ext1-vif1.expected
-
- as $active_gw reset_pcap_file br-phys_n1 $active_gw/br-phys_n1
- as $backup_gw reset_pcap_file br-phys_n1 $backup_gw/br-phys_n1
- as ext1 reset_pcap_file ext1-vif1 ext1/vif1
-
- # Resend packet from foo1 to outside1
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
- OVN_CHECK_PACKETS([ext1/vif1-tx.pcap], [ext1-vif1.expected])
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $active_gw/br-phys_n1-tx.pcap > packets
- cat packets | grep $expected > exp
- cat packets | grep $exp_gw_ip_garp | head -1 >> exp
- AT_CHECK([cat exp], [0], [expout])
-
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $backup_gw/br-phys_n1-tx.pcap > packets
- AT_CHECK([grep $expected packets | sort], [0], [])
-}
-
-test_ip_packet gw1 gw2
-
-ovn-nbctl --timeout=3 --wait=hv \
- --id=@gc0 create Gateway_Chassis name=alice_gw1 \
- chassis_name=gw1 \
- priority=10 -- \
- --id=@gc1 create Gateway_Chassis name=alice_gw2 \
- chassis_name=gw2 \
- priority=20 -- \
- set Logical_Router_Port alice 'gateway_chassis=[@gc0,@gc1]'
-
-test_ip_packet gw2 gw1
-
-OVN_CLEANUP([hv1],[gw1],[gw2],[ext1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 1 LR with distributed router gateway port])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR R1 that has switches foo (192.168.1.0/24) and
-# alice (172.16.1.0/24) connected to it. The logical port
-# between R1 and alice has a "redirect-chassis" specified,
-# i.e. it is the distributed router gateway port.
-# Switch alice also has a localnet port defined.
-# An additional switch outside has a localnet port and the
-# same subnet as alice (172.16.1.0/24).
-
-# Physical network:
-# Three hypervisors hv[123].
-# hv1 hosts vif foo1.
-# hv2 is the "redirect-chassis" that hosts the distributed
-# router gateway port.
-# hv3 hosts vif outside1.
-# In order to show that connectivity works only through hv2,
-# an initial round of tests is run without any bridge-mapping
-# defined for the localnet on hv2. These tests are expected
-# to fail.
-# Subsequent tests are run after defining the bridge-mapping
-# for the localnet on hv2. These tests are expected to succeed.
-
-# Create three hypervisors and create OVS ports corresponding
-# to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl -- add-port br-int hv3-vif1 -- \
- set interface hv3-vif1 external-ids:iface-id=outside1 \
- options:tx_pcap=hv3/vif1-tx.pcap \
- options:rxq_pcap=hv3/vif1-rx.pcap \
- ofport-request=1
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add outside
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect alice to R1 as distributed router gateway port on hv2
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \
- -- set Logical_Router_Port alice options:redirect-chassis="hv2"
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port outside1 in outside
-ovn-nbctl lsp-add outside outside1 \
--- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.3"
-
-# Create localnet port in alice
-ovn-nbctl lsp-add alice ln-alice
-ovn-nbctl lsp-set-addresses ln-alice unknown
-ovn-nbctl lsp-set-type ln-alice localnet
-ovn-nbctl lsp-set-options ln-alice network_name=phys
-
-# Create localnet port in outside
-ovn-nbctl lsp-add outside ln-outside
-ovn-nbctl lsp-set-addresses ln-outside unknown
-ovn-nbctl lsp-set-type ln-outside localnet
-ovn-nbctl lsp-set-options ln-outside network_name=phys
-
-# Create bridge-mappings on hv1 and hv3, leaving hv2 for later
-as hv1 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 2
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-ovn-sbctl list encap
-echo "------ Gateway_Chassis dump (SBDB) -------"
-ovn-sbctl list Gateway_Chassis
-echo "------ Port_Binding chassisredirect -------"
-ovn-sbctl find Port_Binding type=chassisredirect
-echo "-------------------------------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl show br-int
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump ----------"
-as hv2 ovs-ofctl show br-int
-as hv2 ovs-ofctl dump-flows br-int
-echo "------ hv3 dump ----------"
-as hv3 ovs-ofctl show br-int
-as hv3 ovs-ofctl dump-flows br-int
-echo "--------------------------"
-
-
-# Check that redirect mapping is programmed only on hv2
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=33 | grep =0x3,metadata=0x1 | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=33 | grep =0x3,metadata=0x1 | grep load:0x2- | wc -l], [0], [1
-])
-# Check that hv1 sends chassisredirect port traffic to hv2
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep =0x3,metadata=0x1 | grep output | wc -l], [0], [1
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep =0x3,metadata=0x1 | wc -l], [0], [0
-])
-# Check that arp reply on distributed gateway port is only programmed on hv2
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep arp | grep load:0x2- | grep =0x2,metadata=0x1 | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep arp | grep load:0x2- | grep =0x2,metadata=0x1 | wc -l], [0], [1
-])
-
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-
-: > hv2-vif1.expected
-: > hv3-vif1.expected
-
-# test_arp INPORT SHA SPA TPA [REPLY_HA]
-#
-# Causes a packet to be received on INPORT. The packet is an ARP
-# request with SHA, SPA, and TPA as specified. If REPLY_HA is provided, then
-# it should be the hardware address of the target to expect to receive in an
-# ARP reply; otherwise no reply is expected.
-#
-# INPORT is an logical switch port number, e.g. 11 for vif11.
-# SHA and REPLY_HA are each 12 hex digits.
-# SPA and TPA are each 8 hex digits.
-test_arp() {
- local hv=$1 inport=$2 sha=$3 spa=$4 tpa=$5 reply_ha=$6
- local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
- as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
-
- if test X$reply_ha != X; then
- # Expect to receive the reply, if any.
- local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa}
- echo $reply >> hv${hv}-vif$inport.expected
- fi
-}
-
-rtr_ip=$(ip_to_hex 172 16 1 1)
-foo_ip=$(ip_to_hex 192 168 1 2)
-outside_ip=$(ip_to_hex 172 16 1 3)
-
-echo $rtr_ip
-echo $foo_ip
-echo $outside_ip
-
-# ARP for router IP address from outside1, no response expected
-test_arp 3 1 f00000010204 $outside_ip $rtr_ip
-
-# Now check the packets actually received against the ones expected.
-OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected])
-
-# Send ip packet between foo1 and outside1
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 3`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-# Now check the packets actually received against the ones expected.
-OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected])
-
-# Now add bridge-mappings on hv2, which should make everything work
-as hv2 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-# Wait until the patch ports are created in hv2 to connect br-int to br-phys
-OVS_WAIT_UNTIL([test 1 = `as hv2 ovs-vsctl show | \
-grep "Port patch-br-int-to-ln-alice" | wc -l`])
-
-# ARP for router IP address from outside1
-test_arp 3 1 f00000010204 $outside_ip $rtr_ip 000002010203
-
-# hv3-vif1.expected should also have the gw router port garp packet.
-exp_gw_ip_garp=ffffffffffff00000201020308060001080006040001000002010203ac100101000000000000ac100101
-echo $exp_gw_ip_garp >> hv3-vif1.expected
-
-# Now check the packets actually received against the ones expected.
-OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected])
-
-# Send ip packet between foo1 and outside1
-src_mac="f00000010203"
-dst_mac="000001010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 172 16 1 3`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-# Packet to Expect at outside1
-src_mac="000002010203"
-dst_mac="f00000010204"
-expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl show br-int
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump ----------"
-as hv2 ovs-ofctl show br-int
-as hv2 ovs-ofctl dump-flows br-int
-echo "------ hv3 dump ----------"
-as hv3 ovs-ofctl show br-int
-as hv3 ovs-ofctl dump-flows br-int
-echo "----------------------------"
-
-echo $expected >> hv3-vif1.expected
-OVN_CHECK_PACKETS([hv3/vif1-tx.pcap], [hv3-vif1.expected])
-
-#Check ovn-trace over "chassisredirect" port
-AT_CAPTURE_FILE([trace])
-ovn_trace () {
- ovn-trace --all "$@" | tee trace | sed '1,/Minimal trace/d'
-}
-
-echo 'ip.ttl--;' > expout
-echo 'eth.src = 00:00:02:01:02:03;' >> expout
-echo 'eth.dst = f0:00:00:01:02:04;' >> expout
-echo 'output("ln-alice");' >> expout
-AT_CHECK_UNQUOTED([ovn_trace foo 'inport == "foo1" && eth.src == f0:00:00:01:02:03 && eth.dst == 00:00:01:01:02:03 && ip4.src == 192.168.1.2 && ip4.dst == 172.16.1.3 && ip.ttl == 0xff'], [0], [expout])
-
-# Create logical port alice1 in alice on hv1
-as hv1 ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=alice1 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=1
-
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.4"
-
-# Create logical port foo2 in foo on hv2
-as hv2 ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=foo2 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-ovn-nbctl lsp-add foo foo2 \
--- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3"
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-: > hv1-vif2.expected
-
-# Send ip packet between alice1 and foo2
-src_mac="f00000010205"
-dst_mac="000002010203"
-src_ip=`ip_to_hex 172 16 1 4`
-dst_ip=`ip_to_hex 192 168 1 3`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif2 $packet
-
-# Packet to Expect at foo2
-src_mac="000001010203"
-dst_mac="f00000010206"
-src_ip=`ip_to_hex 172 16 1 4`
-dst_ip=`ip_to_hex 192 168 1 3`
-expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000
-
-echo $expected >> hv2-vif1.expected
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [hv2-vif1.expected])
-
-AT_CHECK([ovn-sbctl --bare --columns _uuid find Port_Binding logical_port=cr-alice | wc -l], [0], [1
-])
-
-ovn-nbctl --timeout=3 --wait=sb remove Logical_Router_Port alice options redirect-chassis
-
-AT_CHECK([ovn-sbctl find Port_Binding logical_port=cr-alice | wc -l], [0], [0
-])
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- send gratuitous arp for NAT rules on distributed router])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-# Create logical switches
-ovn-nbctl ls-add ls0
-ovn-nbctl ls-add ls1
-# Create distributed router
-ovn-nbctl create Logical_Router name=lr0
-# Add distributed gateway port to distributed router
-ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.1/24 \
- -- set Logical_Router_Port lrp0 options:redirect-chassis="hv2"
-ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \
- type=router options:router-port=lrp0 addresses="router"
-# Add router port to ls1
-ovn-nbctl lrp-add lr0 lrp1 f0:00:00:00:00:02 10.0.0.1/24
-ovn-nbctl lsp-add ls1 lrp1-rp -- set Logical_Switch_Port lrp1-rp \
- type=router options:router-port=lrp1 addresses="router"
-# Add logical ports for NAT rules
-ovn-nbctl lsp-add ls1 foo1 \
--- lsp-set-addresses foo1 "00:00:00:00:00:03 10.0.0.3"
-ovn-nbctl lsp-add ls1 foo2 \
--- lsp-set-addresses foo2 "00:00:00:00:00:04 10.0.0.4"
-# Add nat-addresses option
-ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
-# Add NAT rules
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.1 10.0.0.0/24])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 192.168.0.2 10.0.0.2])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 192.168.0.3 10.0.0.3 foo1 f0:00:00:00:00:03])
-AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 192.168.0.4 10.0.0.4 foo2 f0:00:00:00:00:04])
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-
-AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-AT_CHECK([ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-# Initially test with no bridge-mapping on hv2, expect to receive no packets
-
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-# Initially test with no bridge-mapping on hv3
-
-# Create a localnet port.
-AT_CHECK([ovn-nbctl lsp-add ls0 ln_port])
-AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
-AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
-AT_CHECK([ovn-nbctl --wait=hv lsp-set-options ln_port network_name=physnet1])
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 2
-
-# Expect no packets when hv2 bridge-mapping is not present
-: > packets
-OVN_CHECK_PACKETS([hv1/snoopvif-tx.pcap], [packets])
-
-# Add bridge-mapping on hv2
-AT_CHECK([as hv2 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-
-# Wait until the patch ports are created in hv2 to connect br-int to br-phys
-OVS_WAIT_UNTIL([test 1 = `as hv2 ovs-vsctl show | \
-grep "Port patch-br-int-to-ln_port" | wc -l`])
-
-# Wait for packets to be received.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80001000000000000c0a80001"
-echo $expected > expout
-expected="fffffffffffff0000000000108060001080006040001f00000000001c0a80002000000000000c0a80002"
-echo $expected >> expout
-AT_CHECK([sort packets], [0], [expout])
-sort packets | cat
-
-# Temporarily remove nat-addresses option to avoid race conditions
-# due to GARP backoff
-ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses=""
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-as hv1 reset_pcap_file snoopvif hv1/snoopvif
-
-# Add OVS ports for foo1 and foo2 on hv3
-ovs-vsctl -- add-port br-int hv3-vif1 -- \
- set interface hv3-vif1 external-ids:iface-id=foo1 \
- ofport-request=1
-ovs-vsctl -- add-port br-int hv3-vif2 -- \
- set interface hv3-vif2 external-ids:iface-id=foo2 \
- ofport-request=2
-
-# Add bridge-mapping on hv3
-AT_CHECK([as hv3 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-
-# Wait until the patch ports are created in hv3 to connect br-int to br-phys
-OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-vsctl show | \
-grep "Port patch-br-int-to-ln_port" | wc -l`])
-
-# Re-add nat-addresses option
-ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
-
-# Wait for packets to be received.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 250])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros > packets
-garp_1="fffffffffffff0000000000308060001080006040001f00000000003c0a80003000000000000c0a80003"
-echo $garp_1 > expout
-garp_2="fffffffffffff0000000000408060001080006040001f00000000004c0a80004000000000000c0a80004"
-echo $garp_2 >> expout
-
-cat packets | grep $garp_1 | head -1 > exp
-cat packets | grep $garp_2 | head -1 >> exp
-AT_CHECK([cat exp], [0], [expout])
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-
-AT_CLEANUP
-
-# VLAN traffic for external network redirected through distributed router
-# gateway port should use vlans(i.e input network vlan tag) across hypervisors
-# instead of tunneling.
-AT_SETUP([ovn -- vlan traffic for external network with distributed router gateway port])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# # One LR R1 that has switches foo (192.168.1.0/24) and
-# # alice (172.16.1.0/24) connected to it. The logical port
-# # between R1 and alice has a "redirect-chassis" specified,
-# # i.e. it is the distributed router gateway port(172.16.1.6).
-# # Switch alice also has a localnet port defined.
-# # An additional switch outside has the same subnet as alice
-# # (172.16.1.0/24), a localnet port and nexthop port(172.16.1.1)
-# # which will receive the packet destined for external network
-# # (i.e 8.8.8.8 as destination ip).
-
-# Physical network:
-# # Four hypervisors hv[1234].
-# # hv1 hosts vif foo1.
-# # hv2 is the "redirect-chassis" that hosts the distributed router gateway port.
-# # Later to test GARPs for the router port - foo, hv2 and hv4 are added to the ha_chassis_group
-# # hv3 hosts nexthop port vif outside1.
-# # All other tests connect hypervisors to network n1 through br-phys for tunneling.
-# # But in this test, hv1 won't connect to n1(and no br-phys in hv1), and
-# # in order to show vlans(instead of tunneling) used between hv1 and hv2,
-# # a new network n2 created and hv1 and hv2 connected to this network through br-ex.
-# # hv2 and hv3 are still connected to n1 network through br-phys.
-net_add n1
-
-# We are not calling ovn_attach for hv1, to avoid adding br-phys.
-# Tunneling won't work in hv1 as ovn-encap-ip is not added to any bridge in hv1
-sim_add hv1
-as hv1
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve,vxlan \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=192.168.0.1 \
- -- add-br br-int \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \
- -- set Open_vSwitch . external-ids:ovn-bridge-mappings=public:br-ex
-
-start_daemon ovn-controller
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings="public:br-ex,phys:br-phys"
-
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl -- add-port br-int hv3-vif1 -- \
- set interface hv3-vif1 external-ids:iface-id=outside1 \
- options:tx_pcap=hv3/vif1-tx.pcap \
- options:rxq_pcap=hv3/vif1-rx.pcap \
- ofport-request=1
-ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings="phys:br-phys"
-
-sim_add hv4
-as hv4
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.4
-ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings="public:br-ex,phys:br-phys"
-
-# Create network n2 for vlan connectivity between hv1 and hv2
-net_add n2
-
-as hv1
-ovs-vsctl add-br br-ex
-net_attach n2 br-ex
-
-as hv2
-ovs-vsctl add-br br-ex
-net_attach n2 br-ex
-
-as hv4
-ovs-vsctl add-br br-ex
-net_attach n2 br-ex
-
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add outside
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect alice to R1 as distributed router gateway port (172.16.1.6) on hv2
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.6/24 \
- -- set Logical_Router_Port alice options:redirect-chassis="hv2"
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router \
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port outside1 in outside, which is a nexthop address
-# for 172.16.1.0/24
-ovn-nbctl lsp-add outside outside1 \
--- lsp-set-addresses outside1 "f0:00:00:01:02:04 172.16.1.1"
-
-# Set default gateway (nexthop) to 172.16.1.1
-ovn-nbctl lr-route-add R1 "0.0.0.0/0" 172.16.1.1 alice
-AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.6 192.168.1.1/24])
-ovn-nbctl set Logical_Switch_Port rp-alice options:nat-addresses=router
-
-ovn-nbctl lsp-add foo ln-foo
-ovn-nbctl lsp-set-addresses ln-foo unknown
-ovn-nbctl lsp-set-options ln-foo network_name=public
-ovn-nbctl lsp-set-type ln-foo localnet
-AT_CHECK([ovn-nbctl set Logical_Switch_Port ln-foo tag=2])
-
-# Create localnet port in alice
-ovn-nbctl lsp-add alice ln-alice
-ovn-nbctl lsp-set-addresses ln-alice unknown
-ovn-nbctl lsp-set-type ln-alice localnet
-ovn-nbctl lsp-set-options ln-alice network_name=phys
-
-# Create localnet port in outside
-ovn-nbctl lsp-add outside ln-outside
-ovn-nbctl lsp-set-addresses ln-outside unknown
-ovn-nbctl lsp-set-type ln-outside localnet
-ovn-nbctl lsp-set-options ln-outside network_name=phys
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-ovn-nbctl --wait=hv --timeout=3 sync
-
-# Check that there is a logical flow in logical switch foo's pipeline
-# to set the outport to rp-foo (which is expected).
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
-grep rp-foo | grep -v is_chassis_resident | wc -l`])
-
-# Set the option 'reside-on-redirect-chassis' for foo
-ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true
-# Check that there is a logical flow in logical switch foo's pipeline
-# to set the outport to rp-foo with the condition is_chassis_redirect.
-ovn-sbctl dump-flows foo
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
-grep rp-foo | grep is_chassis_resident | wc -l`])
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list nat
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-echo "---------------------"
-
-for chassis in hv1 hv2 hv3; do
- as $chassis
- echo "------ $chassis dump ----------"
- ovs-vsctl show br-int
- ovs-ofctl show br-int
- ovs-ofctl dump-flows br-int
- echo "--------------------------"
-done
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-foo1_ip=$(ip_to_hex 192 168 1 2)
-gw_ip=$(ip_to_hex 172 16 1 6)
-dst_ip=$(ip_to_hex 8 8 8 8)
-nexthop_ip=$(ip_to_hex 172 16 1 1)
-
-foo1_mac="f00000010203"
-foo_mac="000001010203"
-gw_mac="000002010203"
-nexthop_mac="f00000010204"
-
-# Send ip packet from foo1 to 8.8.8.8
-src_mac="f00000010203"
-dst_mac="000001010203"
-packet=${foo_mac}${foo1_mac}08004500001c0000000040110000${foo1_ip}${dst_ip}0035111100080000
-
-# Wait for GARPs announcing gw IP to arrive
-OVS_WAIT_UNTIL([
- test `as hv2 ovs-ofctl dump-flows br-int | grep table=66 | \
-grep actions=mod_dl_dst:f0:00:00:01:02:04 | wc -l` -eq 1
- ])
-
-# VLAN tagged packet with router port(192.168.1.1) MAC as destination MAC
-# is expected on bridge connecting hv1 and hv2
-expected=${foo_mac}${foo1_mac}8100000208004500001c0000000040110000${foo1_ip}${dst_ip}0035111100080000
-echo $expected > hv1-br-ex_n2.expected
-
-# Packet to Expect at outside1 i.e nexthop(172.16.1.1) port.
-# As connection tracking not enabled for this test, snat can't be done on the packet.
-# We still see foo1 as the source ip address. But source mac(gateway MAC) and
-# dest mac(nexthop mac) are properly configured.
-expected=${nexthop_mac}${gw_mac}08004500001c000000003f110100${foo1_ip}${dst_ip}0035111100080000
-echo $expected > hv3-vif1.expected
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-as hv1 reset_pcap_file br-ex_n2 hv1/br-ex_n2
-as hv3 reset_pcap_file hv3-vif1 hv3/vif1
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-sleep 2
-
-# On hv1, table 32 check that no packet goes via the tunnel port
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 \
-| grep "NXM_NX_TUN_ID" | grep -v n_packets=0 | wc -l], [0], [[0
-]])
-
-ip_packet() {
- grep "1010203f00000010203"
-}
-
-# Check vlan tagged packet on the bridge connecting hv1 and hv2 with the
-# foo1's mac.
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-ex_n2-tx.pcap | ip_packet | uniq > hv1-br-ex_n2
-cat hv1-br-ex_n2.expected > expout
-AT_CHECK([sort hv1-br-ex_n2], [0], [expout])
-
-# Check expected packet on nexthop interface
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/vif1-tx.pcap | grep ${foo1_ip}${dst_ip} | uniq > hv3-vif1
-cat hv3-vif1.expected > expout
-AT_CHECK([sort hv3-vif1], [0], [expout])
-
-# Test the GARP for the router port ip - 192.168.1.1
-ovn-nbctl --wait=sb ha-chassis-group-add hagrp1
-
-as hv1 reset_pcap_file hv1-vif1 hv1/vif1
-as hv2 reset_pcap_file br-ex_n2 hv2/br-ex_n2
-as hv4 reset_pcap_file br-ex_n2 hv4/br-ex_n2
-
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 hv2 30
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 hv4 20
-
-hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group name=hagrp1`
-ovn-nbctl remove logical_router_port alice options redirect-chassis
-ovn-nbctl --wait=sb set logical_router_port alice ha_chassis_group=$hagrp1_uuid
-
-# When hv2 claims the gw router port cr-alice, it should send out
-# GARP for 192.168.1.1 and it should be received by foo1 on hv1.
-
-# foo1 (on hv1) should receive GARP without VLAN tag
-exp_garp_on_foo1="ffffffffffff00000101020308060001080006040001000001010203c0a80101000000000000c0a80101"
-echo $exp_garp_on_foo1 > foo1.expout
-
-# ovn-controller on hv2 should send garp with VLAN tag
-sent_garp="ffffffffffff0000010102038100000208060001080006040001000001010203c0a80101000000000000c0a80101"
-
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [foo1.expout])
-# Wait until we receive atleast 1 packet
-OVS_WAIT_UNTIL([test 1=`$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-ex_n2-tx.pcap | wc -l`])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-ex_n2-tx.pcap | head -1 > packets
-echo $sent_garp > expout
-AT_CHECK([cat packets], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv4/br-ex_n2-tx.pcap > empty
-AT_CHECK([cat empty], [0], [])
-
-# Make hv4 master
-as hv1 reset_pcap_file hv1-vif1 hv1/vif1
-as hv4 reset_pcap_file br-ex_n2 hv4/br-ex_n2
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 hv4 40
-
-# Wait till cr-alice is claimed by hv4
-hv4_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=hv4)
-# check that the chassis redirect port has been claimed by the gw1 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-alice | grep $hv4_chassis | wc -l], [0],[[1
-]])
-
-# Reset the pcap file for hv2/br-ex_n2. From now on ovn-controller in hv2
-# should not send GARPs for the router ports.
-as hv2 reset_pcap_file br-ex_n2 hv2/br-ex_n2
-
-echo $sent_garp > br-ex_n2.expout
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [foo1.expout])
-OVN_CHECK_PACKETS([hv4/br-ex_n2-tx.pcap], [br-ex_n2.expout])
-
-sleep 2
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-ex_n2-tx.pcap > empty
-AT_CHECK([cat empty], [0], [])
-
-OVN_CLEANUP([hv1],[hv2],[hv3], [hv4])
-AT_CLEANUP
-
-AT_SETUP([ovn -- IPv6 ND Router Solicitation responder])
-AT_KEYWORDS([ovn-nd_ra])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# In this test case we create 1 lswitch with 3 VIF ports attached,
-# and a lrouter connected to the lswitch.
-# We generate the Router solicitation packet and verify the Router Advertisement
-# reply packet from the ovn-controller.
-
-# Create hypervisor and logical switch lsw0, logical router lr0, attach lsw0
-# onto lr0, set Logical_Router_Port.ipv6_ra_configs:address_mode column to
-# 'slaac' to allow lrp0 send RA for SLAAC mode.
-ovn-nbctl ls-add lsw0
-ovn-nbctl lr-add lr0
-ovn-nbctl lrp-add lr0 lrp0 fa:16:3e:00:00:01 fdad:1234:5678::1/64
-ovn-nbctl set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode="slaac"
-ovn-nbctl \
- -- lsp-add lsw0 lsp0 \
- -- set Logical_Switch_Port lsp0 type=router \
- options:router-port=lrp0 \
- addresses='"fa:16:3e:00:00:01 fdad:1234:5678::1"'
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-
-ovn-nbctl lsp-add lsw0 lp1
-ovn-nbctl lsp-set-addresses lp1 "fa:16:3e:00:00:02 10.0.0.12 fdad:1234:5678:0:f816:3eff:fe:2"
-ovn-nbctl lsp-set-port-security lp1 "fa:16:3e:00:00:02 10.0.0.12 fdad:1234:5678:0:f816:3eff:fe:2"
-
-ovn-nbctl lsp-add lsw0 lp2
-ovn-nbctl lsp-set-addresses lp2 "fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3"
-ovn-nbctl lsp-set-port-security lp2 "fa:16:3e:00:00:03 10.0.0.13 fdad:1234:5678:0:f816:3eff:fe:3"
-
-ovn-nbctl lsp-add lsw0 lp3
-ovn-nbctl lsp-set-addresses lp3 "fa:16:3e:00:00:04 10.0.0.14 fdad:1234:5678:0:f816:3eff:fe:4"
-ovn-nbctl lsp-set-port-security lp3 "fa:16:3e:00:00:04 10.0.0.14 fdad:1234:5678:0:f816:3eff:fe:4"
-
-# Add ACL rule for ICMPv6 on lsw0
-ovn-nbctl acl-add lsw0 from-lport 1002 'ip6 && icmp6' allow-related
-ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp1" && ip6 && icmp6' allow-related
-ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp2" && ip6 && icmp6' allow-related
-ovn-nbctl acl-add lsw0 to-lport 1002 'outport == "lp3" && ip6 && icmp6' allow-related
-
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=lp2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-ovs-vsctl -- add-port br-int hv1-vif3 -- \
- set interface hv1-vif3 external-ids:iface-id=lp3 \
- options:tx_pcap=hv1/vif3-tx.pcap \
- options:rxq_pcap=hv1/vif3-rx.pcap \
- ofport-request=3
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-# Make sure that ovn-controller has installed the corresponding OF Flow.
-OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
-
-# This shell function sends a Router Solicitation packet.
-# test_ipv6_ra INPORT SRC_MAC SRC_LLA ADDR_MODE MTU RA_PREFIX_OPT
-test_ipv6_ra() {
- local inport=$1 src_mac=$2 src_lla=$3 addr_mode=$4 mtu=$5 prefix_opt=$6
- local request=333300000002${src_mac}86dd6000000000103aff${src_lla}ff02000000000000000000000000000285000efc000000000101${src_mac}
-
- local len=24
- local mtu_opt=""
- if test $mtu != 0; then
- len=`expr $len + 8`
- mtu_opt=05010000${mtu}
- fi
-
- if test ${#prefix_opt} != 0; then
- prefix_opt=${prefix_opt}fdad1234567800000000000000000000
- len=`expr $len + ${#prefix_opt} / 2`
- fi
-
- len=$(printf "%x" $len)
- local lrp_mac=fa163e000001
- local lrp_lla=fe80000000000000f8163efffe000001
- local reply=${src_mac}${lrp_mac}86dd6000000000${len}3aff${lrp_lla}${src_lla}8600XXXXff${addr_mode}ffff00000000000000000101${lrp_mac}${mtu_opt}${prefix_opt}
- echo $reply >> $inport.expected
-
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $request
-}
-
-AT_CAPTURE_FILE([ofctl_monitor0.log])
-as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0.log
-
-# MTU is not set and the address mode is set to slaac
-addr_mode=00
-default_prefix_option_config=030440c0ffffffffffffffff00000000
-src_mac=fa163e000002
-src_lla=fe80000000000000f8163efffe000002
-test_ipv6_ra 1 $src_mac $src_lla $addr_mode 0 $default_prefix_option_config
-
-# NXT_RESUME should be 1.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-
-cat 1.expected | cut -c -112 > expout
-AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
-
-# Skipping the ICMPv6 checksum.
-cat 1.expected | cut -c 117- > expout
-AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])
-
-rm -f *.expected
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-reset_pcap_file hv1-vif3 hv1/vif3
-
-# Set the MTU to 1500
-ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:mtu=1500
-
-# Make sure that ovn-controller has installed the corresponding OF Flow.
-OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
-
-addr_mode=00
-default_prefix_option_config=030440c0ffffffffffffffff00000000
-src_mac=fa163e000003
-src_lla=fe80000000000000f8163efffe000003
-mtu=000005dc
-
-test_ipv6_ra 2 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config
-
-# NXT_RESUME should be 2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-
-cat 2.expected | cut -c -112 > expout
-AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])
-
-# Skipping the ICMPv6 checksum.
-cat 2.expected | cut -c 117- > expout
-AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])
-
-rm -f *.expected
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-reset_pcap_file hv1-vif3 hv1/vif3
-
-# Set the address mode to dhcpv6_stateful
-ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateful
-# Make sure that ovn-controller has installed the corresponding OF Flow.
-OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
-
-addr_mode=80
-default_prefix_option_config=03044080ffffffffffffffff00000000
-src_mac=fa163e000004
-src_lla=fe80000000000000f8163efffe000004
-mtu=000005dc
-
-test_ipv6_ra 3 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config
-
-# NXT_RESUME should be 3.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif3-tx.pcap > 3.packets
-
-cat 3.expected | cut -c -112 > expout
-AT_CHECK([cat 3.packets | cut -c -112], [0], [expout])
-
-# Skipping the ICMPv6 checksum.
-cat 3.expected | cut -c 117- > expout
-AT_CHECK([cat 3.packets | cut -c 117-], [0], [expout])
-
-rm -f *.expected
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-reset_pcap_file hv1-vif3 hv1/vif3
-
-# Set the address mode to dhcpv6_stateless
-ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateless
-# Make sure that ovn-controller has installed the corresponding OF Flow.
-OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
-
-addr_mode=40
-default_prefix_option_config=030440c0ffffffffffffffff00000000
-src_mac=fa163e000002
-src_lla=fe80000000000000f8163efffe000002
-mtu=000005dc
-
-test_ipv6_ra 1 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config
-
-# NXT_RESUME should be 4.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-
-cat 1.expected | cut -c -112 > expout
-AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
-
-# Skipping the ICMPv6 checksum.
-cat 1.expected | cut -c 117- > expout
-AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])
-
-rm -f *.expected
-reset_pcap_file hv1-vif1 hv1/vif1
-reset_pcap_file hv1-vif2 hv1/vif2
-reset_pcap_file hv1-vif3 hv1/vif3
-
-# Set the address mode to invalid.
-ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=invalid
-# Make sure that ovn-controller has not installed any OF Flow for IPv6 ND RA.
-OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
-
-addr_mode=40
-default_prefix_option_config=""
-src_mac=fa163e000002
-src_lla=fe80000000000000f8163efffe000002
-mtu=000005dc
-
-test_ipv6_ra 1 $src_mac $src_lla $addr_mode $mtu $default_prefix_option_config
-
-# NXT_RESUME should be 4 only.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
-AT_CHECK([cat 1.packets], [0], [])
-
-OVN_CLEANUP([hv1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- /32 router IP address])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# 2 LS 'foo' and 'alice' connected via router R1.
-# R1 connects to 'alice' with a /32 IP address. We use static routes and
-# nexthop to push traffic to a logical port in switch 'alice'
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:00:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo type=router \
- options:router-port=foo addresses=\"00:00:00:01:02:03\"
-
-# Connect alice to R1.
-ovn-nbctl lrp-add R1 alice 00:00:00:01:02:04 172.16.1.1/32
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:00:01:02:04\"
-
-# Create logical port foo1 in foo
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical port alice1 in alice
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 10.0.0.2"
-
-#install default route in R1 to use alice1's IP address as nexthop
-ovn-nbctl lr-route-add R1 0.0.0.0/0 10.0.0.2 alice
-
-# Create two hypervisor and create OVS ports corresponding to logical ports.
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=foo1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=alice1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-# Send ip packets between foo1 and alice1
-src_mac="f00000010203"
-dst_mac="000000010203"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 10 0 0 2`
-packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
-
-# Send the first packet to trigger a ARP response and population of
-# mac_bindings table.
-as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="10.0.0.2" | wc -l` -gt 0])
-ovn-nbctl --wait=hv sync
-
-# Packet to Expect at 'alice1'
-src_mac="000000010204"
-dst_mac="f00000010204"
-src_ip=`ip_to_hex 192 168 1 2`
-dst_ip=`ip_to_hex 10 0 0 2`
-echo "${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}0035111100080000" > expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 HVs, 1 lport/HV, localport ports])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add ls1
-
-# Add localport to the switch
-ovn-nbctl lsp-add ls1 lp01
-ovn-nbctl lsp-set-addresses lp01 f0:00:00:00:00:01
-ovn-nbctl lsp-set-type lp01 localport
-
-net_add n1
-
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
- ovs-vsctl add-port br-int vif01 -- \
- set Interface vif01 external-ids:iface-id=lp01 \
- options:tx_pcap=hv${i}/vif01-tx.pcap \
- options:rxq_pcap=hv${i}/vif01-rx.pcap \
- ofport-request=${i}0
-
- ovs-vsctl add-port br-int vif${i}1 -- \
- set Interface vif${i}1 external-ids:iface-id=lp${i}1 \
- options:tx_pcap=hv${i}/vif${i}1-tx.pcap \
- options:rxq_pcap=hv${i}/vif${i}1-rx.pcap \
- ofport-request=${i}1
-
- ovn-nbctl lsp-add ls1 lp${i}1
- ovn-nbctl lsp-set-addresses lp${i}1 f0:00:00:00:00:${i}1
- ovn-nbctl lsp-set-port-security lp${i}1 f0:00:00:00:00:${i}1
-
- OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp${i}1` = xup])
-done
-
-ovn-nbctl --wait=sb sync
-ovn-sbctl dump-flows
-
-OVN_POPULATE_ARP
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- echo hv${1%?}
-}
-#
-# test_packet INPORT DST SRC ETHTYPE EOUT LOUT DEFHV
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). INPORT is specified as
-# logical switch port numbers, e.g. 11 for vif11.
-#
-# EOUT is the end-to-end output port, that is, where the packet will end up
-# after possibly bouncing through one or more localnet ports. LOUT is the
-# logical output port, which might be a localnet port, as seen by ovn-trace
-# (which doesn't know what localnet ports are connected to and therefore can't
-# figure out the end-to-end answer).
-#
-# DEFHV is the default hypervisor from where the packet is going to be sent
-# if the source port is a localport.
-for i in 1 2; do
- for j in 0 1; do
- : > $i$j.expected
- done
-done
-test_packet() {
- local inport=$1 dst=$2 src=$3 eth=$4 eout=$5 lout=$6 defhv=$7
- echo "$@"
-
- # First try tracing the packet.
- uflow="inport==\"lp$inport\" && eth.dst==$dst && eth.src==$src && eth.type==0x$eth"
- if test $lout != drop; then
- echo "output(\"$lout\");"
- fi > expout
- AT_CAPTURE_FILE([trace])
- AT_CHECK([ovn-trace --all ls1 "$uflow" | tee trace | sed '1,/Minimal trace/d'], [0], [expout])
-
- # Then actually send a packet, for an end-to-end test.
- local packet=$(echo $dst$src | sed 's/://g')${eth}
- hv=`vif_to_hv $inport`
- # If hypervisor 0 (localport) use the defhv parameter
- if test $hv = hv0; then
- hv=$defhv
- fi
- vif=vif$inport
- as $hv ovs-appctl netdev-dummy/receive $vif $packet
- if test $eout != drop; then
- echo $packet >> ${eout#lp}.expected
- fi
-}
-
-
-# lp11 and lp21 are on different hypervisors
-test_packet 11 f0:00:00:00:00:21 f0:00:00:00:00:11 1121 lp21 lp21
-test_packet 21 f0:00:00:00:00:11 f0:00:00:00:00:21 2111 lp11 lp11
-
-# Both VIFs should be able to reach the localport on their own HV
-test_packet 11 f0:00:00:00:00:01 f0:00:00:00:00:11 1101 lp01 lp01
-test_packet 21 f0:00:00:00:00:01 f0:00:00:00:00:21 2101 lp01 lp01
-
-# Packet sent from localport on same hv should reach the vif
-test_packet 01 f0:00:00:00:00:11 f0:00:00:00:00:01 0111 lp11 lp11 hv1
-test_packet 01 f0:00:00:00:00:21 f0:00:00:00:00:01 0121 lp21 lp21 hv2
-
-# Packet sent from localport on different hv should be dropped
-test_packet 01 f0:00:00:00:00:21 f0:00:00:00:00:01 0121 drop lp21 hv1
-test_packet 01 f0:00:00:00:00:11 f0:00:00:00:00:01 0111 drop lp11 hv2
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2; do
- for j in 0 1; do
- OVN_CHECK_PACKETS([hv$i/vif$i$j-tx.pcap], [$i$j.expected])
- done
-done
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- 1 LR with HA distributed router gateway port])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-net_add n1
-
-# create gateways with external network connectivity
-
-for i in 1 2; do
- sim_add gw$i
- as gw$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
- ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-done
-
-ovn-nbctl ls-add inside
-ovn-nbctl ls-add outside
-
-# create hypervisors with a vif port each to an internal network
-
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.1$i
- ovs-vsctl -- add-port br-int hv$i-vif1 -- \
- set interface hv$i-vif1 external-ids:iface-id=inside$i \
- options:tx_pcap=hv$i/vif1-tx.pcap \
- options:rxq_pcap=hv$i/vif1-rx.pcap \
- ofport-request=1
-
- ovn-nbctl lsp-add inside inside$i \
- -- lsp-set-addresses inside$i "f0:00:00:01:22:$i 192.168.1.10$i"
-
-done
-
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R1
-
-# Connect inside to R1
-ovn-nbctl lrp-add R1 inside 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add inside rp-inside -- set Logical_Switch_Port rp-inside \
- type=router options:router-port=inside \
- -- lsp-set-addresses rp-inside router
-
-# Connect outside to R1 as distributed router gateway port on gw1+gw2
-ovn-nbctl lrp-add R1 outside 00:00:02:01:02:04 192.168.0.101/24
-
-ovn-nbctl --id=@gc0 create Gateway_Chassis \
- name=outside_gw1 chassis_name=gw1 priority=20 -- \
- --id=@gc1 create Gateway_Chassis \
- name=outside_gw2 chassis_name=gw2 priority=10 -- \
- set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]'
-
-ovn-nbctl lsp-add outside rp-outside -- set Logical_Switch_Port rp-outside \
- type=router options:router-port=outside \
- -- lsp-set-addresses rp-outside router
-
-# Create localnet port in outside
-ovn-nbctl lsp-add outside ln-outside
-ovn-nbctl lsp-set-addresses ln-outside unknown
-ovn-nbctl lsp-set-type ln-outside localnet
-ovn-nbctl lsp-set-options ln-outside network_name=phys
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-ovn-nbctl --wait=hv --timeout=3 sync
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-ovn-sbctl list encap
-echo "---------------------"
-echo "------ Gateway_Chassis dump (SBDB) -------"
-ovn-sbctl list Gateway_Chassis
-echo "------ Port_Binding chassisredirect -------"
-ovn-sbctl find Port_Binding type=chassisredirect
-echo "-------------------------------------------"
-
-# There should be one ha_chassis_group with the name "outside"
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
-ha_chassis_group name="outside"`
-
-AT_CHECK([test $ha_chassi_grp_name = outside])
-
-# There should be 2 ha_chassis rows in SB DB.
-AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | awk '{print $3}' \
-| grep '-' | wc -l ], [0], [2
-])
-
-ha_ch=`ovn-sbctl --bare --columns ha_chassis find ha_chassis_group`
-# Trim the spaces.
-ha_ch=`echo $ha_ch | sed 's/ //g'`
-
-ha_ch_list=''
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
-do
- ha_ch_list="$ha_ch_list $i"
-done
-
-# Trim the spaces.
-ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
-
-AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
-
-for chassis in gw1 gw2 hv1 hv2; do
- as $chassis
- echo "------ $chassis dump ----------"
- ovs-ofctl show br-int
- ovs-ofctl dump-flows br-int
- echo "--------------------------"
-done
-bfd_dump() {
- for chassis in gw1 gw2 hv1 hv2; do
- as $chassis
- echo "------ $chassis dump (BFD)----"
- echo "BFD (from $chassis):"
- # dump BFD config and status to the other chassis
- for chassis2 in gw1 gw2 hv1 hv2; do
- if [[ "$chassis" != "$chassis2" ]]; then
- echo " -> $chassis2:"
- echo " $(ovs-vsctl --bare --columns bfd,bfd_status find Interface name=ovn-$chassis2-0)"
- fi
- done
- echo "--------------------------"
- done
-}
-
-bfd_dump
-
-hv1_gw1_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0)
-hv1_gw2_ofport=$(as hv1 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0)
-hv2_gw1_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw1-0)
-hv2_gw2_ofport=$(as hv2 ovs-vsctl --bare --columns ofport find Interface name=ovn-gw2-0)
-
-echo $hv1_gw1_ofport
-echo $hv1_gw2_ofport
-echo $hv2_gw1_ofport
-echo $hv2_gw2_ofport
-
-echo "--- hv1 ---"
-as hv1 ovs-ofctl dump-flows br-int table=32
-
-echo "--- hv2 ---"
-as hv2 ovs-ofctl dump-flows br-int table=32
-
-gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1)
-gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
-
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
-| wc -l], [0], [1
-])
-
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport \
-| wc -l], [0], [1
-])
-
-# make sure that flows for handling the outside router port reside on gw1
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=25 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=25 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[0
-]])
-
-# make sure ARP responder flows for outside router port reside on gw1 too
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=9 | \
-grep arp_tpa=192.168.0.101 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[0
-]])
-
-# check that the chassis redirect port has been claimed by the gw1 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
-]])
-
-hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
-hv2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv2"`
-
-exp_ref_ch_list=''
-for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
-do
- if test $i = $hv1_ch_uuid; then
- exp_ref_ch_list="${exp_ref_ch_list}$i"
- elif test $i = $hv2_ch_uuid; then
- exp_ref_ch_list="${exp_ref_ch_list}$i"
- fi
-done
-
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-
-# at this point, we invert the priority of the gw chassis between gw1 and gw2
-
-ovn-nbctl --id=@gc0 create Gateway_Chassis \
- name=outside_gw1 chassis_name=gw1 priority=10 -- \
- --id=@gc1 create Gateway_Chassis \
- name=outside_gw2 chassis_name=gw2 priority=20 -- \
- set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]'
-
-
-# XXX: Let the change propagate down to the ovn-controllers
-ovn-nbctl --wait=hv --timeout=3 sync
-
-# we make sure that the hypervisors noticed, and inverted the slave ports
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport \
-| wc -l], [0], [1
-])
-
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport \
-| wc -l], [0], [1
-])
-
-# check that the chassis redirect port has been reclaimed by the gw2 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-outside | grep $gw2_chassis | wc -l], [0],[[1
-]])
-
-# check BFD enablement on tunnel ports from gw1 #########
-as gw1
-for chassis in gw2 hv1 hv2; do
- echo "checking gw1 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-
-
-# check BFD enablement on tunnel ports from gw2 ##########
-as gw2
-for chassis in gw1 hv1 hv2; do
- echo "checking gw2 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-
-# check BFD enablement on tunnel ports from hv1 ###########
-as hv1
-for chassis in gw1 gw2; do
- echo "checking hv1 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-# make sure BFD is not enabled to hv2, we don't need it
-AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv2-0],[0],
- [[
-]])
-
-
-# check BFD enablement on tunnel ports from hv2 ##########
-as hv2
-for chassis in gw1 gw2; do
- echo "checking hv2 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-# make sure BFD is not enabled to hv1, we don't need it
-AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv1-0],[0],
- [[
-]])
-
-# make sure that flows for handling the outside router port reside on gw2 now
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=25 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=25 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[0
-]])
-
-# disconnect GW2 from the network, GW1 should take over
-as gw2
-port=${sandbox}_br-phys
-as main ovs-vsctl del-port n1 $port
-
-bfd_dump
-
-# make sure that flows for handling the outside router port reside on gw2 now
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=25 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=25 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[0
-]])
-
-# check that the chassis redirect port has been reclaimed by the gw1 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
-]])
-
-ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-rx"=2000
-as gw2
-for chassis in gw1 hv1 hv2; do
- echo "checking gw2 -> $chassis"
- OVS_WAIT_UNTIL([
- bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
- test "$bfd_cfg" = "enable=true min_rx=2000"
-])
-done
-ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-tx"=1500
-for chassis in gw1 hv1 hv2; do
- echo "checking gw2 -> $chassis"
- OVS_WAIT_UNTIL([
- bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
- test "$bfd_cfg" = "enable=true min_rx=2000 min_tx=1500"
-])
-done
-ovn-nbctl remove NB_Global . options "bfd-min-rx"
-ovn-nbctl --wait=hv set NB_Global . options:"bfd-mult"=5
-for chassis in gw1 hv1 hv2; do
- echo "checking gw2 -> $chassis"
- OVS_WAIT_UNTIL([
- bfd_cfg=$(ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0)
- test "$bfd_cfg" = "enable=true min_tx=1500 mult=5"
-])
-done
-
-# Delete the inside1 vif. The ref_chassis in ha_chassis_group shouldn't have
-# reference to hv1.
-as hv1 ovs-vsctl del-port hv1-vif1
-
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$hv2_ch_uuid" = "$ref_ch_list"])
-
-# Delete the inside2 vif.
-ovn-sbctl show
-
-echo "Deleting hv2-vif1"
-as hv2 ovs-vsctl del-port hv2-vif1
-
-# ref_chassis of ha_chassis_group should be empty
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- exp_ref_ch_list=""
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-# Delete the Gateway_Chassis for lrp - outside
-ovn-nbctl clear Logical_Router_Port outside gateway_chassis
-
-# There shoud be no ha_chassis_group rows in SB DB.
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
-
-ovn-nbctl remove NB_Global . options "bfd-min-rx"
-ovn-nbctl remove NB_Global . options "bfd-min-tx"
-ovn-nbctl remove NB_Global . options "bfd-mult"
-
-# Now test with HA chassis group instead of Gateway chassis in NB DB
-ovn-nbctl --wait=sb ha-chassis-group-add hagrp1
-
-ovn-nbctl list ha_chassis_group
-ovn-nbctl --bare --columns _uuid find ha_chassis_group name=hagrp1
-hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group name=hagrp1`
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 gw1 30
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 gw2 20
-
-# ovn-northd should not create HA chassis group and HA chassis rows
-# unless the HA chassis group in OVN NB DB is associated to
-# a logical router port.
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
-
-# Associate hagrp1 to outside logical router port
-ovn-nbctl set Logical_Router_Port outside ha_chassis_group=$hagrp1_uuid
-
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid \
-find ha_chassis_group | wc -l`])
-
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
-grep -v chassis-name | wc -l`])
-
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
-| wc -l], [0], [1
-])
-
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport \
-| wc -l], [0], [1
-])
-
-# make sure that flows for handling the outside router port reside on gw1
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=24 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=24 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[0
-]])
-
-# make sure ARP responder flows for outside router port reside on gw1 too
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=9 | \
-grep arp_tpa=192.168.0.101 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[0
-]])
-
-# check that the chassis redirect port has been claimed by the gw1 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
-]])
-
-# Re add the ovs ports.
-for i in 1 2; do
- as hv$i
- ovs-vsctl -- add-port br-int hv$i-vif1 -- \
- set interface hv$i-vif1 external-ids:iface-id=inside$i \
- options:tx_pcap=hv$i/vif1-tx.pcap \
- options:rxq_pcap=hv$i/vif1-rx.pcap \
- ofport-request=1
-done
-
-hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
-hv2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv2"`
-
-exp_ref_ch_list=''
-for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
-do
- if test $i = $hv1_ch_uuid; then
- exp_ref_ch_list="${exp_ref_ch_list}$i"
- elif test $i = $hv2_ch_uuid; then
- exp_ref_ch_list="${exp_ref_ch_list}$i"
- fi
-done
-
-OVS_WAIT_UNTIL(
- [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
- # Trim the spaces.
- ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
- test "$exp_ref_ch_list" = "$ref_ch_list"])
-
-# Increase the priority of gw2
-ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 gw2 40
-
-OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport \
-| wc -l], [0], [1
-])
-
-OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
-grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport \
-| wc -l], [0], [1
-])
-
-# check that the chassis redirect port has been reclaimed by the gw2 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-outside | grep $gw2_chassis | wc -l], [0],[[1
-]])
-
-# check BFD enablement on tunnel ports from gw1 #########
-as gw1
-for chassis in gw2 hv1 hv2; do
- echo "checking gw1 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-
-# check BFD enablement on tunnel ports from gw2 ##########
-as gw2
-for chassis in gw1 hv1 hv2; do
- echo "checking gw2 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-
-# check BFD enablement on tunnel ports from hv1 ###########
-as hv1
-for chassis in gw1 gw2; do
- echo "checking hv1 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-# make sure BFD is not enabled to hv2, we don't need it
-AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv2-0],[0],
- [[
-]])
-
-# check BFD enablement on tunnel ports from hv2 ##########
-as hv2
-for chassis in gw1 gw2; do
- echo "checking hv2 -> $chassis"
- AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-$chassis-0],[0],
- [[enable=true
-]])
-done
-# make sure BFD is not enabled to hv1, we don't need it
-AT_CHECK([ovs-vsctl --bare --columns bfd find Interface name=ovn-hv1-0],[0],
- [[
-]])
-
-# make sure that flows for handling the outside router port reside on gw2 now
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=24 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=24 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[0
-]])
-
-# disconnect GW2 from the network, GW1 should take over
-as gw2
-port=${sandbox}_br-phys
-as main ovs-vsctl del-port n1 $port
-
-bfd_dump
-
-# make sure that flows for handling the outside router port reside on gw2 now
-OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=24 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[1
-]])
-OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=24 | \
-grep 00:00:02:01:02:04 | wc -l], [0], [[0
-]])
-
-# check that the chassis redirect port has been reclaimed by the gw1 chassis
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
-]])
-
-OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- send gratuitous ARP for NAT rules on HA distributed router])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-ovn-nbctl ls-add ls0
-ovn-nbctl ls-add ls1
-ovn-nbctl create Logical_Router name=lr0
-ovn-nbctl lrp-add lr0 lrp0 f0:00:00:00:00:01 192.168.0.100/24
-
-ovn-nbctl --id=@gc0 create Gateway_Chassis \
- name=outside_gw1 chassis_name=hv2 priority=10 -- \
- --id=@gc1 create Gateway_Chassis \
- name=outside_gw2 chassis_name=hv3 priority=1 -- \
- set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]'
-
-ovn-nbctl lsp-add ls0 lrp0-rp -- set Logical_Switch_Port lrp0-rp \
- type=router options:router-port=lrp0 addresses="router"
-ovn-nbctl lrp-add lr0 lrp1 f0:00:00:00:00:02 10.0.0.1/24
-ovn-nbctl lsp-add ls1 lrp1-rp -- set Logical_Switch_Port lrp1-rp \
- type=router options:router-port=lrp1 addresses="router"
-
-# Add NAT rules
-AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 192.168.0.100 10.0.0.0/24])
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-AT_CHECK([ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-AT_CHECK([ovs-vsctl add-port br-phys snoopvif -- set Interface snoopvif options:tx_pcap=hv1/snoopvif-tx.pcap options:rxq_pcap=hv1/snoopvif-rx.pcap])
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-AT_CHECK([as hv2 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-
-sim_add hv3
-as hv3
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-AT_CHECK([as hv3 ovs-vsctl set Open_vSwitch . external-ids:ovn-bridge-mappings=physnet1:br-phys])
-
-# Create a localnet port.
-AT_CHECK([ovn-nbctl lsp-add ls0 ln_port])
-AT_CHECK([ovn-nbctl lsp-set-addresses ln_port unknown])
-AT_CHECK([ovn-nbctl lsp-set-type ln_port localnet])
-AT_CHECK([ovn-nbctl lsp-set-options ln_port network_name=physnet1])
-
-# wait for earlier changes to take effect
-AT_CHECK([ovn-nbctl --timeout=3 --wait=hv sync], [0], [ignore])
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-as hv1 reset_pcap_file snoopvif hv1/snoopvif
-as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
-as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
-# add nat-addresses option
-ovn-nbctl --wait=hv lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
-
-# Wait for packets to be received through hv2.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-only_broadcast_from_lrp1() {
- grep "fffffffffffff00000000001"
-}
-
-garp="fffffffffffff0000000000108060001080006040001f00000000001c0a80064000000000000c0a80064"
-echo $garp > expout
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv1_snoop_tx
-echo "packets on hv1-snoopvif:"
-cat hv1_snoop_tx
-AT_CHECK([sort hv1_snoop_tx], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx
-echo "packets on hv2 br-phys tx"
-cat hv2_br_phys_tx
-AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
-echo "packets on hv3 br-phys tx"
-cat hv3_br_phys_tx
-AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [])
-
-
-# at this point, we invert the priority of the gw chassis between hv2 and hv3
-
-ovn-nbctl --wait=hv \
- --id=@gc0 create Gateway_Chassis \
- name=outside_gw1 chassis_name=hv2 priority=1 -- \
- --id=@gc1 create Gateway_Chassis \
- name=outside_gw2 chassis_name=hv3 priority=10 -- \
- set Logical_Router_Port lrp0 'gateway_chassis=[@gc0,@gc1]'
-
-
-as hv1 reset_pcap_file snoopvif hv1/snoopvif
-as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
-as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
-
-# Wait for packets to be received.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv1_snoopvif_tx
-AT_CHECK([sort hv1_snoopvif_tx], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
-AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx
-AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [])
-
-# change localnet port tag.
-AT_CHECK([ovn-nbctl set Logical_Switch_Port ln_port tag=2014])
-
-# wait for earlier changes to take effect
-OVS_WAIT_UNTIL([test 1 = `as hv2 ovs-ofctl dump-flows br-int table=65 | \
-grep "actions=mod_vlan_vid:2014" | wc -l`
-])
-
-OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=65 | \
-grep "actions=mod_vlan_vid:2014" | wc -l`
-])
-
-# update nat-addresses option
-ovn-nbctl --wait=hv clear logical_switch_port lrp0-rp options
-
-#Wait until the Port_Binding.nat_addresses is cleared.
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns nat_addresses find port_binding \
-logical_port=lrp0-rp | grep is_chassis | wc -l`])
-
-as hv1 reset_pcap_file snoopvif hv1/snoopvif
-as hv2 reset_pcap_file br-phys_n1 hv2/br-phys_n1
-as hv3 reset_pcap_file br-phys_n1 hv3/br-phys_n1
-
-ovn-nbctl --wait=hv lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
-
-#Wait until the Port_Binding.nat_addresses is set.
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns nat_addresses find port_binding \
-logical_port=lrp0-rp | grep is_chassis | wc -l`])
-
-# Wait for packets to be received.
-OVS_WAIT_UNTIL([test `wc -c < "hv1/snoopvif-tx.pcap"` -ge 100])
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-garp="fffffffffffff00000000001810007de08060001080006040001f00000000001c0a80064000000000000c0a80064"
-echo $garp > expout
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/snoopvif-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv1_snoopvif_tx
-AT_CHECK([sort hv1_snoopvif_tx], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv3/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv3_br_phys_tx
-AT_CHECK([grep $garp hv3_br_phys_tx | sort], [0], [expout])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap | trim_zeros | only_broadcast_from_lrp1 | uniq > hv2_br_phys_tx
-AT_CHECK([grep $garp hv2_br_phys_tx | sort], [0], [])
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- ensure one gw controller restart in HA doesn't bounce the master])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-net_add n1
-
-# create two gateways with external network connectivity
-for i in 1 2; do
- sim_add gw$i
- as gw$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
- ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-done
-
-ovn-nbctl ls-add inside
-ovn-nbctl ls-add outside
-
-# create one hypervisors with a vif port the internal network
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.11
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=inside1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovn-nbctl lsp-add inside inside1 \
- -- lsp-set-addresses inside1 "f0:00:00:01:22:01 192.168.1.101"
-
-
-OVN_POPULATE_ARP
-
-ovn-nbctl create Logical_Router name=R1
-
-# Connect inside to R1
-ovn-nbctl lrp-add R1 inside 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add inside rp-inside -- set Logical_Switch_Port rp-inside \
- type=router options:router-port=inside \
- -- lsp-set-addresses rp-inside router
-
-# Connect outside to R1 as distributed router gateway port on gw1+gw2
-ovn-nbctl lrp-add R1 outside 00:00:02:01:02:04 192.168.0.101/24
-
-ovn-nbctl --id=@gc0 create Gateway_Chassis \
- name=outside_gw1 chassis_name=gw1 priority=20 -- \
- --id=@gc1 create Gateway_Chassis \
- name=outside_gw2 chassis_name=gw2 priority=10 -- \
- set Logical_Router_Port outside 'gateway_chassis=[@gc0,@gc1]'
-
-ovn-nbctl lsp-add outside rp-outside -- set Logical_Switch_Port rp-outside \
- type=router options:router-port=outside \
- -- lsp-set-addresses rp-outside router
-
-# Create localnet port in outside
-ovn-nbctl lsp-add outside ln-outside
-ovn-nbctl lsp-set-addresses ln-outside unknown
-ovn-nbctl lsp-set-type ln-outside localnet
-ovn-nbctl lsp-set-options ln-outside network_name=phys
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-ovn-nbctl --wait=hv --timeout=3 sync
-
-# currently when ovn-controller is restarted, the old entry is deleted
-# and a new one is created, which leaves the Gateway_Chassis with
-# an empty chassis for a while. NOTE: restarting ovn-controller in tests
-# doesn't have the same effect because "name" is conserved, and the
-# Chassis entry is not replaced.
-
-> gw1/ovn-controller.log
-
-gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
-ovn-sbctl destroy Chassis $gw2_chassis
-
-OVS_WAIT_UNTIL([test 0 = `grep -c "Releasing lport" gw1/ovn-controller.log`])
-
-OVN_CLEANUP([gw1],[gw2],[hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- IPv6 Neighbor Solicitation for unknown MAC])
-AT_KEYWORDS([ovn-nd_ns for unknown mac])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add sw0_ip6
-ovn-nbctl lsp-add sw0_ip6 sw0_ip6-port1
-ovn-nbctl lsp-set-addresses sw0_ip6-port1 \
-"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002"
-
-ovn-nbctl lsp-set-port-security sw0_ip6-port1 \
-"50:64:00:00:00:02 aef0::5264:00ff:fe00:0002"
-
-ovn-nbctl lr-add lr0_ip6
-ovn-nbctl lrp-add lr0_ip6 lrp0_ip6 00:00:00:00:af:01 aef0:0:0:0:0:0:0:0/64
-ovn-nbctl lsp-add sw0_ip6 lrp0_ip6-attachment
-ovn-nbctl lsp-set-type lrp0_ip6-attachment router
-ovn-nbctl lsp-set-addresses lrp0_ip6-attachment router
-ovn-nbctl lsp-set-options lrp0_ip6-attachment router-port=lrp0_ip6
-ovn-nbctl set logical_router_port lrp0_ip6 ipv6_ra_configs:address_mode=slaac
-
-ovn-nbctl ls-add public
-ovn-nbctl lsp-add public ln-public
-ovn-nbctl lsp-set-addresses ln-public unknown
-ovn-nbctl lsp-set-type ln-public localnet
-ovn-nbctl lsp-set-options ln-public network_name=phys
-
-ovn-nbctl lrp-add lr0_ip6 ip6_public 00:00:02:01:02:04 \
-2001:db8:1:0:200:02ff:fe01:0204/64 \
--- set Logical_Router_port ip6_public options:redirect-chassis="hv1"
-
-#install static route
-ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
-ip_prefix="\:\:/0" nexthop="2001\:db8\:1\:0\:200\:02ff\:fe01\:1305" \
--- add Logical_Router lr0_ip6 static_routes @lrt
-
-ovn-nbctl lsp-add public rp-ip6_public -- set Logical_Switch_Port \
-rp-ip6_public type=router options:router-port=ip6_public \
--- lsp-set-addresses rp-ip6_public router
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=sw0_ip6-port1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0_ip6-port1` = xup])
-
-# There should be 2 Neighbor Advertisement flows for the router port
-# aef0:: ip address in logical switch pipeline with action nd_na_router.
-AT_CHECK([ovn-sbctl dump-flows sw0_ip6 | grep ls_in_arp_rsp | \
-grep "nd_na_router" | wc -l], [0], [2
-])
-
-# There should be 4 Neighbor Advertisement flows with action nd_na_router
-# in the router pipeline for the router lr0_ip6.
-AT_CHECK([ovn-sbctl dump-flows lr0_ip6 | grep nd_na_router | \
-wc -l], [0], [4
-])
-
-cr_uuid=`ovn-sbctl find port_binding logical_port=cr-ip6_public | grep _uuid | cut -f2 -d ":"`
-
-# There is only one chassis.
-chassis_uuid=`ovn-sbctl list chassis | grep _uuid | cut -f2 -d ":"`
-OVS_WAIT_UNTIL([test $chassis_uuid = `ovn-sbctl get port_binding $cr_uuid chassis`])
-
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-# Test the IPv6 Neighbor Solicitation (NS) - nd_ns action for unknown MAC
-# addresses. ovn-controller should generate an IPv6 NS request for IPv6
-# packets whose MAC is unknown (in the ARP_REQUEST router pipeline stage.
-# test_ipv6 INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-# This function sends ipv6 packet
-test_ipv6() {
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8
-
- local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}
- packet=${packet}8000000000000000
-
- src_mac=000002010204
- expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${src_ip}
- expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000
- expected_packet=${expected_packet}${nd_target}0101${src_mac}
-
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif${inport} $packet
- rm -f ipv6_ns.expected
- echo $expected_packet >> ipv6_ns.expected
-}
-
-src_mac=506400000002
-dst_mac=00000000af01
-src_ip=aef0000000000000526400fffe000002
-dst_ip=20010db800010000020002fffe010205
-dst_mcast_mac=3333ff010205
-mcast_node_ip=ff0200000000000000000001ff010205
-nd_target=20010db800010000020002fffe010205
-# Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet
-# should be received by the ports attached to br-phys.
-test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
-$mcast_node_ip $nd_target
-
-OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
-OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap | \
-trim_zeros > 1.packets
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | \
-trim_zeros > 2.packets
-
-cat ipv6_ns.expected | cut -c -112 > expout
-AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
-AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])
-
-# Skipping the ICMPv6 checksum
-cat ipv6_ns.expected | cut -c 117- > expout
-AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])
-AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])
-
-# Now send a packet with destination ip other than
-# 2001:db8:1:0:200:02ff:fe01:0204/64 prefix.
-reset_pcap_file br-phys_n1 hv1/br-phys_n1
-reset_pcap_file br-phys hv1/br-phys
-
-src_mac=506400000002
-dst_mac=00000000af01
-src_ip=aef0000000000000526400fffe000002
-dst_ip=20020ab8000100000200020000020306
-# multicast mac of the nexthop IP - 2001:db8:1:0:200:02ff:fe01:1305
-dst_mcast_mac=3333ff011305
-mcast_node_ip=ff0200000000000000000001ff011305
-nd_target=20010db800010000020002fffe011305
-test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
-$mcast_node_ip $nd_target
-
-OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
-OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap | \
-trim_zeros > 1.packets
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | \
-trim_zeros > 2.packets
-
-cat ipv6_ns.expected | cut -c -112 > expout
-AT_CHECK([cat 1.packets | cut -c -112], [0], [expout])
-AT_CHECK([cat 2.packets | cut -c -112], [0], [expout])
-
-# Skipping the ICMPv6 checksum
-cat ipv6_ns.expected | cut -c 117- > expout
-AT_CHECK([cat 1.packets | cut -c 117-], [0], [expout])
-AT_CHECK([cat 2.packets | cut -c 117-], [0], [expout])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- options:requested-chassis for logical port])
-ovn_start
-
-net_add n1
-
-ovn-nbctl ls-add ls0
-ovn-nbctl lsp-add ls0 lsp0
-
-# create two hypervisors, each with one vif port
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.11
-ovs-vsctl -- add-port br-int hv1-vif0 -- \
-set Interface hv1-vif0 ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.12
-ovs-vsctl -- add-port br-int hv2-vif0 -- \
-set Interface hv2-vif0 ofport-request=1
-
-# Allow only chassis hv1 to bind logical port lsp0.
-ovn-nbctl lsp-set-options lsp0 requested-chassis=hv1
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-ovn-nbctl --wait=hv --timeout=3 sync
-
-# Retrieve hv1 and hv2 chassis UUIDs from southbound database
-ovn-sbctl wait-until chassis hv1
-ovn-sbctl wait-until chassis hv2
-hv1_uuid=$(ovn-sbctl --bare --columns _uuid find chassis name=hv1)
-hv2_uuid=$(ovn-sbctl --bare --columns _uuid find chassis name=hv2)
-
-# (1) Chassis hv2 should not bind lsp0 when requested-chassis is hv1.
-echo "verifying that hv2 does not bind lsp0 when hv2 physical/logical mapping is added"
-as hv2
-ovs-vsctl set interface hv2-vif0 external-ids:iface-id=lsp0
-
-OVS_WAIT_UNTIL([test 1 = $(grep -c "Not claiming lport lsp0" hv2/ovn-controller.log)])
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x], [0], [])
-
-# (2) Chassis hv2 should not add flows in OFTABLE_PHY_TO_LOG and OFTABLE_LOG_TO_PHY tables.
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=65 | grep output], [1], [])
-
-# (3) Chassis hv1 should bind lsp0 when physical to logical mapping exists on hv1.
-echo "verifying that hv1 binds lsp0 when hv1 physical/logical mapping is added"
-as hv1
-ovs-vsctl set interface hv1-vif0 external-ids:iface-id=lsp0
-
-OVS_WAIT_UNTIL([test 1 = $(grep -c "Claiming lport lsp0" hv1/ovn-controller.log)])
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x"$hv1_uuid"], [0], [])
-
-# (4) Chassis hv1 should add flows in OFTABLE_PHY_TO_LOG and OFTABLE_LOG_TO_PHY tables.
-as hv1 ovs-ofctl dump-flows br-int
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
-
-# (5) Chassis hv1 should release lsp0 binding and chassis hv2 should bind lsp0 when
-# the requested chassis for lsp0 is changed from hv1 to hv2.
-echo "verifying that lsp0 binding moves when requested-chassis is changed"
-
-ovn-nbctl lsp-set-options lsp0 requested-chassis=hv2
-OVS_WAIT_UNTIL([test 1 = $(grep -c "Releasing lport lsp0 from this chassis" hv1/ovn-controller.log)])
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x"$hv2_uuid"])
-
-# (6) Chassis hv2 should add flows and hv1 should not.
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
-
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep output], [1], [])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- options:requested-chassis with hostname])
-
-ovn_start
-
-ovn-nbctl ls-add ls0
-ovn-nbctl lsp-add ls0 lsp0
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.11
-ovs-vsctl -- add-port br-int hv1-vif0 -- set Interface hv1-vif0 ofport-request=1
-
-ovn-sbctl wait-until chassis hv1
-hv1_hostname=$(ovn-sbctl --bare --columns hostname find Chassis name=hv1)
-echo "hv1_hostname=${hv1_hostname}"
-ovn-nbctl --wait=hv --timeout=3 lsp-set-options lsp0 requested-chassis=${hv1_hostname}
-as hv1 ovs-vsctl set interface hv1-vif0 external-ids:iface-id=lsp0
-
-hv1_uuid=$(ovn-sbctl --bare --columns _uuid find Chassis name=hv1)
-echo "hv1_uuid=${hv1_uuid}"
-OVS_WAIT_UNTIL([test 1 = $(grep -c "Claiming lport lsp0" hv1/ovn-controller.log)])
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x"$hv1_uuid"], [0], [])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
-
-ovn-nbctl --wait=hv --timeout=3 lsp-set-options lsp0 requested-chassis=non-existant-chassis
-OVS_WAIT_UNTIL([test 1 = $(grep -c "Releasing lport lsp0 from this chassis" hv1/ovn-controller.log)])
-ovn-nbctl --wait=hv --timeout=3 sync
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x], [0], [])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep output], [1], [])
-
-OVN_CLEANUP([hv1])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- IPv6 periodic RA])
-ovn_start
-
-# This test sets up two hypervisors.
-# hv1 and hv2 run ovn-controllers, and
-# each has a VIF connected to the same
-# logical switch in OVN. The logical
-# switch is connected to a logical
-# router port that is configured to send
-# periodic router advertisements.
-#
-# The reason for having two ovn-controller
-# hypervisors is to ensure that the
-# periodic RAs being sent by each ovn-controller
-# are kept to their local hypervisors. If the
-# packets are not kept local, then each port
-# will receive too many RAs.
-
-net_add n1
-sim_add hv1
-sim_add hv2
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-
-ovn-nbctl lr-add ro
-ovn-nbctl lrp-add ro ro-sw 00:00:00:00:00:01 aef0:0:0:0:0:0:0:1/64
-
-ovn-nbctl ls-add sw
-ovn-nbctl lsp-add sw sw-ro
-ovn-nbctl lsp-set-type sw-ro router
-ovn-nbctl lsp-set-options sw-ro router-port=ro-sw
-ovn-nbctl lsp-set-addresses sw-ro 00:00:00:00:00:01
-ovn-nbctl lsp-add sw sw-p1
-ovn-nbctl lsp-set-addresses sw-p1 "00:00:00:00:00:02 aef0::200:ff:fe00:2"
-ovn-nbctl lsp-add sw sw-p2
-ovn-nbctl lsp-set-addresses sw-p2 "00:00:00:00:00:03 aef0::200:ff:fe00:3"
-
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:send_periodic=true
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=slaac
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:max_interval=4
-ovn-nbctl set Logical_Router_Port ro-sw ipv6_ra_configs:min_interval=3
-
-for i in 1 2 ; do
- as hv$i
- ovs-vsctl -- add-port br-int hv$i-vif1 -- \
- set interface hv$i-vif1 external-ids:iface-id=sw-p$i \
- options:tx_pcap=hv$i/vif1-tx.pcap \
- options:rxq_pcap=hv$i/vif1-rx.pcap \
- ofport-request=1
-done
-
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw-p1` = xup])
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw-p2` = xup])
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-
-}
-
-construct_expected_ra() {
- local src_mac=000000000001
- local dst_mac=333300000001
- local src_addr=fe80000000000000020000fffe000001
- local dst_addr=ff020000000000000000000000000001
-
- local mtu=$1
- local ra_mo=$2
- local ra_prefix_la=$3
-
- local slla=0101${src_mac}
- local mtu_opt=""
- if test $mtu != 0; then
- mtu_opt=05010000${mtu}
- fi
- shift 3
-
- local prefix=""
- while [[ $# -gt 0 ]] ; do
- local size=$1
- local net=$2
- prefix=${prefix}0304${size}${ra_prefix_la}ffffffffffffffff00000000${net}
- shift 2
- done
-
- local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}
- local icmp=8600XXXX${ra}
-
- local ip_len=$(expr ${#icmp} / 2)
- ip_len=$(echo "$ip_len" | awk '{printf "%0.4x\n", $0}')
-
- local ip=60000000${ip_len}3aff${src_addr}${dst_addr}${icmp}
- local eth=${dst_mac}${src_mac}86dd${ip}
- local packet=${eth}
- echo $packet >> expected
-}
-
-ra_test() {
- construct_expected_ra $@
-
- for i in hv1 hv2 ; do
- OVS_WAIT_WHILE([test 24 = $(wc -c $i/vif1-tx.pcap | cut -d " " -f1)])
-
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" $i/vif1-tx.pcap > packets
-
- cat expected | cut -c -112 > expout
- AT_CHECK([cat packets | cut -c -112], [0], [expout])
-
- # Skip ICMPv6 checksum.
- cat expected | cut -c 117- > expout
- AT_CHECK([cat packets | cut -c 117-], [0], [expout])
-
- rm -f packets
- as $i reset_pcap_file $i-vif1 $i/vif1
- done
-
- rm -f expected
-}
-
-# Baseline test with no MTU
-ra_test 0 00 c0 40 aef00000000000000000000000000000
-
-# Now make sure an MTU option makes it
-ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500
-ra_test 000005dc 00 c0 40 aef00000000000000000000000000000
-
-# Now test for multiple network prefixes
-ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48'
-ra_test 000005dc 00 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
-
-# Test a different address mode now
-ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful
-ra_test 000005dc 80 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
-
-# And the other address mode
-ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless
-ra_test 000005dc 40 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
-
-OVN_CLEANUP([hv1],[hv2])
-AT_CLEANUP
-
-AT_SETUP([ovn -- ACL reject rule test])
-AT_KEYWORDS([acl-reject])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# test_ip_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM EXP_IP_CHKSUM EXP_ICMP_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv4 packet with
-# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHKSUM as specified.
-# EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are the ip and icmp checksums of the icmp destination
-# unreachable frame generated from ACL rule hit
-#
-# INPORT is a lport number, e.g. 11 for vif11.
-# HV is a hypervisor number
-# ETH_SRC and ETH_DST are each 12 hex digits.
-# IPV4_SRC and IPV4_DST are each 8 hex digits.
-# IP_CHKSUM, EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits
-test_ip_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_chksum=$7
- local exp_ip_chksum=$8 exp_icmp_chksum=$9
- shift 9
-
- local ip_ttl=ff
- local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}
-
- local reply_icmp_ttl=ff
- local icmp_type_code_response=0301
- local icmp_data=00000000
- local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_data}
- local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload}
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# test_ipv6_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST EXP_ICMP_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv6 packet with
-# ETH_SRC, ETH_DST, IPV6_SRC, IPV6_DST as specified.
-# EXP_ICMP_CHKSUM is the icmp6 checksums of the icmp6 destination unreachable frame generated from ACL rule hit
-test_ipv6_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 exp_icmp_chksum=$7
- shift 7
-
- local ip6_hdr=6000000000083aff${ipv6_src}${ipv6_dst}
- local packet=${eth_dst}${eth_src}86dd${ip6_hdr}0000000000000000
-
- local reply=${eth_src}${eth_dst}86dd6000000000303aff${ipv6_dst}${ipv6_src}0101${exp_icmp_chksum}00000000${ip6_hdr}
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# test_tcp_syn_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST IP_CHKSUM TCP_SPORT TCP_DPORT TCP_CHKSUM EXP_IP_CHKSUM EXP_TCP_RST_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an TCP syn segment with
-# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHKSUM, TCP_SPORT, TCP_DPORT, TCP_CHKSUM as specified.
-# EXP_IP_CHKSUM and EXP_TCP_RST_CHKSUM are the ip and tcp checksums of the tcp reset segment generated from ACL rule hit
-#
-# INPORT is an lport number, e.g. 11 for vif11.
-# HV is an hypervisor number
-# ETH_SRC and ETH_DST are each 12 hex digits.
-# IPV4_SRC and IPV4_DST are each 8 hex digits.
-# TCP_SPORT and TCP_DPORT are 4 hex digits.
-# IP_CHKSUM, TCP_CHKSUM, EXP_IP_CHSUM and EXP_TCP_RST_CHKSUM are each 4 hex digits
-test_tcp_syn_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_chksum=$7
- local tcp_sport=$8 tcp_dport=$9 tcp_chksum=${10}
- local exp_ip_chksum=${11} exp_tcp_rst_chksum=${12}
- shift 12
-
- local ip_ttl=ff
- local packet=${eth_dst}${eth_src}08004500002800004000${ip_ttl}06${ip_chksum}${ipv4_src}${ipv4_dst}${tcp_sport}${tcp_dport}000000010000000050027210${tcp_chksum}0000
-
- local tcp_rst_ttl=ff
- local reply=${eth_src}${eth_dst}08004500002800004000${tcp_rst_ttl}06${exp_ip_chksum}${ipv4_dst}${ipv4_src}${tcp_dport}${tcp_sport}000000000000000150040000${exp_tcp_rst_chksum}0000
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# Create hypervisors hv[123].
-# Add vif1[123] to hv1, vif2[123] to hv2, vif3[123] to hv3.
-# Add all of the vifs to a single logical switch sw0.
-
-net_add n1
-ovn-nbctl ls-add sw0
-for i in 1 2 3; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-
- for j in 1 2 3; do
- ovn-nbctl lsp-add sw0 sw0-p$i$j -- \
- lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j"
-
- ovs-vsctl -- add-port br-int vif$i$j -- \
- set interface vif$i$j \
- external-ids:iface-id=sw0-p$i$j \
- options:tx_pcap=hv$i/vif$i$j-tx.pcap \
- options:rxq_pcap=hv$i/vif$i$j-rx.pcap \
- ofport-request=$i$j
- done
-done
-
-OVN_POPULATE_ARP
-# allow some time for ovn-northd and ovn-controller to catch up.
-sleep 1
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-for i in 1 2 3; do
- : > vif${i}1.expected
-done
-
-ovn-nbctl --log acl-add sw0 to-lport 1000 "outport == \"sw0-p12\"" reject
-ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p11\"" reject
-ovn-nbctl --log acl-add sw0 from-lport 1000 "inport == \"sw0-p21\"" reject
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-ovn-nbctl --timeout=3 --wait=hv sync
-
-test_ip_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(ip_to_hex 192 168 1 21) 0000 7d8d fcfe
-test_ip_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 7d8d fcfe
-test_ip_packet 31 3 000000000031 000000000012 $(ip_to_hex 192 168 1 31) $(ip_to_hex 192 168 1 12) 0000 7d82 fcfe
-
-test_ipv6_packet 11 1 000000000011 000000000021 fe80000000000000020001fffe000001 fe80000000000000020001fffe000002 6183
-
-test_tcp_syn_packet 11 1 000000000011 000000000021 $(ip_to_hex 192 168 1 11) $(ip_to_hex 192 168 1 21) 0000 8b40 3039 0000 7d8d 4486
-test_tcp_syn_packet 21 2 000000000021 000000000011 $(ip_to_hex 192 168 1 21) $(ip_to_hex 192 168 1 11) 0000 8b40 3039 0000 7d8d 4486
-test_tcp_syn_packet 31 3 000000000031 000000000012 $(ip_to_hex 192 168 1 31) $(ip_to_hex 192 168 1 12) 0000 8b40 3039 0000 7d82 4486
-
-for i in 1 2 3; do
- OVN_CHECK_PACKETS([hv$i/vif${i}1-tx.pcap], [vif${i}1.expected])
-done
-
-OVN_CLEANUP([hv1], [hv2], [hv3])
-AT_CLEANUP
-
-AT_SETUP([ovn -- Port Groups])
-AT_KEYWORDS([ovnpg])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-#
-# Three logical switches ls1, ls2, ls3.
-# One logical router lr0 connected to ls[123],
-# with nine subnets, three per logical switch:
-#
-# lrp11 on ls1 for subnet 192.168.11.0/24
-# lrp12 on ls1 for subnet 192.168.12.0/24
-# lrp13 on ls1 for subnet 192.168.13.0/24
-# ...
-# lrp33 on ls3 for subnet 192.168.33.0/24
-#
-# 27 VIFs, 9 per LS, 3 per subnet: lp[123][123][123], where the first two
-# digits are the subnet and the last digit distinguishes the VIF.
-#
-# This test will create two port groups and uses them in ACL.
-
-get_lsp_uuid () {
- ovn-nbctl lsp-list ls${1%??} | grep lp$1 | awk '{ print $1 }'
-}
-
-pg1_ports=
-pg2_ports=
-for i in 1 2 3; do
- ovn-nbctl ls-add ls$i
- for j in 1 2 3; do
- for k in 1 2 3; do
- ovn-nbctl \
- -- lsp-add ls$i lp$i$j$k \
- -- lsp-set-addresses lp$i$j$k \
- "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k"
- # logical ports lp[12]?1 belongs to port group pg1
- if test $i != 3 && test $k == 1; then
- pg1_ports="$pg1_ports `get_lsp_uuid $i$j$k`"
- fi
- # logical ports lp[23]?2 belongs to port group pg2
- if test $i != 1 && test $k == 2; then
- pg2_ports="$pg2_ports `get_lsp_uuid $i$j$k`"
- fi
- done
- done
-done
-
-ovn-nbctl lr-add lr0
-for i in 1 2 3; do
- for j in 1 2 3; do
- ovn-nbctl lrp-add lr0 lrp$i$j 00:00:00:00:ff:$i$j 192.168.$i$j.254/24
- ovn-nbctl \
- -- lsp-add ls$i lrp$i$j-attachment \
- -- set Logical_Switch_Port lrp$i$j-attachment type=router \
- options:router-port=lrp$i$j \
- addresses='"00:00:00:00:ff:'$i$j'"'
- done
-done
-
-ovn-nbctl create Port_Group name=pg1 ports="$pg1_ports"
-ovn-nbctl create Port_Group name=pg2 ports="$pg2_ports"
-
-# create ACLs on all lswitches to drop traffic from pg2 to pg1
-ovn-nbctl acl-add ls1 to-lport 1001 'outport == @pg1 && ip4.src == $pg2_ip4' drop
-ovn-nbctl acl-add ls2 to-lport 1001 'outport == @pg1 && ip4.src == $pg2_ip4' drop
-ovn-nbctl acl-add ls3 to-lport 1001 'outport == @pg1 && ip4.src == $pg2_ip4' drop
-
-# Physical network:
-#
-# Three hypervisors hv[123].
-# lp?1[123] spread across hv[123]: lp?11 on hv1, lp?12 on hv2, lp?13 on hv3.
-# lp?2[123] spread across hv[23]: lp?21 and lp?22 on hv2, lp?23 on hv3.
-# lp?3[123] all on hv3.
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- case $1 in dnl (
- ?11) echo 1 ;; dnl (
- ?12 | ?21 | ?22) echo 2 ;; dnl (
- ?13 | ?23 | ?3?) echo 3 ;;
- esac
-}
-
-# Given the name of a logical port, prints the name of its logical router
-# port, e.g. "vif_to_lrp 123" yields 12.
-vif_to_lrp() {
- echo ${1%?}
-}
-
-# Given the name of a logical port, prints the name of its logical
-# switch, e.g. "vif_to_ls 123" yields 1.
-vif_to_ls() {
- echo ${1%??}
-}
-
-net_add n1
-for i in 1 2 3; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-done
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- hv=`vif_to_hv $i$j$k`
- as hv$hv ovs-vsctl \
- -- add-port br-int vif$i$j$k \
- -- set Interface vif$i$j$k \
- external-ids:iface-id=lp$i$j$k \
- options:tx_pcap=hv$hv/vif$i$j$k-tx.pcap \
- options:rxq_pcap=hv$hv/vif$i$j$k-rx.pcap \
- ofport-request=$i$j$k
- done
- done
-done
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-#
-# This shell function causes a packet to be received on INPORT. The packet's
-# content has Ethernet destination DST and source SRC (each exactly 12 hex
-# digits) and Ethernet type ETHTYPE (4 hex digits). The OUTPORTs (zero or
-# more) list the VIFs on which the packet should be received. INPORT and the
-# OUTPORTs are specified as logical switch port numbers, e.g. 123 for vif123.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- : > $i$j$k.expected
- done
- done
-done
-test_ip() {
- # This packet has bad checksums but logical L3 routing doesn't check.
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
- shift; shift; shift; shift; shift
- hv=hv`vif_to_hv $inport`
- as $hv ovs-appctl netdev-dummy/receive vif$inport $packet
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
- in_ls=`vif_to_ls $inport`
- in_lrp=`vif_to_lrp $inport`
- for outport; do
- out_ls=`vif_to_ls $outport`
- if test $in_ls = $out_ls; then
- # Ports on the same logical switch receive exactly the same packet.
- echo $packet
- else
- # Routing decrements TTL and updates source and dest MAC
- # (and checksum).
- out_lrp=`vif_to_lrp $outport`
- echo f00000000${outport}00000000ff${out_lrp}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
- fi >> $outport.expected
- done
-}
-
-as hv1 ovs-vsctl --columns=name,ofport list interface
-as hv1 ovn-sbctl list port_binding
-as hv1 ovn-sbctl list datapath_binding
-as hv1 ovn-sbctl list port_group
-as hv1 ovn-sbctl list address_set
-as hv1 ovn-sbctl dump-flows
-as hv1 ovs-ofctl dump-flows br-int
-
-# Send IP packets between all pairs of source and destination ports,
-# packets matches ACL (pg2 to pg1) should be dropped
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-for is in 1 2 3; do
- for js in 1 2 3; do
- for ks in 1 2 3; do
- bcast=
- s=$is$js$ks
- smac=f00000000$s
- sip=`ip_to_hex 192 168 $is$js $ks`
- for id in 1 2 3; do
- for jd in 1 2 3; do
- for kd in 1 2 3; do
- d=$id$jd$kd
- dip=`ip_to_hex 192 168 $id$jd $kd`
- if test $is = $id; then dmac=f00000000$d; else dmac=00000000ff$is$js; fi
- if test $d != $s; then unicast=$d; else unicast=; fi
-
- # packets matches ACL should be dropped
- if test $id != 3 && test $kd == 1; then
- if test $is != 1 && test $ks == 2; then
- unicast=
- fi
- fi
- test_ip $s $smac $dmac $sip $dip $unicast #1
- done
- done
- done
- done
- done
-done
-
-# Allow some time for packet forwarding.
-# XXX This can be improved.
-sleep 1
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- OVN_CHECK_PACKETS([hv`vif_to_hv $i$j$k`/vif$i$j$k-tx.pcap],
- [$i$j$k.expected])
- done
- done
-done
-
-# Gracefully terminate daemons
-OVN_CLEANUP([hv1], [hv2], [hv3])
-AT_CLEANUP
-
-AT_SETUP([ovn -- ACLs on Port Groups])
-AT_KEYWORDS([ovnpg_acl])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-#
-# Three logical switches ls1, ls2, ls3.
-# One logical router lr0 connected to ls[123],
-# with nine subnets, three per logical switch:
-#
-# lrp11 on ls1 for subnet 192.168.11.0/24
-# lrp12 on ls1 for subnet 192.168.12.0/24
-# lrp13 on ls1 for subnet 192.168.13.0/24
-# ...
-# lrp33 on ls3 for subnet 192.168.33.0/24
-#
-# 27 VIFs, 9 per LS, 3 per subnet: lp[123][123][123], where the first two
-# digits are the subnet and the last digit distinguishes the VIF.
-#
-# This test will create two port groups and ACLs will be applied on them.
-
-get_lsp_uuid () {
- ovn-nbctl lsp-list ls${1%??} | grep lp$1 | awk '{ print $1 }'
-}
-
-pg1_ports=
-pg2_ports=
-for i in 1 2 3; do
- ovn-nbctl ls-add ls$i
- for j in 1 2 3; do
- for k in 1 2 3; do
- ovn-nbctl \
- -- lsp-add ls$i lp$i$j$k \
- -- lsp-set-addresses lp$i$j$k \
- "f0:00:00:00:0$i:$j$k 192.168.$i$j.$k"
- # logical ports lp[12]?1 belongs to port group pg1
- if test $i != 3 && test $k == 1; then
- pg1_ports="$pg1_ports `get_lsp_uuid $i$j$k`"
- fi
- # logical ports lp[23]?2 belongs to port group pg2
- if test $i != 1 && test $k == 2; then
- pg2_ports="$pg2_ports `get_lsp_uuid $i$j$k`"
- fi
- done
- done
-done
-
-ovn-nbctl lr-add lr0
-for i in 1 2 3; do
- for j in 1 2 3; do
- ovn-nbctl lrp-add lr0 lrp$i$j 00:00:00:00:ff:$i$j 192.168.$i$j.254/24
- ovn-nbctl \
- -- lsp-add ls$i lrp$i$j-attachment \
- -- set Logical_Switch_Port lrp$i$j-attachment type=router \
- options:router-port=lrp$i$j \
- addresses='"00:00:00:00:ff:'$i$j'"'
- done
-done
-
-ovn-nbctl create Port_Group name=pg1 ports="$pg1_ports"
-ovn-nbctl create Port_Group name=pg2 ports="$pg2_ports"
-
-# create ACLs on pg1 to drop traffic from pg2 to pg1
-ovn-nbctl acl-add pg1 to-lport 1001 'outport == @pg1' drop
-ovn-nbctl --type=port-group acl-add pg1 to-lport 1002 \
- 'outport == @pg1 && ip4.src == $pg2_ip4' allow-related
-
-# Physical network:
-#
-# Three hypervisors hv[123].
-# lp?1[123] spread across hv[123]: lp?11 on hv1, lp?12 on hv2, lp?13 on hv3.
-# lp?2[123] spread across hv[23]: lp?21 and lp?22 on hv2, lp?23 on hv3.
-# lp?3[123] all on hv3.
-
-# Given the name of a logical port, prints the name of the hypervisor
-# on which it is located.
-vif_to_hv() {
- case $1 in dnl (
- ?11) echo 1 ;; dnl (
- ?12 | ?21 | ?22) echo 2 ;; dnl (
- ?13 | ?23 | ?3?) echo 3 ;;
- esac
-}
-
-# Given the name of a logical port, prints the name of its logical router
-# port, e.g. "vif_to_lrp 123" yields 12.
-vif_to_lrp() {
- echo ${1%?}
-}
-
-# Given the name of a logical port, prints the name of its logical
-# switch, e.g. "vif_to_ls 123" yields 1.
-vif_to_ls() {
- echo ${1%??}
-}
-
-net_add n1
-for i in 1 2 3; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-done
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- hv=`vif_to_hv $i$j$k`
- as hv$hv ovs-vsctl \
- -- add-port br-int vif$i$j$k \
- -- set Interface vif$i$j$k \
- external-ids:iface-id=lp$i$j$k \
- options:tx_pcap=hv$hv/vif$i$j$k-tx.pcap \
- options:rxq_pcap=hv$hv/vif$i$j$k-rx.pcap \
- ofport-request=$i$j$k
- done
- done
-done
-
-# Pre-populate the hypervisors' ARP tables so that we don't lose any
-# packets for ARP resolution (native tunneling doesn't queue packets
-# for ARP resolution).
-OVN_POPULATE_ARP
-
-# Allow some time for ovn-northd and ovn-controller to catch up.
-# XXX This should be more systematic.
-sleep 1
-
-lsp_to_mac() {
- echo f0:00:00:00:0${1:0:1}:${1:1:2}
-}
-
-lrp_to_mac() {
- echo 00:00:00:00:ff:$1
-}
-
-# test_icmp INPORT SRC_MAC DST_MAC SRC_IP DST_IP ICMP_TYPE OUTPORT...
-#
-# This shell function causes a ICMP packet to be received on INPORT.
-# The OUTPORTs (zero or more) list the VIFs on which the packet should
-# be received. INPORT and the OUTPORTs are specified as logical switch
-# port numbers, e.g. 123 for vif123.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- : > $i$j$k.expected
- done
- done
-done
-
-test_icmp() {
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 icmp_type=$6
- local packet="inport==\"lp$inport\" && eth.src==$src_mac &&
- eth.dst==$dst_mac && ip.ttl==64 && ip4.src==$src_ip
- && ip4.dst==$dst_ip && icmp4.type==$icmp_type &&
- icmp4.code==0"
- shift; shift; shift; shift; shift; shift
- hv=hv`vif_to_hv $inport`
- as $hv ovs-appctl -t ovn-controller inject-pkt "$packet"
- in_ls=`vif_to_ls $inport`
- in_lrp=`vif_to_lrp $inport`
- for outport; do
- out_ls=`vif_to_ls $outport`
- if test $in_ls = $out_ls; then
- # Ports on the same logical switch receive exactly the same packet.
- echo $packet | ovstest test-ovn expr-to-packets
- else
- # Routing decrements TTL and updates source and dest MAC
- # (and checksum).
- out_lrp=`vif_to_lrp $outport`
- exp_smac=`lrp_to_mac $out_lrp`
- exp_dmac=`lsp_to_mac $outport`
- exp_packet="eth.src==$exp_smac && eth.dst==$exp_dmac &&
- ip.ttl==63 && ip4.src==$src_ip && ip4.dst==$dst_ip &&
- icmp4.type==$icmp_type && icmp4.code==0"
- echo $exp_packet | ovstest test-ovn expr-to-packets
-
- fi >> $outport.expected
- done
-}
-
-as hv1 ovs-vsctl --columns=name,ofport list interface
-as hv1 ovn-sbctl list port_binding
-as hv1 ovn-sbctl list datapath_binding
-as hv1 ovn-sbctl list port_group
-as hv1 ovn-sbctl list address_set
-as hv1 ovn-sbctl dump-flows
-as hv1 ovs-ofctl dump-flows br-int
-
-# Send IP packets between all pairs of source and destination ports,
-# packets matches ACL1 but not ACL2 should be dropped
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-for is in 1 2 3; do
- for js in 1 2 3; do
- for ks in 1 2 3; do
- bcast=
- s=$is$js$ks
- slsp_mac=`lsp_to_mac $s`
- slrp_mac=`lrp_to_mac $is$js`
- sip=192.168.$is$js.$ks
- for id in 1 2 3; do
- for jd in 1 2 3; do
- for kd in 1 2 3; do
- d=$id$jd$kd
- dlsp_mac=`lsp_to_mac $d`
- dlrp_mac=`lrp_to_mac $id$jd`
- dip=192.168.$id$jd.$kd
- if test $is = $id; then dmac=$dlsp_mac; else dmac=$slrp_mac; fi
- if test $d != $s; then unicast=$d; else unicast=; fi
-
- # packets matches ACL1 but not ACL2 should be dropped
- if test $id != 3 && test $kd == 1; then
- if test $is == 1 || test $ks != 2; then
- unicast=
- fi
- fi
- # icmp request (type = 8)
- test_icmp $s $slsp_mac $dmac $sip $dip 8 $unicast
-
- # if packets are not dropped, test the return traffic (icmp echo)
- # to make sure stateful works, too.
- if test x$unicast != x; then
- if test $is = $id; then dmac=$slsp_mac; else dmac=$dlrp_mac; fi
- # icmp echo (type = 0)
- test_icmp $unicast $dlsp_mac $dmac $dip $sip 0 $s
- fi
- done
- done
- done
- done
- done
-done
-
-# Allow some time for packet forwarding.
-# XXX This can be improved.
-sleep 1
-
-# Now check the packets actually received against the ones expected.
-for i in 1 2 3; do
- for j in 1 2 3; do
- for k in 1 2 3; do
- OVN_CHECK_PACKETS([hv`vif_to_hv $i$j$k`/vif$i$j$k-tx.pcap],
- [$i$j$k.expected])
- done
- done
-done
-
-# Gracefully terminate daemons
-OVN_CLEANUP([hv1], [hv2], [hv3])
-AT_CLEANUP
-
-AT_SETUP([ovn -- Address Set generation from Port Groups (static addressing)])
-ovn_start
-
-ovn-nbctl ls-add ls1
-
-ovn-nbctl lsp-add ls1 lp1
-ovn-nbctl lsp-add ls1 lp2
-ovn-nbctl lsp-add ls1 lp3
-
-ovn-nbctl lsp-set-addresses lp1 "02:00:00:00:00:01 10.0.0.1 2001:db8::1"
-ovn-nbctl lsp-set-addresses lp2 "02:00:00:00:00:02 10.0.0.2 2001:db8::2"
-ovn-nbctl lsp-set-addresses lp3 "02:00:00:00:00:03 10.0.0.3 2001:db8::3"
-
-ovn-nbctl create Port_Group name=pg1
-ovn-nbctl create Port_Group name=pg2
-
-ovn-nbctl --id=@p get Logical_Switch_Port lp1 -- add Port_Group pg1 ports @p
-ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg1 ports @p
-ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg2 ports @p
-ovn-nbctl --id=@p get Logical_Switch_Port lp3 -- add Port_Group pg2 ports @p
-
-ovn-nbctl --wait=sb sync
-
-dnl Check if port group address sets were populated with ports' addresses
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
- [0], [[["10.0.0.1", "10.0.0.2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg2_ip4 addresses],
- [0], [[["10.0.0.2", "10.0.0.3"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
- [0], [[["2001:db8::1", "2001:db8::2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg2_ip6 addresses],
- [0], [[["2001:db8::2", "2001:db8::3"]]
-])
-
-ovn-nbctl --wait=sb lsp-set-addresses lp1 \
- "02:00:00:00:00:01 10.0.0.11 2001:db8::11"
-
-dnl Check if updated address got propagated to the port group address sets
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
- [0], [[["10.0.0.11", "10.0.0.2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
- [0], [[["2001:db8::11", "2001:db8::2"]]
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- Address Set generation from Port Groups (dynamic addressing)])
-ovn_start
-
-ovn-nbctl ls-add ls1
-ovn-nbctl ls-add ls2
-ovn-nbctl ls-add ls3
-
-ovn-nbctl set Logical_Switch ls1 \
- other_config:subnet=10.1.0.0/24 other_config:ipv6_prefix="2001:db8:1::"
-ovn-nbctl set Logical_Switch ls2 \
- other_config:subnet=10.2.0.0/24 other_config:ipv6_prefix="2001:db8:2::"
-ovn-nbctl set Logical_Switch ls3 \
- other_config:subnet=10.3.0.0/24 other_config:ipv6_prefix="2001:db8:3::"
-
-ovn-nbctl lsp-add ls1 lp1
-ovn-nbctl lsp-add ls2 lp2
-ovn-nbctl lsp-add ls3 lp3
-
-ovn-nbctl lsp-set-addresses lp1 "02:00:00:00:00:01 dynamic"
-ovn-nbctl lsp-set-addresses lp2 "02:00:00:00:00:02 dynamic"
-ovn-nbctl lsp-set-addresses lp3 "02:00:00:00:00:03 dynamic"
-
-ovn-nbctl create Port_Group name=pg1
-ovn-nbctl create Port_Group name=pg2
-
-ovn-nbctl --id=@p get Logical_Switch_Port lp1 -- add Port_Group pg1 ports @p
-ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg1 ports @p
-ovn-nbctl --id=@p get Logical_Switch_Port lp2 -- add Port_Group pg2 ports @p
-ovn-nbctl --id=@p get Logical_Switch_Port lp3 -- add Port_Group pg2 ports @p
-
-ovn-nbctl --wait=sb sync
-
-dnl Check if port group address sets were populated with ports' addresses
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
- [0], [[["10.1.0.2", "10.2.0.2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg2_ip4 addresses],
- [0], [[["10.2.0.2", "10.3.0.2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
- [0], [[["2001:db8:1::ff:fe00:1", "2001:db8:2::ff:fe00:2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg2_ip6 addresses],
- [0], [[["2001:db8:2::ff:fe00:2", "2001:db8:3::ff:fe00:3"]]
-])
-
-ovn-nbctl --wait=sb set Logical_Switch ls1 \
- other_config:subnet=10.11.0.0/24 other_config:ipv6_prefix="2001:db8:11::"
-
-dnl Check if updated address got propagated to the port group address sets
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
- [0], [[["10.11.0.2", "10.2.0.2"]]
-])
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
- [0], [[["2001:db8:11::ff:fe00:1", "2001:db8:2::ff:fe00:2"]]
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- ACL conjunction])
-ovn_start
-
-ovn-nbctl ls-add ls1
-
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4"
-
-ovn-nbctl lsp-set-port-security ls1-lp1 "f0:00:00:00:00:01 10.0.0.4"
-
-ovn-nbctl lsp-add ls1 ls1-lp2 \
--- lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.6"
-
-ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.6"
-
-net_add n1
-sim_add hv1
-
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=ls1-lp1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=ls1-lp2 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=2
-
-ovn-nbctl create Address_Set name=set1 \
-addresses=\"10.0.0.4\",\"10.0.0.5\",\"10.0.0.6\"
-ovn-nbctl create Address_Set name=set2 \
-addresses=\"10.0.0.7\",\"10.0.0.8\",\"10.0.0.9\"
-ovn-nbctl acl-add ls1 to-lport 1002 \
-'ip4 && ip4.src == $set1 && ip4.dst == $set1' allow
-ovn-nbctl acl-add ls1 to-lport 1001 \
-'ip4 && ip4.src == $set1 && ip4.dst == $set2' drop
-
-# test_ip INPORT SRC_MAC DST_MAC SRC_IP DST_IP OUTPORT...
-#
-# This shell function causes an ip packet to be received on INPORT.
-# The packet's content has Ethernet destination DST and source SRC
-# (each exactly 12 hex digits) and Ethernet type ETHTYPE (4 hex digits).
-# The OUTPORTs (zero or more) list the VIFs on which the packet should
-# be received. INPORT and the OUTPORTs are specified as logical switch
-# port numbers, e.g. 11 for vif11.
-test_ip() {
- # This packet has bad checksums but logical L3 routing doesn't check.
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}\
-${dst_ip}0035111100080000
- shift; shift; shift; shift; shift
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
- for outport; do
- echo $packet >> $outport.expected
- done
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-
-sip=`ip_to_hex 10 0 0 4`
-dip=`ip_to_hex 10 0 0 6`
-
-test_ip 1 f00000000001 f00000000002 $sip $dip 2
-
-cat 2.expected > expout
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-AT_CHECK([cat 2.packets], [0], [expout])
-
-# There should be total of 12 flows present with conjunction action and 2 flows
-# with conj match. Eg.
-# table=44, priority=2002,conj_id=2,metadata=0x1 actions=resubmit(,45)
-# table=44, priority=2001,conj_id=3,metadata=0x1 actions=drop
-# priority=2002,ip,metadata=0x1,nw_dst=10.0.0.6 actions=conjunction(2,2/2)
-# priority=2002,ip,metadata=0x1,nw_dst=10.0.0.4 actions=conjunction(2,2/2)
-# priority=2002,ip,metadata=0x1,nw_dst=10.0.0.5 actions=conjunction(2,2/2)
-# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 actions=conjunction(3,2/2)
-# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(3,2/2)
-# priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(3,2/2)
-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(2,1/2)
-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(2,1/2)
-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(2,1/2)
-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(3,1/2)
-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2)
-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2)
-
-OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \
-grep conjunction | wc -l`])
-OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \
-grep conj_id | wc -l`])
-
-as hv1 ovs-ofctl dump-flows br-int
-
-# Set the ip address for ls1-lp2 from set2 so that the drop ACL flow is hit.
-ovn-nbctl lsp-set-addresses ls1-lp2 "f0:00:00:00:00:02 10.0.0.7 20.0.0.4"
-ovn-nbctl lsp-set-port-security ls1-lp2 "f0:00:00:00:00:02 10.0.0.7 20.0.0.4"
-
-reset_pcap_file hv1-vif2 hv1/vif2
-
-rm -f 2.packets
-
-sip=`ip_to_hex 10 0 0 4`
-dip=`ip_to_hex 10 0 0 7`
-
-test_ip 1 f00000000001 f00000000002 $sip $dip
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
-AT_CHECK([cat 2.packets], [0], [])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- TTL exceeded])
-AT_KEYWORDS([ttl-exceeded])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# test_ip_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_DST IPV4_ROUTER IP_CHKSUM EXP_IP_CHKSUM EXP_ICMP_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv4 packet with
-# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHKSUM as specified and TTL set to 1.
-# EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are the ip and icmp checksums of the icmp time exceeded frame
-# generated by OVN logical router
-#
-# INPORT is a lport number, e.g. 11 for vif11.
-# HV is a hypervisor number
-# ETH_SRC and ETH_DST are each 12 hex digits.
-# IPV4_SRC, IPV4_DST and IPV4_ROUTER are each 8 hex digits.
-# IP_CHKSUM, EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits
-test_ip_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_router=$7 ip_chksum=$8
- local exp_ip_chksum=$9 exp_icmp_chksum=${10}
- shift 10
-
- local ip_ttl=01
- local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}
-
- local reply_icmp_ttl=fe
- local icmp_type_code_response=0b00
- local icmp_data=00000000
- local reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_data}
- local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_ROUTER EXP_ICMP_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv6
-# packet with ETH_SRC, ETH_DST, IPV6_SRC and IPV6_DST as specified.
-# IPV6_ROUTER and EXP_ICMP_CHKSUM are the source IP and checksum of the icmpv6 ttl exceeded
-# packet sent by OVN logical router
-test_ip6_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 ipv6_router=$7 exp_icmp_chksum=$8
- shift 8
-
- local ip6_hdr=6000000000151101${ipv6_src}${ipv6_dst}
- local packet=${eth_dst}${eth_src}86dd${ip6_hdr}dbb8303900155bac6b646f65206676676e6d66720a
-
- local reply=${eth_src}${eth_dst}86dd6000000000303afe${ipv6_router}${ipv6_src}0300${exp_icmp_chksum}00000000${ip6_hdr}
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-for i in 1 2; do
- net_add n$i
- ovn-nbctl ls-add sw$i
-
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n$i br-phys 192.168.$i.1
-
- ovn-nbctl lsp-add sw$i sw$i-p${i}0 -- \
- lsp-set-addresses sw$i-p${i}0 "00:00:00:00:00:0$i 192.168.$i.1 2001:db8:$i::11"
-
- ovs-vsctl -- add-port br-int vif$i -- \
- set interface vif$i \
- external-ids:iface-id=sw$i-p${i}0 \
- options:tx_pcap=hv$i/vif$i-tx.pcap \
- options:rxq_pcap=hv$i/vif$i-rx.pcap \
- ofport-request=$i
-done
-
-ovn-nbctl lr-add lr0
-for i in 1 2; do
- ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.254/24 2001:db8:$i::1/64
- ovn-nbctl -- lsp-add sw$i lrp$i-attachment \
- -- set Logical_Switch_Port lrp$i-attachment type=router \
- options:router-port=lrp$i addresses='"00:00:00:00:ff:0'$i' 192.168.'$i'.254 2001:db8:'$i'::1"'
-done
-
-OVN_POPULATE_ARP
-# allow some time for ovn-northd and ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-
-test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 1 254) 0000 7dae f4ff
-test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000200000000000000000011 20010db8000100000000000000000001 d461
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
-
-OVN_CLEANUP([hv1], [hv2])
-AT_CLEANUP
-
-AT_SETUP([ovn -- router port unreachable])
-AT_KEYWORDS([router-port-unreachable])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# test_ip_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_ROUTER L4_PROTCOL IP_CHKSUM EXP_IP_CHKSUM EXP_ICMP_CHKSUM EXP_ICMP_CODE
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv4 packet with
-# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_ROUTER, L4_PROTCOL, IP_CHKSUM as specified.
-# EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are the ip and icmp checksums of the icmp frame generated by OVN logical router
-# EXP_ICMP_CODE are code and type of the icmp frame generated by OVN logical router
-#
-# INPORT is a lport number, e.g. 11 for vif11.
-# HV is a hypervisor number
-# ETH_SRC and ETH_DST are each 12 hex digits.
-# IPV4_SRC and IPV4_ROUTER are each 8 hex digits.
-# IP_CHKSUM, EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits
-test_ip_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ip_router=$6 l4_proto=$7 ip_chksum=$8
- local exp_ip_chksum=$9 exp_icmp_chksum=${10} exp_icmp_code=${11}
- shift 11
-
- local ip_ttl=ff
- local packet=${eth_dst}${eth_src}08004500001400004000${ip_ttl}${l4_proto}${ip_chksum}${ipv4_src}${ip_router}
-
- local reply_icmp_ttl=fe
- local icmp_data=00000000
- local reply_icmp_payload=${exp_icmp_code}${exp_icmp_chksum}${icmp_data}
- local reply=${eth_src}${eth_dst}08004500001c00004000${reply_icmp_ttl}01${exp_ip_chksum}${ip_router}${ipv4_src}${reply_icmp_payload}
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# test_tcp_syn_packet INPORT HV ETH_SRC ETH_DST IPV4_SRC IPV4_ROUTER IP_CHKSUM TCP_SPORT TCP_DPORT TCP_CHKSUM EXP_IP_CHKSUM EXP_TCP_RST_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an TCP syn segment with
-# ETH_SRC, ETH_DST, IPV4_SRC, IPV4_ROUTER, IP_CHKSUM, TCP_SPORT, TCP_DPORT, TCP_CHKSUM as specified.
-# EXP_IP_CHKSUM and EXP_TCP_RST_CHKSUM are the ip and tcp checksums of the tcp reset segment generated by OVN logical router
-#
-# INPORT is an lport number, e.g. 11 for vif11.
-# HV is an hypervisor number
-# ETH_SRC and ETH_DST are each 12 hex digits.
-# IPV4_SRC and IPV4_ROUTER are each 8 hex digits.
-# TCP_SPORT and TCP_DPORT are 4 hex digits.
-# IP_CHKSUM, TCP_CHKSUM, EXP_IP_CHSUM and EXP_TCP_RST_CHKSUM are each 4 hex digits
-test_tcp_syn_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ip_router=$6 ip_chksum=$7
- local tcp_sport=$8 tcp_dport=$9 tcp_chksum=${10}
- local exp_ip_chksum=${11} exp_tcp_rst_chksum=${12}
- shift 12
-
- local ip_ttl=ff
- local packet=${eth_dst}${eth_src}08004500002800004000${ip_ttl}06${ip_chksum}${ipv4_src}${ip_router}${tcp_sport}${tcp_dport}000000010000000050027210${tcp_chksum}0000
-
- local tcp_rst_ttl=fe
- local reply=${eth_src}${eth_dst}08004500002800004000${tcp_rst_ttl}06${exp_ip_chksum}${ip_router}${ipv4_src}${tcp_dport}${tcp_sport}000000000000000150040000${exp_tcp_rst_chksum}0000
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# test_tcp6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_ROUTER TCP_SPORT TCP_DPORT TCP_CHKSUM EXP_TCP_RST_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is a TCP syn segment with
-# ETH_SRC, ETH_DST, IPV6_SRC, IPV6_ROUTER, TCP_SPORT, TCP_DPORT and TCP_CHKSUM as specified.
-# EXP_TCP_RST_CHKSUM is the tcp checksums of the tcp reset segment generated by OVN logical router
-test_tcp6_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_router=$6
- local tcp_sport=$7 tcp_dport=$8 tcp_chksum=$9
- local exp_tcp_rst_chksum=${10}
- shift 10
-
- local ip6_hdr=60000000001406ff${ipv6_src}${ipv6_router}
- local packet=${eth_dst}${eth_src}86dd${ip6_hdr}${tcp_sport}${tcp_dport}000000010000000050027210${tcp_chksum}0000
-
- local reply=${eth_src}${eth_dst}86dd60000000001406fe${ipv6_router}${ipv6_src}${tcp_dport}${tcp_sport}000000000000000150040000${exp_tcp_rst_chksum}0000
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-# test_ip6_packet INPORT HV ETH_SRC ETH_DST IPV6_SRC IPV6_DST IPV6_PROTO IPV6_LEN DATA EXP_ICMP_CODE EXP_ICMP_CHKSUM
-#
-# Causes a packet to be received on INPORT of the hypervisor HV. The packet is an IPv6
-# packet with ETH_SRC, ETH_DST, IPV6_SRC, IPV6_DST, IPV6_PROTO, IPV6_LEN and DATA as specified.
-# EXP_ICMP_CODE and EXP_ICMP_CHKSUM are the code and checksum of the icmp6 packet sent by OVN logical router
-test_ip6_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 ipv6_proto=$7 ipv6_len=$8 data=$9
- local exp_icmp_code=${10} exp_icmp_chksum=${11}
- shift 11
-
- local ip6_hdr=60000000${ipv6_len}${ipv6_proto}ff${ipv6_src}${ipv6_dst}
- local packet=${eth_dst}${eth_src}86dd${ip6_hdr}${data}
-
- local reply=${eth_src}${eth_dst}86dd6000000000303afe${ipv6_dst}${ipv6_src}${exp_icmp_code}${exp_icmp_chksum}00000000${ip6_hdr}
- echo $reply >> vif$inport.expected
-
- as hv$hv ovs-appctl netdev-dummy/receive vif$inport $packet
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-for i in 1 2; do
- net_add n$i
- ovn-nbctl ls-add sw$i
-
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n$i br-phys 192.168.$i.1
-
- ovn-nbctl lsp-add sw$i sw$i-p${i}0 -- \
- lsp-set-addresses sw$i-p${i}0 "00:00:00:00:00:0$i 192.168.$i.1 2001:db8:$i::11"
-
- ovs-vsctl -- add-port br-int vif$i -- \
- set interface vif$i \
- external-ids:iface-id=sw$i-p${i}0 \
- options:tx_pcap=hv$i/vif$i-tx.pcap \
- options:rxq_pcap=hv$i/vif$i-rx.pcap \
- ofport-request=$i
-done
-
-ovn-nbctl lr-add lr0
-for i in 1 2; do
- ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$i 192.168.$i.254/24 2001:db8:$i::1/64
- ovn-nbctl -- lsp-add sw$i lrp$i-attachment \
- -- set Logical_Switch_Port lrp$i-attachment type=router \
- options:router-port=lrp$i addresses='"00:00:00:00:ff:0'$i' 192.168.'$i'.254 2001:db8:'$i'::1"'
-done
-
-OVN_POPULATE_ARP
-# allow some time for ovn-northd and ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-
-test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 11 0000 7dae fcfc 0303
-test_ip_packet 1 1 000000000001 00000000ff01 $(ip_to_hex 192 168 1 1) $(ip_to_hex 192 168 1 254) 84 0000 7dae fcfd 0302
-test_ip6_packet 1 1 000000000001 00000000ff01 20010db8000100000000000000000011 20010db8000100000000000000000001 11 0015 dbb8303900155bac6b646f65206676676e6d66720a 0104 d570
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
-
-test_tcp_syn_packet 2 2 000000000002 00000000ff02 $(ip_to_hex 192 168 2 1) $(ip_to_hex 192 168 2 254) 0000 8b40 3039 0000 7bae 4486
-test_ip6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 84 0004 01020304 0103 627e
-test_tcp6_packet 2 2 000000000002 00000000ff02 20010db8000200000000000000000011 20010db8000200000000000000000001 8b40 3039 0000 4486
-OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [vif2.expected])
-
-OVN_CLEANUP([hv1], [hv2])
-AT_CLEANUP
-
-AT_SETUP([ovn -- ovn-controller exit])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-# Logical network:
-# One Logical Router: ro, with two logical switches sw1 and sw2.
-# sw1 is for subnet 10.0.0.0/8
-# sw2 is for subnet 20.0.0.0/8
-# sw1 has a single port bound on hv1
-# sw2 has a single port bound on hv2
-
-ovn-nbctl lr-add ro
-ovn-nbctl ls-add sw1
-ovn-nbctl ls-add sw2
-
-sw1_ro_mac=00:00:10:00:00:01
-sw1_ro_ip=10.0.0.1
-sw2_ro_mac=00:00:20:00:00:01
-sw2_ro_ip=20.0.0.1
-sw1_p1_mac=00:00:10:00:00:02
-sw1_p1_ip=10.0.0.2
-sw2_p1_mac=00:00:20:00:00:02
-sw2_p1_ip=20.0.0.2
-
-ovn-nbctl lrp-add ro ro-sw1 $sw1_ro_mac ${sw1_ro_ip}/8
-ovn-nbctl lrp-add ro ro-sw2 $sw2_ro_mac ${sw2_ro_ip}/8
-ovn-nbctl lsp-add sw1 sw1-ro -- set Logical_Switch_Port sw1-ro type=router \
- options:router-port=ro-sw1 addresses=\"$sw1_ro_mac\"
-ovn-nbctl lsp-add sw2 sw2-ro -- set Logical_Switch_Port sw2-ro type=router \
- options:router-port=ro-sw2 addresses=\"$sw2_ro_mac\"
-
-ovn-nbctl lsp-add sw1 sw1-p1 \
--- lsp-set-addresses sw1-p1 "$sw1_p1_mac $sw1_p1_ip"
-
-ovn-nbctl lsp-add sw2 sw2-p1 \
--- lsp-set-addresses sw2-p1 "$sw2_p1_mac $sw2_p1_ip"
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=sw1-p1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=sw2-p1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-OVN_POPULATE_ARP
-
-sleep 1
-
-packet="inport==\"sw1-p1\" && eth.src==$sw1_p1_mac && eth.dst==$sw1_ro_mac &&
- ip4 && ip.ttl==64 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-
-# Start by Sending the packet and make sure it makes it there as expected
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Expected packet has TTL decreased by 1
-expected="eth.src==$sw2_ro_mac && eth.dst==$sw2_p1_mac &&
- ip4 && ip.ttl==63 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $expected | ovstest test-ovn expr-to-packets > expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Stop ovn-controller on hv2
-as hv2 ovs-appctl -t ovn-controller exit
-
-# Now send the packet again. This time, it should not arrive.
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Start ovn-controller again just so OVN_CLEANUP doesn't complain
-as hv2 start_daemon ovn-controller
-
-OVN_CLEANUP([hv1],[hv2])
-AT_CLEANUP
-
-AT_SETUP([ovn -- external logical port])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-net_add n1
-sim_add hv1
-sim_add hv2
-sim_add hv3
-
-ovn-nbctl ls-add ls1
-ovn-nbctl lsp-add ls1 ls1-lp1 \
--- lsp-set-addresses ls1-lp1 "f0:00:00:00:00:01 10.0.0.4 ae70::4"
-
-# Add a couple of external logical port
-ovn-nbctl lsp-add ls1 ls1-lp_ext1 \
--- lsp-set-addresses ls1-lp_ext1 "f0:00:00:00:00:03 10.0.0.6 ae70::6"
-ovn-nbctl lsp-set-port-security ls1-lp_ext1 \
-"f0:00:00:00:00:03 10.0.0.6 ae70::6"
-ovn-nbctl lsp-set-type ls1-lp_ext1 external
-
-ovn-nbctl lsp-add ls1 ls1-lp_ext2 \
--- lsp-set-addresses ls1-lp_ext2 "f0:00:00:00:00:04 10.0.0.7 ae70::7"
-ovn-nbctl lsp-set-port-security ls1-lp_ext2 \
-"f0:00:00:00:00:04 10.0.0.7 ae70::8"
-ovn-nbctl lsp-set-type ls1-lp_ext2 external
-
-d1="$(ovn-nbctl create DHCP_Options cidr=10.0.0.0/24 \
-options="\"server_id\"=\"10.0.0.1\" \"server_mac\"=\"ff:10:00:00:00:01\" \
-\"lease_time\"=\"3600\" \"router\"=\"10.0.0.1\"")"
-
-d2="$(ovn-nbctl create DHCP_Options cidr="ae70\:\:/64" \
-options="\"server_id\"=\"00:00:00:10:00:01\"")"
-
-ovn-nbctl lsp-set-dhcpv4-options ls1-lp1 ${d1}
-ovn-nbctl lsp-set-dhcpv4-options ls1-lp_ext1 ${d1}
-ovn-nbctl lsp-set-dhcpv4-options ls1-lp_ext2 ${d1}
-
-ovn-nbctl lsp-set-dhcpv6-options ls1-lp1 ${d2}
-ovn-nbctl lsp-set-dhcpv6-options ls1-lp_ext1 ${d2}
-ovn-nbctl lsp-set-dhcpv6-options ls1-lp_ext2 ${d2}
-
-# Create a logical router and connect it to ls1
-ovn-nbctl lr-add lr0
-ovn-nbctl lrp-add lr0 lr0-ls1 a0:10:00:00:00:01 10.0.0.1/24
-ovn-nbctl lsp-add ls1 ls1-lr0
-ovn-nbctl set Logical_Switch_Port ls1-lr0 type=router \
- options:router-port=lr0-ls1 addresses=router
-
-# Create HA chassis group
-ovn-nbctl ha-chassis-group-add hagrp1
-ovn-nbctl ha-chassis-group-add-chassis hagrp1 hv1 30
-
-hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group name="hagrp1"`
-
-# There should be 1 HA_Chassis rows with chassis sets
-OVS_WAIT_UNTIL([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
-| grep '-' | wc -l ], [0], [1
-])
-
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-phys hv1-ext1 -- \
- set interface hv1-ext1 options:tx_pcap=hv1/ext1-tx.pcap \
- options:rxq_pcap=hv1/ext1-rx.pcap \
- ofport-request=2
-ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-phys hv2-ext2 -- \
- set interface hv2-ext2 options:tx_pcap=hv2/ext2-tx.pcap \
- options:rxq_pcap=hv2/ext2-rx.pcap \
- ofport-request=2
-ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-as hv3
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.3
-ovs-vsctl -- add-port br-phys hv3-ext3 -- \
- set interface hv3-ext3 options:tx_pcap=hv3/ext3-tx.pcap \
- options:rxq_pcap=hv3/ext3-rx.pcap \
- ofport-request=2
-ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-# No DHCPv4/v6 flows for the external port - ls1-lp_ext1 - 10.0.0.6 in hv1 and
-# hv2 as ha-chassis-group is not set and no localnet port added to ls1.
-AT_CHECK([ovn-sbctl dump-flows ls1 | grep "offerip = 10.0.0.6" | \
-wc -l], [0], [0
-])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0
-])
-
-hv1_uuid=$(ovn-sbctl list chassis hv1 | grep uuid | awk '{print $3}')
-hv2_uuid=$(ovn-sbctl list chassis hv2 | grep uuid | awk '{print $3}')
-hv3_uuid=$(ovn-sbctl list chassis hv3 | grep uuid | awk '{print $3}')
-
-# The port_binding row for ls1-lp_ext1 should have empty chassis
-chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
-
-AT_CHECK([test x$chassis == x], [0], [])
-
-# Associate hagrp1 ha-chassis-group to ls1-lp_ext1
-ovn-nbctl --wait=hv set Logical_Switch_Port ls1-lp_ext1 \
-ha-chassis-group=$hagrp1_uuid
-
-# Get the hagrp1 uuid in SB DB.
-sb_hagrp1_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group \
-name="hagrp1"`
-
-# Wait till ls1-lp_ext1 port_binding has ha_chassis_group set
-OVS_WAIT_UNTIL(
- [sb_pb_hagrp=`ovn-sbctl --bare --columns ha_chassis_group find \
-port_binding logical_port=ls1-lp_ext1`
- test "$sb_pb_hagrp" = "$sb_hagrp1_uuid"])
-
-# No DHCPv4/v6 flows for the external port - ls1-lp_ext1 - 10.0.0.6 in hv1 and hv2
-# as no localnet port added to ls1 yet.
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0
-])
-
-# Add the localnet port to the logical switch ls1
-ovn-nbctl lsp-add ls1 ln-public
-ovn-nbctl lsp-set-addresses ln-public unknown
-ovn-nbctl lsp-set-type ln-public localnet
-ovn-nbctl --wait=hv lsp-set-options ln-public network_name=phys
-
-ln_public_key=$(ovn-sbctl list port_binding ln-public | grep tunnel_key | \
-awk '{print $3}')
-
-# The ls1-lp_ext1 should be bound to hv1 as only hv1 is part of the
-# ha chassis group.
-OVS_WAIT_UNTIL(
- [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
- test "$chassis" = "$hv1_uuid"])
-
-# There should be DHCPv4/v6 OF flows for the ls1-lp_ext1 port in hv1
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | grep reg14=0x$ln_public_key | \
-wc -l], [0], [3
-])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | \
-grep reg14=0x$ln_public_key | wc -l], [0], [1
-])
-
-# There should be no DHCPv4/v6 flows for ls1-lp_ext1 on hv2
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | wc -l], [0], [0
-])
-
-# No DHCPv4/v6 flows for the external port - ls1-lp_ext2 - 10.0.0.7 in hv1 and
-# hv2 as requested-chassis option is not set.
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.07" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.07" | wc -l], [0], [0
-])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.07" | wc -l], [0], [0
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.07" | wc -l], [0], [0
-])
-
-as hv1
-ovs-vsctl show
-
-# This shell function sends a DHCP request packet
-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ...
-test_dhcp() {
- local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 use_ip=$5
- shift; shift; shift; shift; shift;
- if test $use_ip != 0; then
- src_ip=$1
- dst_ip=$2
- shift; shift;
- else
- src_ip=`ip_to_hex 0 0 0 0`
- dst_ip=`ip_to_hex 255 255 255 255`
- fi
- local request=ffffffffffff${src_mac}0800451001100000000080110000${src_ip}${dst_ip}
- # udp header and dhcp header
- request=${request}0044004300fc0000
- request=${request}010106006359aa760000000000000000000000000000000000000000${src_mac}
- # client hardware padding
- request=${request}00000000000000000000
- # server hostname
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- # boot file name
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- request=${request}0000000000000000000000000000000000000000000000000000000000000000
- # dhcp magic cookie
- request=${request}63825363
- # dhcp message type
- request=${request}3501${dhcp_type}ff
-
- local srv_mac=$1 srv_ip=$2 expected_dhcp_opts=$3
- # total IP length will be the IP length of the request packet
- # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2)
- ip_len=`expr 280 + ${#expected_dhcp_opts} / 2`
- udp_len=`expr $ip_len - 20`
- ip_len=$(printf "%x" $ip_len)
- udp_len=$(printf "%x" $udp_len)
- # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len
- local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
- # udp header and dhcp header.
- # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
- reply=${reply}004300440${udp_len}0000020106006359aa760000000000000000
- # your ip address
- reply=${reply}${offer_ip}
- # next server ip address, relay agent ip address, client mac address
- reply=${reply}0000000000000000${src_mac}
- # client hardware padding
- reply=${reply}00000000000000000000
- # server hostname
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- # boot file name
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- reply=${reply}0000000000000000000000000000000000000000000000000000000000000000
- # dhcp magic cookie
- reply=${reply}63825363
- # dhcp message type
- local dhcp_reply_type=02
- if test $dhcp_type = 03; then
- dhcp_reply_type=05
- fi
- reply=${reply}3501${dhcp_reply_type}${expected_dhcp_opts}00000000ff00000000
- echo $reply >> ext1_v4.expected
-
- as hv1 ovs-appctl netdev-dummy/receive hv${inport}-ext${inport} $request
-}
-
-
-trim_zeros() {
- sed 's/\(00\)\{1,\}$//'
-}
-
-# This shell function sends a DHCPv6 request packet
-# test_dhcpv6 INPORT SRC_MAC SRC_LLA DHCPv6_MSG_TYPE OFFER_IP OUTPORT...
-# The OUTPORTs (zero or more) list the VIFs on which the original DHCPv6
-# packet should be received twice (one from ovn-controller and the other
-# from the "ovs-ofctl monitor br-int resume"
-test_dhcpv6() {
- local inport=$1 src_mac=$2 src_lla=$3 msg_code=$4 offer_ip=$5
- local req_pkt_in_expected=$6
- local request=ffffffffffff${src_mac}86dd00000000002a1101${src_lla}
- # dst ip ff02::1:2
- request=${request}ff020000000000000000000000010002
- # udp header and dhcpv6 header
- request=${request}02220223002affff${msg_code}010203
- # Client identifier
- request=${request}0001000a00030001${src_mac}
- # IA-NA (Identity Association for Non Temporary Address)
- request=${request}0003000c0102030400000e1000001518
- shift; shift; shift; shift; shift;
-
- local server_mac=000000100001
- local server_lla=fe80000000000000020000fffe100001
- local reply_code=07
- if test $msg_code = 01; then
- reply_code=02
- fi
- local msg_len=54
- if test $offer_ip = 1; then
- msg_len=28
- fi
- local reply=${src_mac}${server_mac}86dd0000000000${msg_len}1101
- reply=${reply}${server_lla}${src_lla}
-
- # udp header and dhcpv6 header
- reply=${reply}0223022200${msg_len}ffff${reply_code}010203
- # Client identifier
- reply=${reply}0001000a00030001${src_mac}
- # IA-NA
- if test $offer_ip != 1; then
- reply=${reply}0003002801020304ffffffffffffffff00050018${offer_ip}
- reply=${reply}ffffffffffffffff
- fi
- # Server identifier
- reply=${reply}0002000a00030001${server_mac}
-
- echo $reply | trim_zeros >> ext${inport}_v6.expected
- # The inport also receives the request packet since it is connected
- # to the br-phys.
- #echo $request >> ext${inport}_v6.expected
-
- as hv1 ovs-appctl netdev-dummy/receive hv${inport}-ext${inport} $request
-}
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-AT_CAPTURE_FILE([ofctl_monitor0_hv1.log])
-as hv1 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0_hv1.log
-
-AT_CAPTURE_FILE([ofctl_monitor0_hv2.log])
-as hv2 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0_hv2.log
-
-AT_CAPTURE_FILE([ofctl_monitor0_hv3.log])
-as hv3 ovs-ofctl monitor br-int resume --detach --no-chdir \
---pidfile=ovs-ofctl0.pid 2> ofctl_monitor0_hv3.log
-
-as hv1
-reset_pcap_file hv1-ext1 hv1/ext1
-
-# Send DHCPDISCOVER.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-server_mac=ff1000000001
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-$expected_dhcp_opts
-
-# NXT_RESUMEs should be 1 in hv1.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 0 in hv2.
-OVS_WAIT_UNTIL([test 0 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap > ext1_v4.packets
-cat ext1_v4.expected | cut -c -48 > expout
-AT_CHECK([cat ext1_v4.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat ext1_v4.expected | cut -c 53- > expout
-AT_CHECK([cat ext1_v4.packets | cut -c 53-], [0], [expout])
-
-# ovs-ofctl also resumes the packets and this causes other ports to receive
-# the DHCP request packet. So reset the pcap files so that its easier to test.
-as hv1
-reset_pcap_file hv1-ext1 hv1/ext1
-
-rm -f ext1_v4.expected
-rm -f ext1_v4.packets
-
-# Send DHCPv6 request
-src_mac=f00000000003
-src_lla=fe80000000000000f20000fffe000003
-offer_ip=ae700000000000000000000000000006
-test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip
-
-# NXT_RESUMEs should be 2 in hv1.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 0 in hv2.
-OVS_WAIT_UNTIL([test 0 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap | \
-sort > ext1_v6.packets
-cat ext1_v6.expected | cut -c -120 > expout
-AT_CHECK([cat ext1_v6.packets | cut -c -120], [0], [expout])
-# Skipping the UDP checksum
-cat ext1_v6.expected | cut -c 125- > expout
-AT_CHECK([cat ext1_v6.packets | cut -c 125-], [0], [expout])
-
-rm -f ext1_v6.expected
-rm -f ext1_v6.packets
-
-as hv1
-reset_pcap_file hv1-ext1 hv1/ext1
-
-# Delete the ha-chassis hv1.
-ovn-nbctl ha-chassis-group-remove-chassis hagrp1 hv1
-OVS_WAIT_UNTIL(
- [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
- test "$chassis" = ""])
-
-# Add hv2 to the ha chassis group
-ovn-nbctl --wait=hv ha-chassis-group-add-chassis hagrp1 hv2 20
-
-ovn-sbctl list ha_chassis_group
-ovn-sbctl list ha_chassis
-
-ovn-sbctl find port_binding logical_port=ls1-lp_ext1
-
-# The ls1-lp_ext1 should be bound to hv2
-OVS_WAIT_UNTIL(
- [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
- test "$chassis" = "$hv2_uuid"])
-
-# There should be OF flows for DHCP4/v6 for the ls1-lp_ext1 port in hv2
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | grep reg14=0x$ln_public_key | \
-wc -l], [0], [3
-])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | \
-grep reg14=0x$ln_public_key | wc -l], [0], [1
-])
-
-# There should be no DHCPv4/v6 flows for ls1-lp_ext1 on hv1
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep "0a.00.00.06" | wc -l], [0], [0
-])
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=20 | \
-grep controller | grep tp_src=546 | grep \
-"ae.70.00.00.00.00.00.00.00.00.00.00.00.00.00.06" | \
-grep reg14=0x$ln_public_key | wc -l], [0], [0
-])
-
-# Send DHCPDISCOVER again for hv1/ext1. The DHCP response should come from
-# hv2 ovn-controller.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-server_mac=ff1000000001
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-$expected_dhcp_opts
-
-# NXT_RESUMEs should be 2 in hv1.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 1 in hv2.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap > ext1_v4.packets
-cat ext1_v4.expected | cut -c -48 > expout
-AT_CHECK([cat ext1_v4.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat ext1_v4.expected | cut -c 53- > expout
-AT_CHECK([cat ext1_v4.packets | cut -c 53-], [0], [expout])
-
-# ovs-ofctl also resumes the packets and this causes other ports to receive
-# the DHCP request packet. So reset the pcap files so that its easier to test.
-as hv1
-reset_pcap_file hv1-ext1 hv1/ext1
-
-rm -f ext1_v4.expected
-
-# Send DHCPv6 request again
-src_mac=f00000000003
-src_lla=fe80000000000000f20000fffe000003
-offer_ip=ae700000000000000000000000000006
-test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip 1
-
-# NXT_RESUMEs should be 2 in hv1.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 2 in hv2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap | \
-sort > ext1_v6.packets
-cat ext1_v6.expected | cut -c -120 > expout
-AT_CHECK([cat ext1_v6.packets | cut -c -120], [0], [expout])
-# Skipping the UDP checksum
-cat ext1_v6.expected | cut -c 125- > expout
-AT_CHECK([cat ext1_v6.packets | cut -c 125-], [0], [expout])
-
-rm -f ext1_v6.expected
-rm -f ext1_v6.packets
-
-as hv1
-ovs-vsctl show
-reset_pcap_file hv1-ext1 hv1/ext1
-reset_pcap_file br-phys_n1 hv1/br-phys_n1
-reset_pcap_file br-phys hv1/br-phys
-
-as hv2
-ovs-vsctl show
-reset_pcap_file hv2-ext2 hv2/ext2
-reset_pcap_file br-phys_n1 hv2/br-phys_n1
-reset_pcap_file br-phys hv2/br-phys
-
-# From ls1-lp_ext1, send ARP request for the router ip. The ARP
-# response should come from the router pipeline of hv2.
-ext1_mac=f00000000003
-router_mac=a01000000001
-ext1_ip=`ip_to_hex 10 0 0 6`
-router_ip=`ip_to_hex 10 0 0 1`
-arp_request=ffffffffffff${ext1_mac}08060001080006040001${ext1_mac}${ext1_ip}000000000000${router_ip}
-
-as hv1 ovs-appctl netdev-dummy/receive hv1-ext1 $arp_request
-expected_response=${src_mac}${router_mac}08060001080006040002${router_mac}${router_ip}${ext1_mac}${ext1_ip}
-echo $expected_response > expout
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap > ext1_arp_resp
-AT_CHECK([cat ext1_arp_resp], [0], [expout])
-
-# Verify that the response came from hv2
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap > ext1_arp_resp
-AT_CHECK([cat ext1_arp_resp], [0], [expout])
-
-# Now add 3 ha chassis to the ha chassis group
-ovn-nbctl ha-chassis-group-add-chassis hagrp1 hv1 30
-ovn-nbctl ha-chassis-group-add-chassis hagrp1 hv2 20
-ovn-nbctl ha-chassis-group-add-chassis hagrp1 hv3 10
-
-# hv1 should be master and claim ls1-lp_ext1
-OVS_WAIT_UNTIL(
- [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
- test "$chassis" = "$hv1_uuid"])
-
-as hv1
-ovs-vsctl show
-reset_pcap_file hv1-ext1 hv1/ext1
-reset_pcap_file br-phys_n1 hv1/br-phys_n1
-reset_pcap_file br-phys hv1/br-phys
-
-as hv2
-ovs-vsctl show
-reset_pcap_file hv2-ext2 hv2/ext2
-reset_pcap_file br-phys_n1 hv2/br-phys_n1
-reset_pcap_file br-phys hv2/br-phys
-
-as hv3
-ovs-vsctl show
-reset_pcap_file hv3-ext3 hv3/ext3
-reset_pcap_file br-phys_n1 hv3/br-phys_n1
-reset_pcap_file br-phys hv3/br-phys
-
-# Send DHCPDISCOVER.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-server_mac=ff1000000001
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-$expected_dhcp_opts
-
-# NXT_RESUMEs should be 3 in hv1.
-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 2 in hv2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap > ext1_v4.packets
-cat ext1_v4.expected | cut -c -48 > expout
-AT_CHECK([cat ext1_v4.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat ext1_v4.expected | cut -c 53- > expout
-AT_CHECK([cat ext1_v4.packets | cut -c 53-], [0], [expout])
-
-# ovs-ofctl also resumes the packets and this causes other ports to receive
-# the DHCP request packet. So reset the pcap files so that its easier to test.
-as hv1
-reset_pcap_file hv1-ext1 hv1/ext1
-
-rm -f ext1_v4.expected
-rm -f ext1_v4.packets
-
-# Send DHCPv6 request
-src_mac=f00000000003
-src_lla=fe80000000000000f20000fffe000003
-offer_ip=ae700000000000000000000000000006
-test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip
-
-# NXT_RESUMEs should be 4 in hv1.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 2 in hv2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap | \
-sort > ext1_v6.packets
-cat ext1_v6.expected | cut -c -120 > expout
-AT_CHECK([cat ext1_v6.packets | cut -c -120], [0], [expout])
-# Skipping the UDP checksum
-cat ext1_v6.expected | cut -c 125- > expout
-AT_CHECK([cat ext1_v6.packets | cut -c 125-], [0], [expout])
-
-rm -f ext1_v6.expected
-rm -f ext1_v6.packets
-as hv1 reset_pcap_file hv1-ext1 hv1/ext1
-
-# Now increase the priority of hv3 so it becomes master.
-ovn-nbctl ha-chassis-group-add-chassis hagrp1 hv3 50
-
-# hv3 should be master and claim ls1-lp_ext1
-OVS_WAIT_UNTIL(
- [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
- test "$chassis" = "$hv3_uuid"])
-
-as hv1
-ovs-vsctl show
-reset_pcap_file hv1-ext1 hv1/ext1
-reset_pcap_file br-phys_n1 hv1/br-phys_n1
-reset_pcap_file br-phys hv1/br-phys
-
-as hv2
-ovs-vsctl show
-reset_pcap_file hv2-ext2 hv2/ext2
-reset_pcap_file br-phys_n1 hv2/br-phys_n1
-reset_pcap_file br-phys hv2/br-phys
-
-as hv2
-ovs-vsctl show
-reset_pcap_file hv3-ext3 hv3/ext3
-reset_pcap_file br-phys_n1 hv3/br-phys_n1
-reset_pcap_file br-phys hv3/br-phys
-
-# Send DHCPDISCOVER.
-offer_ip=`ip_to_hex 10 0 0 6`
-server_ip=`ip_to_hex 10 0 0 1`
-server_mac=ff1000000001
-expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
-$expected_dhcp_opts
-
-# NXT_RESUMEs should be 4 in hv1.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 2 in hv2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 1 in hv3.
-OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor0_hv3.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap > ext1_v4.packets
-cat ext1_v4.expected | cut -c -48 > expout
-AT_CHECK([cat ext1_v4.packets | cut -c -48], [0], [expout])
-# Skipping the IPv4 checksum.
-cat ext1_v4.expected | cut -c 53- > expout
-AT_CHECK([cat ext1_v4.packets | cut -c 53-], [0], [expout])
-
-# ovs-ofctl also resumes the packets and this causes other ports to receive
-# the DHCP request packet. So reset the pcap files so that its easier to test.
-as hv1
-reset_pcap_file hv1-ext1 hv1/ext1
-
-rm -f ext1_v4.expected
-rm -f ext1_v4.packets
-
-# Send DHCPv6 request
-src_mac=f00000000003
-src_lla=fe80000000000000f20000fffe000003
-offer_ip=ae700000000000000000000000000006
-test_dhcpv6 1 $src_mac $src_lla 01 $offer_ip
-
-# NXT_RESUMEs should be 4 in hv1.
-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor0_hv1.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 2 in hv2.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv2.log | grep -c NXT_RESUME`])
-
-# NXT_RESUMEs should be 2 in hv3.
-OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor0_hv3.log | grep -c NXT_RESUME`])
-
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/ext1-tx.pcap | \
-sort > ext1_v6.packets
-cat ext1_v6.expected | cut -c -120 > expout
-AT_CHECK([cat ext1_v6.packets | cut -c -120], [0], [expout])
-# Skipping the UDP checksum
-cat ext1_v6.expected | cut -c 125- > expout
-AT_CHECK([cat ext1_v6.packets | cut -c 125-], [0], [expout])
-
-# disconnect hv3 from the network, hv1 should take over
-as hv3
-port=${sandbox}_br-phys
-as main ovs-vsctl del-port n1 $port
-
-# hv1 should be master and claim ls1-lp_ext1
-OVS_WAIT_UNTIL(
- [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=ls1-lp_ext1`
- test "$chassis" = "$hv1_uuid"])
-
-OVN_CLEANUP([hv1],[hv2],[hv3])
-AT_CLEANUP
-
-AT_SETUP([ovn -- Address Set Incremental Processing])
-AT_KEYWORDS([ovn_as_inc])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.10
-
-ovn-nbctl ls-add ls1
-for i in 1 2; do
- ovn-nbctl lsp-add ls1 lp$i \
- -- lsp-set-addresses lp$i "f0:00:00:00:00:0$i 192.168.1.$i"
- as hv1 ovs-vsctl \
- -- add-port br-int vif$i \
- -- set Interface vif$i \
- external-ids:iface-id=lp$i
-done
-
-for i in 1 2 3; do
- as1_uuid=`ovn-nbctl --wait=hv create addr name=as1`
- as2_uuid=`ovn-nbctl --wait=hv create addr name=as2`
- ovn-nbctl --wait=hv acl-add ls1 to-lport 200 \
- 'outport=="lp1" && ip4 && ip4.src == {$as1, $as2}' allow-related
- ovn-nbctl --wait=hv set addr as1 addresses="10.1.2.10"
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.10"], [0], [ignore])
-
- # Update address set as1
- ovn-nbctl --wait=hv set addr as1 addresses="10.1.2.10 10.1.2.11"
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.11"], [0], [ignore])
-
- # Update address set as2
- ovn-nbctl --wait=hv set addr as2 addresses="10.1.2.12 10.1.2.13"
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.12"], [0], [ignore])
-
- # Add another ACL referencing as1
- n_flows_before=`ovs-ofctl dump-flows br-int | grep "10.1.2.10" | wc -l`
- ovn-nbctl --wait=hv acl-add ls1 to-lport 200 \
- 'outport=="lp2" && ip4 && ip4.src == $as1' allow-related
- n_flows_after=`ovs-ofctl dump-flows br-int | grep "10.1.2.10" | wc -l`
- AT_CHECK([test $(expr $n_flows_before \* 2) = $n_flows_after], [0], [ignore])
-
- # Remove an ACL
- ovn-nbctl --wait=hv acl-del ls1 to-lport 200 \
- 'outport=="lp2" && ip4 && ip4.src == $as1'
- n_flows_after=`ovs-ofctl dump-flows br-int | grep "10.1.2.10" | wc -l`
- AT_CHECK([test $n_flows_before = $n_flows_after], [0], [ignore])
-
- # Remove as1 while it is still used by an ACL, the lflows should be reparsed and
- # parsing should fail.
- echo "before del as1"
- ovn-nbctl list addr | grep as1
- ovn-nbctl --wait=hv destroy addr $as1_uuid
- echo "after del as1"
- ovn-nbctl list addr | grep as1
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.10"], [1], [ignore])
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.12"], [1], [ignore])
-
- # Recreate as1
- as1_uuid=`ovn-nbctl --wait=hv create addr name=as1`
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.12"], [0], [ignore])
-
- # Remove ACLs and address sets
- ovn-nbctl --wait=hv destroy addr $as1_uuid -- destroy addr $as2_uuid
- AT_CHECK([ovs-ofctl dump-flows br-int | grep "10.1.2.12"], [1], [ignore])
-
- ovn-nbctl --wait=hv acl-del ls1
-done
-
-# Gracefully terminate daemons
-OVN_CLEANUP([hv1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- ovn-controller restart])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One Logical Router: ro, with two logical switches sw1 and sw2.
-# sw1 is for subnet 10.0.0.0/8
-# sw2 is for subnet 20.0.0.0/8
-# sw1 has a single port bound on hv1
-# sw2 has a single port bound on hv2
-
-ovn-nbctl lr-add ro
-ovn-nbctl ls-add sw1
-ovn-nbctl ls-add sw2
-
-sw1_ro_mac=00:00:10:00:00:01
-sw1_ro_ip=10.0.0.1
-sw2_ro_mac=00:00:20:00:00:01
-sw2_ro_ip=20.0.0.1
-sw1_p1_mac=00:00:10:00:00:02
-sw1_p1_ip=10.0.0.2
-sw2_p1_mac=00:00:20:00:00:02
-sw2_p1_ip=20.0.0.2
-
-ovn-nbctl lrp-add ro ro-sw1 $sw1_ro_mac ${sw1_ro_ip}/8
-ovn-nbctl lrp-add ro ro-sw2 $sw2_ro_mac ${sw2_ro_ip}/8
-ovn-nbctl lsp-add sw1 sw1-ro -- set Logical_Switch_Port sw1-ro type=router \
- options:router-port=ro-sw1 addresses=\"$sw1_ro_mac\"
-ovn-nbctl lsp-add sw2 sw2-ro -- set Logical_Switch_Port sw2-ro type=router \
- options:router-port=ro-sw2 addresses=\"$sw2_ro_mac\"
-
-ovn-nbctl lsp-add sw1 sw1-p1 \
--- lsp-set-addresses sw1-p1 "$sw1_p1_mac $sw1_p1_ip"
-
-ovn-nbctl lsp-add sw2 sw2-p1 \
--- lsp-set-addresses sw2-p1 "$sw2_p1_mac $sw2_p1_ip"
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=sw1-p1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=sw2-p1 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-OVN_POPULATE_ARP
-
-sleep 1
-
-packet="inport==\"sw1-p1\" && eth.src==$sw1_p1_mac && eth.dst==$sw1_ro_mac &&
- ip4 && ip.ttl==64 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-
-# Start by Sending the packet and make sure it makes it there as expected
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-# Expected packet has TTL decreased by 1
-expected="eth.src==$sw2_ro_mac && eth.dst==$sw2_p1_mac &&
- ip4 && ip.ttl==63 && ip4.src==$sw1_p1_ip && ip4.dst==$sw2_p1_ip &&
- udp && udp.src==53 && udp.dst==4369"
-echo $expected | ovstest test-ovn expr-to-packets > expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-# Stop ovn-controller on hv2 with --restart flag
-as hv2 ovs-appctl -t ovn-controller exit --restart
-
-# Now send the packet again. This time, it should still arrive
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-cat expected expected > expected2
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected2])
-
-# Start ovn-controller again just so OVN_CLEANUP doesn't complain
-as hv2 start_daemon ovn-controller
-
-OVN_CLEANUP([hv1],[hv2])
-
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- ovn-nbctl duplicate addresses])
-ovn_start
-
-# Set up a switch with some switch ports of varying address types
-ovn-nbctl ls-add sw1
-ovn-nbctl set logical_switch sw1 other_config:subnet=192.168.0.0/24
-
-ovn-nbctl lsp-add sw1 sw1-p1
-ovn-nbctl lsp-add sw1 sw1-p2
-ovn-nbctl lsp-add sw1 sw1-p3
-ovn-nbctl lsp-add sw1 sw1-p4
-
-ovn-nbctl lsp-set-addresses sw1-p1 "00:00:00:00:00:01 10.0.0.1 aef0::1" "00:00:00:00:00:02 10.0.0.2 aef0::2"
-ovn-nbctl lsp-set-addresses sw1-p2 "00:00:00:00:00:03 dynamic"
-ovn-nbctl lsp-set-addresses sw1-p3 "dynamic"
-ovn-nbctl lsp-set-addresses sw1-p4 "router"
-ovn-nbctl lsp-set-addresses sw1-p5 "unknown"
-
-ovn-nbctl list logical_switch_port
-
-# Now try to add duplicate addresses on a new port. These should all fail
-ovn-nbctl --wait=sb lsp-add sw1 sw1-p5
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.1"], [1], [],
-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.1
-])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.2"], [1], [],
-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.2
-])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::1"], [1], [],
-[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::1
-])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::2"], [1], [],
-[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::2
-])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.2"], [1], [],
-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.2
-])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.3"], [1], [],
-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.3
-])
-
-# Now try re-setting sw1-p1. This should succeed
-AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p1 "00:00:00:00:00:01 10.0.0.1 aef0::1"])
-
-# Now create a new switch and try setting IP addresses the same as the
-# first switch. This should succeed.
-ovn-nbctl ls-add sw2
-ovn-nbctl lsp-add sw2 sw2-p1
-
-AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 10.0.0.1"])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 192.168.0.2"])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 192.168.0.3"])
-AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 aef0::1"])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- router - check packet length - icmp defrag])
-AT_KEYWORDS([check packet length])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 sw0-port1
-ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3"
-
-ovn-nbctl lr-add lr0
-ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
-ovn-nbctl lsp-add sw0 sw0-lr0
-ovn-nbctl lsp-set-type sw0-lr0 router
-ovn-nbctl lsp-set-addresses sw0-lr0 router
-ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
-
-ovn-nbctl ls-add public
-ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
-ovn-nbctl lsp-add public public-lr0
-ovn-nbctl lsp-set-type public-lr0 router
-ovn-nbctl lsp-set-addresses public-lr0 router
-ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
-
-# localnet port
-ovn-nbctl lsp-add public ln-public
-ovn-nbctl lsp-set-type ln-public localnet
-ovn-nbctl lsp-set-addresses ln-public unknown
-ovn-nbctl lsp-set-options ln-public network_name=phys
-
-ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20
-ovn-nbctl lr-nat-add lr0 snat 172.168.0.100 10.0.0.0/24
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=sw0-port1 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
- options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
- options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-test_ip_packet_larger() {
- local icmp_pmtu_reply_expected=$1
-
- # Send ip packet from sw0-port1 to outside
- src_mac="505400000001" # sw-port1 mac
- dst_mac="00000000ff01" # sw0-lr0 mac (internal router leg)
- src_ip=`ip_to_hex 10 0 0 3`
- dst_ip=`ip_to_hex 172 168 0 3`
- # Set the packet length to 100.
- pkt_len=0064
- packet=${dst_mac}${src_mac}08004500${pkt_len}0000000040010000
- orig_packet_l3=${src_ip}${dst_ip}0304000000000000
- orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
- orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
- orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
- orig_packet_l3=${orig_packet_l3}000000000000000000000000000000000000
- packet=${packet}${orig_packet_l3}
-
- gw_ip_garp=ffffffffffff00002020121308060001080006040001000020201213aca80064000000000000aca80064
-
- # If icmp_pmtu_reply_expected is 0, it means the packet is lesser than
- # the gateway mtu and should be delivered to the provider bridge via the
- # localnet port.
- # If icmp_pmtu_reply_expected is 1, it means the packet is larger than
- # the gateway mtu and ovn-controller should drop the packet and instead
- # generate ICMPv4 Destination Unreachable message with pmtu set to 42.
- if test $icmp_pmtu_reply_expected = 0; then
- # Packet to expect at br-phys.
- src_mac="000020201213"
- dst_mac="00000012af11"
- src_ip=`ip_to_hex 10 0 0 3`
- dst_ip=`ip_to_hex 172 168 0 3`
- expected=${dst_mac}${src_mac}08004500${pkt_len}000000003f010100
- expected=${expected}${src_ip}${dst_ip}0304000000000000
- expected=${expected}000000000000000000000000000000000000
- expected=${expected}000000000000000000000000000000000000
- expected=${expected}000000000000000000000000000000000000
- expected=${expected}000000000000000000000000000000000000
- echo $expected > br_phys_n1.expected
- echo $gw_ip_garp >> br_phys_n1.expected
- else
- # MTU would be 100 - 18 = 82 (hex 0052)
- mtu=0052
- src_ip=`ip_to_hex 10 0 0 1`
- dst_ip=`ip_to_hex 10 0 0 3`
- # pkt len should be 128 (28 (icmp packet) + 100 (orig ip + payload))
- reply_pkt_len=0080
- ip_csum=bd91
- icmp_reply=${src_mac}${dst_mac}08004500${reply_pkt_len}00004000fe016879
- icmp_reply=${icmp_reply}${src_ip}${dst_ip}0304${ip_csum}0000${mtu}
- icmp_reply=${icmp_reply}4500${pkt_len}000000003f010100
- icmp_reply=${icmp_reply}${orig_packet_l3}
- echo $icmp_reply > hv1-vif1.expected
- fi
-
- as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
- as hv1 reset_pcap_file hv1-vif1 hv1/vif1
-
- # Send packet from sw0-port1 to outside
- as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
-
- if test $icmp_pmtu_reply_expected = 0; then
- OVN_CHECK_PACKETS([hv1/br-phys_n1-tx.pcap], [br_phys_n1.expected])
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > pkts
- # hv1/vif1-tx.pcap can receive the GARP packet generated by ovn-controller
- # for the gateway router port. So ignore this packet.
- cat pkts | grep -v $gw_ip_garp > packets
- AT_CHECK([cat packets], [0], [])
- else
- OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [hv1-vif1.expected])
- $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap > \
- pkts
- # hv1/br-phys_n1-tx.pcap can receive the GARP packet generated by ovn-controller
- # for the gateway router port. So ignore this packet.
- cat pkts | grep -v $gw_ip_garp > packets
- AT_CHECK([cat packets], [0], [])
- fi
-}
-
-ovn-nbctl show
-ovn-sbctl show
-
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int \
-| grep "check_pkt_larger" | wc -l], [0], [[0
-]])
-dp_uuid=$(ovn-sbctl find datapath_binding | grep sw0 -B2 | grep _uuid | \
-awk '{print $3}')
-ovn-sbctl create MAC_Binding ip=172.168.0.3 datapath=$dp_uuid \
-logical_port=lr0-public mac="00\:00\:00\:12\:af\:11"
-
-# Set the gateway mtu to 100. If the packet length is > 100, ovn-controller
-# should send icmp host not reachable with pmtu set to 100.
-ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=100
-as hv3 ovs-appctl netdev-dummy/receive hv3-vif1 $arp_reply
-OVS_WAIT_UNTIL([
- test `as hv1 ovs-ofctl dump-flows br-int | grep "check_pkt_larger(100)" | \
- wc -l` -eq 1
-])
-
-icmp_reply_expected=1
-test_ip_packet_larger $icmp_reply_expected
-
-# Set the gateway mtu to 500.
-ovn-nbctl --wait=hv set logical_router_port lr0-public options:gateway_mtu=500
-as hv3 ovs-appctl netdev-dummy/receive hv3-vif1 $arp_reply
-OVS_WAIT_UNTIL([
- test `as hv1 ovs-ofctl dump-flows br-int | grep "check_pkt_larger(500)" | \
- wc -l` -eq 1
-])
-
-# Now the packet should be sent via the localnet port to br-phys.
-icmp_reply_expected=0
-test_ip_packet_larger $icmp_reply_expected
-OVN_CLEANUP([hv1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- IP packet buffering])
-AT_KEYWORDS([ip-buffering])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# One LR lr0 that has switches sw0 (192.168.1.0/24) and
-# sw1 (172.16.1.0/24) connected to it.
-#
-# Physical network:
-# Tw0 hypervisors hv[12].
-# hv1 hosts vif sw0-p0.
-# hv1 hosts vif sw1-p0.
-
-send_icmp_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv4_src=$5 ipv4_dst=$6 ip_chksum=$7 data=$8
- shift 8
-
- local ip_ttl=ff
- local ip_len=001c
- local packet=${eth_dst}${eth_src}08004500${ip_len}00004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${data}
- as hv$hv ovs-appctl netdev-dummy/receive hv$hv-vif$inport $packet
-}
-
-send_icmp6_packet() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ipv6_src=$5 ipv6_dst=$6 ipv6_router=$7 exp_icmp_chksum=$8
- shift 8
-
- local ip6_hdr=6000000000083aff${ipv6_src}${ipv6_dst}
- local packet=${eth_dst}${eth_src}86dd${ip6_hdr}8000dcb662f00001
-
- as hv$hv ovs-appctl netdev-dummy/receive hv$hv-vif$inport $packet
-}
-
-get_arp_req() {
- local eth_src=$1 spa=$2 tpa=$3
- local request=ffffffffffff${eth_src}08060001080006040001${eth_src}${spa}000000000000${tpa}
- echo $request
-}
-
-send_arp_reply() {
- local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
- local request=${eth_dst}${eth_src}08060001080006040002${eth_src}${spa}${eth_dst}${tpa}
- as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
-}
-
-send_na() {
- local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 src_ip=$5 dst_ip=$6
- local ip6_hdr=6000000000203aff${src_ip}${dst_ip}
- local request=${eth_dst}${eth_src}86dd${ip6_hdr}8800d78440000000${src_ip}0201${eth_src}
-
- as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
-}
-
-get_nd() {
- local eth_src=$1 src_ip=$2 dst_ip=$3 ta=$4
- local ip6_hdr=6000000000203aff${src_ip}${dst_ip}
- request=3333ff000010${eth_src}86dd${ip6_hdr}8700357600000000${ta}0101${eth_src}
-
- echo $request
-}
-
-net_add n1
-
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=sw0-p0 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=sw1-p0 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-
-ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
-ovn-nbctl ls-add sw0
-ovn-nbctl ls-add sw1
-
-ovn-nbctl lrp-add lr0 sw0 00:00:01:01:02:03 192.168.1.1/24 2001:0:0:0:0:0:0:1/64
-ovn-nbctl lsp-add sw0 rp-sw0 -- set Logical_Switch_Port rp-sw0 \
- type=router options:router-port=sw0 \
- -- lsp-set-addresses rp-sw0 router
-
-ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64
-ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
- type=router options:router-port=sw1 \
- -- lsp-set-addresses rp-sw1 router
-
-ovn-nbctl lsp-add sw0 sw0-p0 \
- -- lsp-set-addresses sw0-p0 "f0:00:00:01:02:03 192.168.1.2 2001::2"
-
-ovn-nbctl lsp-add sw1 sw1-p0 \
- -- lsp-set-addresses sw1-p0 unknown
-
-OVN_POPULATE_ARP
-ovn-nbctl --wait=hv sync
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-src_mac=f00000010203
-src_ip=$(ip_to_hex 192 168 1 2)
-src_ip6=20010000000000000000000000000002
-
-router_mac0=000001010203
-router_mac1=000002010203
-router_ip=$(ip_to_hex 172 16 1 1)
-router_ip6=20020000000000000000000000000001
-
-dst_mac=001122334455
-dst_ip=$(ip_to_hex 172 16 1 10)
-dst_ip6=20020000000000000000000000000010
-
-data=0800bee4391a0001
-
-send_icmp_packet 1 1 $src_mac $router_mac0 $src_ip $dst_ip 0000 $data
-send_arp_reply 2 1 $dst_mac $router_mac1 $dst_ip $router_ip
-echo $(get_arp_req $router_mac1 $router_ip $dst_ip) > expected
-echo "${dst_mac}${router_mac1}08004500001c00004000fe010100${src_ip}${dst_ip}${data}" >> expected
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-nd_ip=ff0200000000000000000001ff000010
-ip6_hdr=6000000000083afe${src_ip6}${dst_ip6}
-
-send_icmp6_packet 1 1 $src_mac $router_mac0 $src_ip6 $dst_ip6
-echo $(get_nd $router_mac1 $src_ip6 $nd_ip $dst_ip6) >> expected
-echo "${dst_mac}${router_mac1}86dd${ip6_hdr}8000dcb662f00001" >> expected
-send_na 2 1 $dst_mac $router_mac1 $dst_ip6 $router_ip6
-
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1],[hv2])
-AT_CLEANUP
-
-AT_SETUP([ovn -- neighbor update on same HV])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# A public switch (pub) with a localnet port connected to two LRs (lr0 and lr1)
-# each with a distributed gateway port.
-# Two VMs: lp0 on sw0 connected to lr0
-# lp1 on sw1 connected to lr1
-#
-# This test adds a floating IP to each VM so when they are bound to the same
-# hypervisor, it checks that the GARP sent by ovn-controller causes the
-# MAC_Binding entries to be updated properly on each logical router.
-# It will also capture packets on the physical interface to make sure that the
-# GARPs have been sent out to the external network as well.
-
-# Create logical switches
-ovn-nbctl ls-add sw0
-ovn-nbctl ls-add sw1
-ovn-nbctl ls-add pub
-
-# Created localnet port on public switch
-ovn-nbctl lsp-add pub ln-pub
-ovn-nbctl lsp-set-type ln-pub localnet
-ovn-nbctl lsp-set-addresses ln-pub unknown
-ovn-nbctl lsp-set-options ln-pub network_name=phys
-
-# Create logical routers and connect them to public switch
-ovn-nbctl create Logical_Router name=lr0
-ovn-nbctl create Logical_Router name=lr1
-
-ovn-nbctl lrp-add lr0 lr0-pub f0:00:00:00:00:01 172.24.4.220/24
-ovn-nbctl lsp-add pub pub-lr0 -- set Logical_Switch_Port pub-lr0 \
- type=router options:router-port=lr0-pub options:nat-addresses="router" addresses="router"
-ovn-nbctl lrp-add lr1 lr1-pub f0:00:00:00:01:01 172.24.4.221/24
-ovn-nbctl lsp-add pub pub-lr1 -- set Logical_Switch_Port pub-lr1 \
- type=router options:router-port=lr1-pub options:nat-addresses="router" addresses="router"
-
-ovn-nbctl lrp-set-gateway-chassis lr0-pub hv1 10
-ovn-nbctl lrp-set-gateway-chassis lr1-pub hv1 10
-
-# Connect sw0 and sw1 to lr0 and lr1
-ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.254/24
-ovn-nbctl lsp-add sw0 sw0-lr0 -- set Logical_Switch_Port sw0-lr0 type=router \
- options:router-port=lr0-sw0 addresses="router"
-ovn-nbctl lrp-add lr1 lr1-sw1 00:00:00:00:ff:02 20.0.0.254/24
-ovn-nbctl lsp-add sw1 sw1-lr1 -- set Logical_Switch_Port sw1-lr1 type=router \
- options:router-port=lr1-sw1 addresses="router"
-
-
-# Add SNAT rules
-ovn-nbctl lr-nat-add lr0 snat 172.24.4.220 10.0.0.0/24
-ovn-nbctl lr-nat-add lr1 snat 172.24.4.221 20.0.0.0/24
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 172.24.4.1
-ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
-
-ovs-vsctl add-port br-int vif0 -- set Interface vif0 external-ids:iface-id=lp0
-ovs-vsctl add-port br-int vif1 -- set Interface vif1 external-ids:iface-id=lp1
-
-ovn-nbctl lsp-add sw0 lp0
-ovn-nbctl lsp-add sw1 lp1
-ovn-nbctl lsp-set-addresses lp0 "50:54:00:00:00:01 10.0.0.10"
-ovn-nbctl lsp-set-addresses lp1 "50:54:00:00:00:02 20.0.0.10"
-
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp0` = xup])
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp1` = xup])
-
-# Create two floating IPs, one for each VIF
-ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.24.4.100 10.0.0.10
-ovn-nbctl lr-nat-add lr1 dnat_and_snat 172.24.4.200 20.0.0.10
-
-# Check that the MAC_Binding entries have been properly created
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding logical_port="lr0-pub" ip="172.24.4.200" | wc -l` -gt 0])
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding logical_port="lr1-pub" ip="172.24.4.100" | wc -l` -gt 0])
-
-# Check that the GARPs went also to the external physical network
-# Wait until at least 4 packets have arrived and copy them to a separate file as
-# more GARPs are expected in the capture in order to avoid race conditions.
-OVS_WAIT_UNTIL([test `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | wc -l` -gt 4])
-$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys-tx.pcap | head -n4 > hv1/br-phys-tx4.pcap
-
-# GARP for lp0 172.24.4.100 on lr0-pub MAC (f0:00:00:00:00:01)
-echo "fffffffffffff0000000000108060001080006040001f00000000001ac180464000000000000ac180464" > expout
-# GARP for 172.24.4.220 on lr0-pub (f0:00:00:00:00:01)
-echo "fffffffffffff0000000000108060001080006040001f00000000001ac1804dc000000000000ac1804dc" >> expout
-# GARP for lp1 172.24.4.200 on lr1-pub MAC (f0:00:00:00:01:01)
-echo "fffffffffffff0000000010108060001080006040001f00000000101ac1804c8000000000000ac1804c8" >> expout
-# GARP for 172.24.4.221 on lr1-pub (f0:00:00:00:01:01)
-echo "fffffffffffff0000000010108060001080006040001f00000000101ac1804dd000000000000ac1804dd" >> expout
-AT_CHECK([sort hv1/br-phys-tx4.pcap], [0], [expout])
-#OVN_CHECK_PACKETS([hv1/br-phys-tx4.pcap], [br-phys.expected])
-
-OVN_CLEANUP([hv1])
-AT_CLEANUP
-
-AT_SETUP([ovn -- ipam to non-ipam])
-ovn_start
-
-ovn-nbctl --wait=hv set NB_Global . options:mac_prefix="0a:00:00:00:00:00"
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 p0 -- lsp-set-addresses p0 dynamic
-ovn-nbctl --wait=sb add Logical-Switch sw0 other_config subnet=192.168.1.0/24
-
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p0 dynamic_addresses], [0],
- ["0a:00:00:a8:01:03 192.168.1.2"
-])
-
-ovn-nbctl --wait=sb lsp-set-addresses p0 router
-
-ovn-nbctl get Logical-Switch-Port p0 dynamic_addresses
-
-AT_CHECK([ovn-nbctl get Logical-Switch-Port p0 dynamic_addresses], [0], [[[]]
-])
-AT_CLEANUP
-
-AT_SETUP([ovn -- ipam router ports])
-ovn_start
-
-ovn-nbctl ls-add sw
-ovn-nbctl set logical_switch sw other-config:subnet=192.168.1.0/24
-
-for i in 2 3 4; do
- ovn-nbctl lr-add ro$i
- ovn-nbctl lsp-add sw swp$i
- ovn-nbctl --wait=sb lsp-set-addresses swp$i "02:00:00:00:00:0$i dynamic"
- cidr=$(ovn-nbctl get logical_switch_port swp$i dynamic_addresses |cut -f2 -d' '|cut -f1 -d\")
- ovn-nbctl lrp-add ro$i rop$i 02:00:00:00:00:0$i $cidr/24 -- set logical_switch_port swp$i type=router options:router-port=rop$i addresses=router;
- AT_CHECK_UNQUOTED([ovn-nbctl get logical_router_port rop$i networks], [0], [[["192.168.1.$i/24"]]
-])
-done
-
-ovn-nbctl list logical_switch_port
-ovn-nbctl list logical_router_port
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- test transport zones])
-ovn_start
-
-net_add n1
-for i in 1 2 3 4 5; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.$i.1
-done
-
-dnl Wait for the changes to be propagated
-ovn-nbctl --wait=sb --timeout=3 sync
-ovn-nbctl --wait=hv --timeout=3 sync
-
-dnl Assert that each Chassis has a tunnel formed to every other Chassis
-as hv1
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv2-0
-ovn-hv3-0
-ovn-hv4-0
-ovn-hv5-0
-]])
-
-as hv2
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv3-0
-ovn-hv4-0
-ovn-hv5-0
-]])
-
-as hv3
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv2-0
-ovn-hv4-0
-ovn-hv5-0
-]])
-
-as hv4
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv2-0
-ovn-hv3-0
-ovn-hv5-0
-]])
-
-as hv5
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv2-0
-ovn-hv3-0
-ovn-hv4-0
-]])
-
-dnl Let's now add some Chassis to different transport zones
-dnl * hv1: Will be part of two transport zones: tz1 and tz2 so it
-dnl should have tunnels formed between the other two Chassis (hv2 and hv3)
-dnl
-dnl * hv2: Will be part of one transport zone: tz1. It should have a tunnel
-dnl to hv1 but not to hv3
-dnl
-dnl * hv3: Will be part of one transport zone: tz2. It should have a tunnel
-dnl to hv1 but not to hv2
-dnl
-dnl * hv4 and hv5: Will not have any TZ set so they will keep the tunnels
-dnl between themselves and remove the tunnels to other Chassis which now
-dnl belongs to some TZs
-dnl
-as hv1
-ovs-vsctl set open . external-ids:ovn-transport-zones=tz1,tz2
-
-as hv2
-ovs-vsctl set open . external-ids:ovn-transport-zones=tz1
-
-as hv3
-ovs-vsctl set open . external-ids:ovn-transport-zones=tz2
-
-dnl Wait for the changes to be propagated
-ovn-nbctl --wait=sb --timeout=3 sync
-ovn-nbctl --wait=hv --timeout=3 sync
-
-as hv1
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv2-0
-ovn-hv3-0
-]])
-
-as hv2
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-]])
-
-as hv3
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-]])
-
-as hv4
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv5-0
-]])
-
-as hv5
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv4-0
-]])
-
-dnl Removing the transport zones should make all Chassis to create
-dnl tunnels between every other Chassis again
-for i in 1 2 3; do
- as hv$i
- ovs-vsctl remove open . external-ids ovn-transport-zones
-done
-
-dnl Wait for the changes to be propagated
-ovn-nbctl --wait=sb --timeout=3 sync
-ovn-nbctl --wait=hv --timeout=3 sync
-
-as hv1
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv2-0
-ovn-hv3-0
-ovn-hv4-0
-ovn-hv5-0
-]])
-
-as hv2
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv3-0
-ovn-hv4-0
-ovn-hv5-0
-]])
-
-as hv3
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv2-0
-ovn-hv4-0
-ovn-hv5-0
-]])
-
-as hv4
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv2-0
-ovn-hv3-0
-ovn-hv5-0
-]])
-
-as hv5
-AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
-[[ovn-hv1-0
-ovn-hv2-0
-ovn-hv3-0
-ovn-hv4-0
-]])
-
-OVN_CLEANUP([hv1], [hv2], [hv3])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR chassis mac])
-ovn_start
-
-
-# In this test cases we create 2 switches, all connected to same
-# physical network (through br-phys on each HV). Each switch has
-# 1 VIF. Each HV has 1 VIF port. The first digit
-# of VIF port name indicates the hypervisor it is bound to, e.g.
-# lp23 means VIF 3 on hv2.
-#
-# Each switch's VLAN tag and their logical switch ports are:
-# - ls1:
-# - tagged with VLAN 101
-# - ports: lp11
-# - ls2:
-# - tagged with VLAN 201
-# - ports: lp22
-#
-# Note: a localnet port is created for each switch to connect to
-# physical network.
-
-for i in 1 2; do
- ls_name=ls$i
- ovn-nbctl ls-add $ls_name
- ln_port_name=ln$i
- if test $i -eq 1; then
- ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
- elif test $i -eq 2; then
- ovn-nbctl lsp-add $ls_name $ln_port_name "" 201
- fi
- ovn-nbctl lsp-set-addresses $ln_port_name unknown
- ovn-nbctl lsp-set-type $ln_port_name localnet
- ovn-nbctl lsp-set-options $ln_port_name network_name=phys
-done
-
-# lsp_to_ls LSP
-#
-# Prints the name of the logical switch that contains LSP.
-lsp_to_ls () {
- case $1 in dnl (
- lp?[[11]]) echo ls1 ;; dnl (
- lp?[[12]]) echo ls2 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-vif_to_ls () {
- case $1 in dnl (
- vif?[[11]]) echo ls1 ;; dnl (
- vif?[[12]]) echo ls2 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-hv_to_num () {
- case $1 in dnl (
- hv1) echo 1 ;; dnl (
- hv2) echo 2 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-vif_to_num () {
- case $1 in dnl (
- vif22) echo 22 ;; dnl (
- vif21) echo 21 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-vif_to_hv () {
- case $1 in dnl (
- vif[[1]]?) echo hv1 ;; dnl (
- vif[[2]]?) echo hv2 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-vif_to_lrp () {
- echo router-to-`vif_to_ls $1`
-}
-
-hv_to_chassis_mac () {
- case $1 in dnl (
- hv[[1]]) echo aa:bb:cc:dd:ee:11 ;; dnl (
- hv[[2]]) echo aa:bb:cc:dd:ee:22 ;; dnl (
- *) AT_FAIL_IF([:]) ;;
- esac
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-net_add n1
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
- ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
- ovn_attach n1 br-phys 192.168.0.$i
-
- ovs-vsctl add-port br-int vif$i$i -- \
- set Interface vif$i$i external-ids:iface-id=lp$i$i \
- options:tx_pcap=hv$i/vif$i$i-tx.pcap \
- options:rxq_pcap=hv$i/vif$i$i-rx.pcap \
- ofport-request=$i$i
-
- lsp_name=lp$i$i
- ls_name=$(lsp_to_ls $lsp_name)
-
- ovn-nbctl lsp-add $ls_name $lsp_name
- ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i"
- ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i
-
- OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
-
-done
-
-ovn-nbctl lr-add router
-ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24
-ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24
-
-ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router
-ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router
-
-ovn-nbctl --wait=sb sync
-#ovn-sbctl dump-flows
-
-ovn-nbctl show
-ovn-sbctl show
-
-OVN_POPULATE_ARP
-
-test_ip() {
- # This packet has bad checksums but logical L3 routing doesn't check.
- local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
- local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
- shift; shift; shift; shift; shift
- hv=`vif_to_hv $inport`
- hv_num=`hv_to_num $hv`
- chassis_mac=`hv_to_chassis_mac $hv`
- as $hv ovs-appctl netdev-dummy/receive $inport $packet
- #as $hv ovs-appctl ofproto/trace br-int in_port=$inport $packet
- in_ls=`vif_to_ls $inport`
- in_lrp=`vif_to_lrp $inport`
- for outport; do
- out_ls=`vif_to_ls $outport`
- if test $in_ls = $out_ls; then
- # Ports on the same logical switch receive exactly the same packet.
- echo $packet
- else
- # Routing decrements TTL and updates source and dest MAC
- # (and checksum).
- outport_num=`vif_to_num $outport`
- out_lrp=`vif_to_lrp $outport`
- echo f000000000${outport_num}aabbccddee${hv_num}${hv_num}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
- fi >> $outport.expected
- done
-}
-
-# Dump a bunch of info helpful for debugging if there's a failure.
-
-echo "------ OVN dump ------"
-ovn-nbctl show
-ovn-sbctl show
-
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-vsctl list Open_Vswitch
-
-echo "------ hv2 dump ------"
-as hv2 ovs-vsctl show
-as hv2 ovs-vsctl list Open_Vswitch
-
-echo "Send traffic"
-sip=`ip_to_hex 192 168 1 1`
-dip=`ip_to_hex 192 168 2 2`
-test_ip vif11 f00000000011 000001010203 $sip $dip vif22
-
-echo "----------- Post Traffic hv1 dump -----------"
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-as hv1 ovs-appctl fdb/show br-phys
-
-echo "----------- Post Traffic hv2 dump -----------"
-as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
-as hv2 ovs-appctl fdb/show br-phys
-
-OVN_CHECK_PACKETS([hv2/vif22-tx.pcap], [vif22.expected])
-
-OVN_CLEANUP([hv1],[hv2])
-
-AT_CLEANUP
-
-# Run ovn-nbctl in daemon mode, change to a backup database and verify that
-# an insert operation is not allowed.
-AT_SETUP([ovn -- can't write to a backup database server instance])
-ovn_start
-on_exit 'kill $(cat ovn-nbctl.pid)'
-export OVN_NB_DAEMON=$(ovn-nbctl --pidfile --detach)
-
-AT_CHECK([ovn-nbctl ls-add sw0])
-as ovn-nb
-AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/sync-status | grep active | wc -l], [0], [1
-])
-ovs-appctl -t ovsdb-server ovsdb-server/set-active-ovsdb-server tcp:192.0.2.2:6641
-ovs-appctl -t ovsdb-server ovsdb-server/connect-active-ovsdb-server
-AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/sync-status | grep -c backup], [0], [1
-])
-AT_CHECK([ovn-nbctl ls-add sw1], [1], [ignore],
-[ovn-nbctl: transaction error: {"details":"insert operation not allowed when database server is in read only mode","error":"not allowed"}
-])
-
-AT_CLEANUP
-
-AT_SETUP([ovn -- controller event])
-AT_KEYWORDS([ovn_controller_event])
-ovn_start
-
-# Create hypervisors hv[12].
-# Add vif1[12] to hv1, vif2[12] to hv2
-# Add all of the vifs to a single logical switch sw0.
-
-net_add n1
-ovn-nbctl ls-add sw0
-for i in 1 2; do
- sim_add hv$i
- as hv$i
- ovs-vsctl add-br br-phys
- ovn_attach n1 br-phys 192.168.0.$i
-
- for j in 1 2; do
- ovn-nbctl lsp-add sw0 sw0-p$i$j -- \
- lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j"
-
- ovs-vsctl -- add-port br-int vif$i$j -- \
- set interface vif$i$j \
- external-ids:iface-id=sw0-p$i$j \
- options:tx_pcap=hv$i/vif$i$j-tx.pcap \
- options:rxq_pcap=hv$i/vif$i$j-rx.pcap \
- ofport-request=$i$j
- done
-done
-
-ovn-nbctl --wait=hv set NB_Global . options:controller_event=true
-ovn-nbctl lb-add lb0 192.168.1.100:80 ""
-ovn-nbctl ls-lb-add sw0 lb0
-uuid_lb=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0)
-
-OVN_POPULATE_ARP
-ovn-nbctl --timeout=3 --wait=hv sync
-ovn-sbctl lflow-list
-as hv1 ovs-ofctl dump-flows br-int
-
-packet="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 &&
- ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.1.100 &&
- tcp && tcp.src==10000 && tcp.dst==80"
-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
-
-ovn-sbctl list controller_event
-uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}')
-AT_CHECK([ovn-sbctl get controller_event $uuid event_type], [0], [dnl
-empty_lb_backends
-])
-AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl
-"192.168.1.100:80"
-])
-AT_CHECK([ovn-sbctl get controller_event $uuid event_info:protocol], [0], [dnl
-tcp
-])
-AT_CHECK_UNQUOTED([ovn-sbctl get controller_event $uuid event_info:load_balancer], [0], [dnl
-"$uuid_lb"
-])
-AT_CHECK([ovn-sbctl get controller_event $uuid seq_num], [0], [dnl
-1
-])
-
-OVN_CLEANUP([hv1], [hv2])
-AT_CLEANUP
-
-AT_SETUP([ovn -- IGMP snoop/querier])
-AT_SKIP_IF([test $HAVE_PYTHON = no])
-ovn_start
-
-# Logical network:
-# Two independent logical switches (sw1 and sw2).
-# sw1:
-# - subnet 10.0.0.0/8
-# - 2 ports bound on hv1 (sw1-p11, sw1-p12)
-# - 2 ports bound on hv2 (sw1-p21, sw1-p22)
-# sw2:
-# - subnet 20.0.0.0/8
-# - 1 port bound on hv1 (sw2-p1)
-# - 1 port bound on hv2 (sw2-p2)
-# - IGMP Querier from 20.0.0.254
-
-reset_pcap_file() {
- local iface=$1
- local pcap_file=$2
- ovs-vsctl -- set Interface $iface options:tx_pcap=dummy-tx.pcap \
-options:rxq_pcap=dummy-rx.pcap
- rm -f ${pcap_file}*.pcap
- ovs-vsctl -- set Interface $iface options:tx_pcap=${pcap_file}-tx.pcap \
-options:rxq_pcap=${pcap_file}-rx.pcap
-}
-
-ip_to_hex() {
- printf "%02x%02x%02x%02x" "$@"
-}
-
-#
-# send_igmp_v3_report INPORT HV ETH_SRC IP_SRC IP_CSUM GROUP REC_TYPE
-# IGMP_CSUM OUTFILE
-#
-# This shell function causes an IGMPv3 report to be received on INPORT of HV.
-# The packet's content has Ethernet destination 01:00:5E:00:00:22 and source
-# ETH_SRC (exactly 12 hex digits). Ethernet type is set to IP.
-# GROUP is the IP multicast group to be joined/to leave (based on REC_TYPE).
-# REC_TYPE == 04: join GROUP
-# REC_TYPE == 03: leave GROUP
-# The packet hexdump is also stored in OUTFILE.
-#
-send_igmp_v3_report() {
- local inport=$1 hv=$2 eth_src=$3 ip_src=$4 ip_chksum=$5 group=$6
- local rec_type=$7 igmp_chksum=$8 outfile=$9
-
- local eth_dst=01005e000016
- local ip_dst=$(ip_to_hex 224 0 0 22)
- local ip_ttl=01
- local ip_ra_opt=94040000
-
- local igmp_type=2200
- local num_rec=00000001
- local aux_dlen=00
- local num_src=0000
-
- local eth=${eth_dst}${eth_src}0800
- local ip=46c0002800004000${ip_ttl}02${ip_chksum}${ip_src}${ip_dst}${ip_ra_opt}
- local igmp=${igmp_type}${igmp_chksum}${num_rec}${rec_type}${aux_dlen}${num_src}${group}
- local packet=${eth}${ip}${igmp}
-
- echo ${packet} >> ${outfile}
- as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet}
-}
-
-#
-# store_igmp_v3_query ETH_SRC IP_SRC IP_CSUM OUTFILE
-#
-# This shell function builds an IGMPv3 general query from ETH_SRC and IP_SRC
-# and stores the hexdump of the packet in OUTFILE.
-#
-store_igmp_v3_query() {
- local eth_src=$1 ip_src=$2 ip_chksum=$3 outfile=$4
-
- local eth_dst=01005e000001
- local ip_dst=$(ip_to_hex 224 0 0 1)
- local ip_ttl=01
- local igmp_type=11
- local max_resp=0a
- local igmp_chksum=eeeb
- local addr=00000000
-
- local eth=${eth_dst}${eth_src}0800
- local ip=4500002000004000${ip_ttl}02${ip_chksum}${ip_src}${ip_dst}
- local igmp=${igmp_type}${max_resp}${igmp_chksum}${addr}000a0000
- local packet=${eth}${ip}${igmp}
-
- echo ${packet} >> ${outfile}
-}
-
-#
-# send_ip_multicast_pkt INPORT HV ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN
-# IP_PROTO DATA OUTFILE
-#
-# This shell function causes an IP multicast packet to be received on INPORT
-# of HV.
-# The hexdump of the packet is stored in OUTFILE.
-#
-send_ip_multicast_pkt() {
- local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ip_src=$5 ip_dst=$6
- local ip_len=$7 ip_chksum=$8 proto=$9 data=${10} outfile=${11}
-
- local ip_ttl=20
-
- local eth=${eth_dst}${eth_src}0800
- local ip=450000${ip_len}95f14000${ip_ttl}${proto}${ip_chksum}${ip_src}${ip_dst}
- local packet=${eth}${ip}${data}
-
- as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet}
- echo ${packet} >> ${outfile}
-}
-
-ovn-nbctl ls-add sw1
-ovn-nbctl ls-add sw2
-
-ovn-nbctl lsp-add sw1 sw1-p11
-ovn-nbctl lsp-add sw1 sw1-p12
-ovn-nbctl lsp-add sw1 sw1-p21
-ovn-nbctl lsp-add sw1 sw1-p22
-ovn-nbctl lsp-add sw2 sw2-p1
-ovn-nbctl lsp-add sw2 sw2-p2
-
-net_add n1
-sim_add hv1
-as hv1
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.1
-ovs-vsctl -- add-port br-int hv1-vif1 -- \
- set interface hv1-vif1 external-ids:iface-id=sw1-p11 \
- options:tx_pcap=hv1/vif1-tx.pcap \
- options:rxq_pcap=hv1/vif1-rx.pcap \
- ofport-request=1
-ovs-vsctl -- add-port br-int hv1-vif2 -- \
- set interface hv1-vif2 external-ids:iface-id=sw1-p12 \
- options:tx_pcap=hv1/vif2-tx.pcap \
- options:rxq_pcap=hv1/vif2-rx.pcap \
- ofport-request=1
-ovs-vsctl -- add-port br-int hv1-vif3 -- \
- set interface hv1-vif3 external-ids:iface-id=sw2-p1 \
- options:tx_pcap=hv1/vif3-tx.pcap \
- options:rxq_pcap=hv1/vif3-rx.pcap \
- ofport-request=1
-
-sim_add hv2
-as hv2
-ovs-vsctl add-br br-phys
-ovn_attach n1 br-phys 192.168.0.2
-ovs-vsctl -- add-port br-int hv2-vif1 -- \
- set interface hv2-vif1 external-ids:iface-id=sw1-p21 \
- options:tx_pcap=hv2/vif1-tx.pcap \
- options:rxq_pcap=hv2/vif1-rx.pcap \
- ofport-request=1
-ovs-vsctl -- add-port br-int hv2-vif2 -- \
- set interface hv2-vif2 external-ids:iface-id=sw1-p22 \
- options:tx_pcap=hv2/vif2-tx.pcap \
- options:rxq_pcap=hv2/vif2-rx.pcap \
- ofport-request=1
-ovs-vsctl -- add-port br-int hv2-vif3 -- \
- set interface hv2-vif3 external-ids:iface-id=sw2-p2 \
- options:tx_pcap=hv2/vif3-tx.pcap \
- options:rxq_pcap=hv2/vif3-rx.pcap \
- ofport-request=1
-
-OVN_POPULATE_ARP
-
-# Enable IGMP snooping on sw1.
-ovn-nbctl set Logical_Switch sw1 other_config:mcast_querier="false"
-ovn-nbctl set Logical_Switch sw1 other_config:mcast_snoop="true"
-
-# No IGMP query should be generated by sw1 (mcast_querier="false").
-truncate -s 0 expected
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected])
-
-ovn-nbctl --wait=hv sync
-
-# Inject IGMP Join for 239.0.1.68 on sw1-p11.
-send_igmp_v3_report hv1-vif1 hv1 \
- 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
- $(ip_to_hex 239 0 1 68) 04 e9b9 \
- /dev/null
-# Inject IGMP Join for 239.0.1.68 on sw1-p21.
-send_igmp_v3_report hv2-vif1 hv2 000000000002 $(ip_to_hex 10 0 0 2) f9f9 \
- $(ip_to_hex 239 0 1 68) 04 e9b9 \
- /dev/null
-
-# Check that the IGMP Group is learned on both hv.
-OVS_WAIT_UNTIL([
- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
- test "${total_entries}" = "2"
-])
-
-# Send traffic and make sure it gets forwarded only on the two ports that
-# joined.
-truncate -s 0 expected
-truncate -s 0 expected_empty
-send_ip_multicast_pkt hv1-vif2 hv1 \
- 000000000001 01005e000144 \
- $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \
- e518e518000a3b3a0000 \
- expected
-
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
-
-# Inject IGMP Leave for 239.0.1.68 on sw1-p11.
-send_igmp_v3_report hv1-vif1 hv1 \
- 000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
- $(ip_to_hex 239 0 1 68) 03 eab9 \
- /dev/null
-
-# Check IGMP_Group table on both HV.
-OVS_WAIT_UNTIL([
- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
- test "${total_entries}" = "1"
-])
-
-# Send traffic traffic and make sure it gets forwarded only on the port that
-# joined.
-as hv1 reset_pcap_file hv1-vif1 hv1/vif1
-as hv2 reset_pcap_file hv2-vif1 hv2/vif1
-truncate -s 0 expected
-truncate -s 0 expected_empty
-send_ip_multicast_pkt hv1-vif2 hv1 \
- 000000000001 01005e000144 \
- $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \
- e518e518000a3b3a0000 \
- expected
-
-OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty])
-OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
-
-# Flush IGMP groups.
-ovn-sbctl ip-multicast-flush sw1
-ovn-nbctl --wait=hv -t 3 sync
-OVS_WAIT_UNTIL([
- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
- test "${total_entries}" = "0"
-])
-
-# Enable IGMP snooping and querier on sw2 and set query interval to minimum.
-ovn-nbctl set Logical_Switch sw2 \
- other_config:mcast_snoop="true" \
- other_config:mcast_querier="true" \
- other_config:mcast_query_interval=1 \
- other_config:mcast_eth_src="00:00:00:00:02:fe" \
- other_config:mcast_ip4_src="20.0.0.254"
-
-# Wait for 1 query interval (1 sec) and check that two queries are generated.
-truncate -s 0 expected
-store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected
-store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected
-
-sleep 1
-OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
-OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected])
-
-OVN_CLEANUP([hv1], [hv2])
-AT_CLEANUP
diff --git a/tests/system-kmod-testsuite.at b/tests/system-kmod-testsuite.at
index 2fe2e8f94..3de0290c0 100644
--- a/tests/system-kmod-testsuite.at
+++ b/tests/system-kmod-testsuite.at
@@ -19,11 +19,9 @@ m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS])
m4_include([tests/ovs-macros.at])
m4_include([tests/ovsdb-macros.at])
m4_include([tests/ofproto-macros.at])
-m4_include([tests/ovn-macros.at])
m4_include([tests/system-common-macros.at])
m4_include([tests/system-kmod-macros.at])
m4_include([tests/system-traffic.at])
m4_include([tests/system-layer3-tunnels.at])
-m4_include([tests/system-ovn.at])
m4_include([tests/system-interface.at])
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
deleted file mode 100644
index f88ad31e4..000000000
--- a/tests/system-ovn.at
+++ /dev/null
@@ -1,1667 +0,0 @@
-AT_BANNER([system-ovn])
-
-AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, SNAT and DNAT])
-AT_KEYWORDS([ovnnat])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# Two LRs - R1 and R2 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and
-# bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected
-# to it. R2 is a gateway router on which we add NAT rules.
-#
-# foo -- R1 -- join - R2 -- alice
-# |
-# bar ----
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Static routes.
-ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
-ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \
-"192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2"
-
-# Add a DNAT rule.
-ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \
- external_ip=30.0.0.2 -- add logical_router R2 nat @nat
-
-# Add a SNAT rule
-ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \
- external_ip=30.0.0.1 -- add logical_router R2 nat @nat
-
-# wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=30.0.0.1)'])
-
-# 'alice1' should be able to ping 'foo1' directly.
-NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 192.168.1.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# North-South DNAT: 'alice1' should also be able to ping 'foo1' via 30.0.0.2
-NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# Check conntrack entries.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic
-# from 30.0.0.1
-NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that SNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=30.0.0.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# Add static routes to handle east-west NAT.
-ovn-nbctl lr-route-add R1 30.0.0.0/24 20.0.0.2
-
-# wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-
-# Flush conntrack entries for easier output parsing of next test.
-AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-
-# East-west DNAT and SNAT: 'bar1' pings 30.0.0.2. 'foo1' receives it.
-NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# As we have a static route that sends all packets with destination
-# 30.0.0.2 to R2, it hits the DNAT rule and converts 30.0.0.2 to 192.168.1.2
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.2.2,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=192.168.2.2,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# As we have a SNAT rule that converts 192.168.2.2 to 30.0.0.1, the source is
-# SNATted and 'foo1' receives it.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.2.2,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=30.0.0.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, easy SNAT])
-AT_KEYWORDS([ovnnat])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# Two LRs - R1 and R2 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) connected
-# to it. R2 has alice (172.16.1.0/24) connected to it.
-# R2 is a gateway router on which we add NAT rules.
-#
-# foo -- R1 -- join - R2 -- alice
-
-ovn-nbctl lr-add R1
-ovn-nbctl lr-add R2 -- set Logical_Router R2 options:chassis=hv1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add join
-
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-
-# Connect foo to R1
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect alice to R2
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Static routes.
-ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
-ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
-
-# Add a SNAT rule
-ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.1.2 \
- external_ip=172.16.1.1 -- add logical_router R2 nat @nat
-
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)'])
-
-# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic
-# from 172.16.1.1
-NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that SNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT])
-AT_KEYWORDS([ovnnat])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and
-# bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected
-# to it. R3 has bob (172.16.1.0/24) connected to it. Note how both alice and
-# bob have the same subnet behind it. We are trying to simulate external
-# network via those 2 switches. In real world the switch ports of these
-# switches will have addresses set as "unknown" to make them learning switches.
-# Or those switches will be "localnet" ones.
-#
-# foo -- R1 -- join - R2 -- alice
-# | |
-# bar ---- - R3 --- bob
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
-ovn-nbctl create Logical_Router name=R3 options:chassis=hv1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add bob
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect bob to R3
-ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24
-ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
- type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Connect R3 to join
-ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24
-ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
- type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
-
-# Install static routes with source ip address as the policy for routing.
-# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3.
-ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2
-ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3
-
-# Static routes.
-ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1
-
-# For gateway routers R2 and R3, set a force SNAT rule.
-ovn-nbctl set logical_router R2 options:dnat_force_snat_ip=20.0.0.2
-ovn-nbctl set logical_router R3 options:dnat_force_snat_ip=20.0.0.3
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \
-"192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2"
-
-# Logical port 'bob1' in switch 'bob'.
-ADD_NAMESPACES(bob1)
-ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \
- "172.16.1.2")
-ovn-nbctl lsp-add bob bob1 \
--- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4"
-
-# Router R2
-# Add a DNAT rule.
-ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \
- external_ip=30.0.0.2 -- add logical_router R2 nat @nat
-
-# Add a SNAT rule
-ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.1.2 \
- external_ip=30.0.0.1 -- add logical_router R2 nat @nat
-
-# Router R3
-# Add a DNAT rule.
-ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=192.168.1.2 \
- external_ip=30.0.0.3 -- add logical_router R3 nat @nat
-
-# Add a SNAT rule
-ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \
- external_ip=30.0.0.4 -- add logical_router R3 nat @nat
-
-# wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=30.0.0.4)'])
-
-# North-South DNAT: 'alice1' should be able to ping 'foo1' via 30.0.0.2
-NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# Check conntrack entries.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.3,dst=30.0.0.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.3,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# But foo1 should receive traffic from 20.0.0.2
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.3,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.2,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# North-South DNAT: 'bob1' should be able to ping 'foo1' via 30.0.0.3
-NS_CHECK_EXEC([bob1], [ping -q -c 3 -i 0.3 -w 2 30.0.0.3 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# Check conntrack entries.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.4,dst=30.0.0.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# But foo1 should receive traffic from 20.0.0.3
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0.3) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.4,dst=192.168.1.2,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=20.0.0.3,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# South-North SNAT: 'bar1' pings 'bob1'. But 'bob1' receives traffic
-# from 30.0.0.4
-NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that SNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.4) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.2.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=30.0.0.4,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic
-# from 30.0.0.1
-NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that SNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=<cleared>,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- load-balancing])
-AT_KEYWORDS([ovnlb])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# 2 logical switches "foo" (192.168.1.0/24) and "bar" (172.16.1.0/24)
-# connected to a router R1.
-# foo has foo1 to act as a client.
-# bar has bar1, bar2, bar3 to act as servers.
-#
-# Loadbalancer VIPs in 30.0.0.0/24 network.
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 172.16.1.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Create logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Create logical ports 'bar1', 'bar2', 'bar3' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "172.16.1.2/24", "f0:00:0f:01:02:03", \
- "172.16.1.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:0f:01:02:03 172.16.1.2"
-
-ADD_NAMESPACES(bar2)
-ADD_VETH(bar2, bar2, br-int, "172.16.1.3/24", "f0:00:0f:01:02:04", \
- "172.16.1.1")
-ovn-nbctl lsp-add bar bar2 \
--- lsp-set-addresses bar2 "f0:00:0f:01:02:04 172.16.1.3"
-
-ADD_NAMESPACES(bar3)
-ADD_VETH(bar3, bar3, br-int, "172.16.1.4/24", "f0:00:0f:01:02:05", \
- "172.16.1.1")
-ovn-nbctl lsp-add bar bar3 \
--- lsp-set-addresses bar3 "f0:00:0f:01:02:05 172.16.1.4"
-
-# Config OVN load-balancer with a VIP.
-uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="172.16.1.2,172.16.1.3,172.16.1.4"`
-ovn-nbctl set logical_switch foo load_balancer=$uuid
-
-# Create another load-balancer with another VIP.
-uuid=`ovn-nbctl create load_balancer vips:30.0.0.3="172.16.1.2,172.16.1.3,172.16.1.4"`
-ovn-nbctl add logical_switch foo load_balancer $uuid
-
-# Config OVN load-balancer with another VIP (this time with ports).
-ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"'
-
-# Wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-grep 'nat(dst=172.16.1.4:80)'])
-
-# Start webservers in 'bar1', 'bar2' and 'bar3'.
-OVS_START_L7([bar1], [http])
-OVS_START_L7([bar2], [http])
-OVS_START_L7([bar3], [http])
-
-dnl Should work with the virtual IP 30.0.0.1 address through NAT
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([foo1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-dnl Should work with the virtual IP 30.0.0.3 address through NAT
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([foo1], [wget 30.0.0.3 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.3) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.3,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-dnl Test load-balancing that includes L4 ports in NAT.
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([foo1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- load-balancing - same subnet.])
-AT_KEYWORDS([ovnlb])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# 1 logical switch "foo" (192.168.1.0/24) connected to router R1.
-# foo has foo1, foo2, foo3, foo4 as logical ports.
-#
-# Loadbalancer VIPs in 30.0.0.0/24 network. Router is needed for default
-# gateway. We will test load-balancing with foo1 as a client and foo2, foo3 and
-# foo4 as servers.
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl ls-add foo
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Create logical port 'foo1', 'foo2', 'foo3' and 'foo4' in switch 'foo'.
-ADD_NAMESPACES(foo1, foo2, foo3, foo4)
-for i in `seq 1 4`; do
- j=`expr $i + 1`
- ADD_VETH(foo$i, foo$i, br-int, "192.168.1.$j/24", "f0:00:00:01:02:0$j", \
- "192.168.1.1")
- ovn-nbctl lsp-add foo foo$i \
- -- lsp-set-addresses foo$i "f0:00:00:01:02:0$j 192.168.1.$j"
-done
-
-# Config OVN load-balancer with a VIP.
-uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.3,192.168.1.4,192.168.1.5"`
-ovn-nbctl set logical_switch foo load_balancer=$uuid
-
-# Config OVN load-balancer with another VIP (this time with ports).
-ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.3:80,192.168.1.4:80,192.168.1.5:80"'
-
-# Wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-grep 'nat(dst=192.168.1.5:80)'])
-
-# Start webservers in 'foo2', 'foo3' and 'foo4'.
-OVS_START_L7([foo2], [http])
-OVS_START_L7([foo3], [http])
-OVS_START_L7([foo4], [http])
-
-dnl Should work with the virtual IP address through NAT
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([foo1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.5,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-dnl Test load-balancing that includes L4 ports in NAT.
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([foo1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.5,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- load balancing in gateway router])
-AT_KEYWORDS([ovnlb])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# Two LRs - R1 and R2 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and
-# bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected
-# to it. R2 is a gateway router on which we add load-balancing rules.
-#
-# foo -- R1 -- join - R2 -- alice
-# |
-# bar ----
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Static routes.
-ovn-nbctl lr-route-add R1 172.16.1.0/24 20.0.0.2
-ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:04", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \
-"192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2"
-
-# Config OVN load-balancer with a VIP.
-uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2"`
-ovn-nbctl set logical_router R2 load_balancer=$uuid
-
-# Config OVN load-balancer with another VIP (this time with ports).
-ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"192.168.1.2:80,192.168.2.2:80"'
-
-# Add SNAT rule to make sure that Load-balancing still works with a SNAT rule.
-ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=192.168.2.2 \
- external_ip=30.0.0.2 -- add logical_router R2 nat @nat
-
-
-# Wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-grep 'nat(dst=192.168.2.2:80)'])
-
-# Start webservers in 'foo1', 'bar1'.
-OVS_START_L7([foo1], [http])
-OVS_START_L7([bar1], [http])
-
-dnl Should work with the virtual IP address through NAT
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([alice1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.2,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-dnl Test load-balancing that includes L4 ports in NAT.
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([alice1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) |
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- multiple gateway routers, load-balancing])
-AT_KEYWORDS([ovnlb])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
-# in 20.0.0.0/24 network. R1 has switchess foo (192.168.1.0/24) and
-# bar (192.168.2.0/24) connected to it. R2 has alice (172.16.1.0/24) connected
-# to it. R3 has bob (172.16.1.0/24) connected to it. Note how both alice and
-# bob have the same subnet behind it. We are trying to simulate external
-# network via those 2 switches. In real world the switch ports of these
-# switches will have addresses set as "unknown" to make them learning switches.
-# Or those switches will be "localnet" ones.
-#
-# foo -- R1 -- join - R2 -- alice
-# | |
-# bar ---- - R3 --- bob
-
-ovn-nbctl create Logical_Router name=R1
-ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
-ovn-nbctl create Logical_Router name=R3 options:chassis=hv1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-ovn-nbctl ls-add bob
-ovn-nbctl ls-add join
-
-# Connect foo to R1
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
-
-# Connect bar to R1
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
-
-# Connect alice to R2
-ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 172.16.1.1/24
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
-
-# Connect bob to R3
-ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 172.16.1.2/24
-ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
- type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
-
-# Connect R1 to join
-ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 20.0.0.1/24
-ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
- type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
-
-# Connect R2 to join
-ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 20.0.0.2/24
-ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
- type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
-
-# Connect R3 to join
-ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 20.0.0.3/24
-ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
- type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
-
-# Install static routes with source ip address as the policy for routing.
-# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3.
-ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.1.0/24 20.0.0.2
-ovn-nbctl --policy="src-ip" lr-route-add R1 192.168.2.0/24 20.0.0.3
-
-# Static routes.
-ovn-nbctl lr-route-add R2 192.168.0.0/16 20.0.0.1
-ovn-nbctl lr-route-add R3 192.168.0.0/16 20.0.0.1
-
-# For gateway routers R2 and R3, set a force SNAT rule.
-ovn-nbctl set logical_router R2 options:lb_force_snat_ip=20.0.0.2
-ovn-nbctl set logical_router R3 options:lb_force_snat_ip=20.0.0.3
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.3/24", "f0:00:00:01:02:04", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.3"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:05", \
-"192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:05 192.168.2.2"
-
-# Logical port 'bob1' in switch 'bob'.
-ADD_NAMESPACES(bob1)
-ADD_VETH(bob1, bob1, br-int, "172.16.1.4/24", "f0:00:00:01:02:06", \
- "172.16.1.2")
-ovn-nbctl lsp-add bob bob1 \
--- lsp-set-addresses bob1 "f0:00:00:01:02:06 172.16.1.4"
-
-# Config OVN load-balancer with a VIP.
-uuid=`ovn-nbctl create load_balancer vips:30.0.0.1="192.168.1.2,192.168.2.2"`
-ovn-nbctl set logical_router R2 load_balancer=$uuid
-ovn-nbctl set logical_router R3 load_balancer=$uuid
-
-# Wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-grep 'nat(dst=192.168.2.2)'])
-
-# Start webservers in 'foo1', 'bar1'.
-OVS_START_L7([foo1], [http])
-OVS_START_L7([bar1], [http])
-
-dnl Should work with the virtual IP address through NAT
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([alice1], [wget 30.0.0.1 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) |
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.3,dst=30.0.0.1,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.3,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-dnl Force SNAT should have worked.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(20.0.0) |
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=20.0.0.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.3,dst=192.168.2.2,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=20.0.0.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- load balancing in router with gateway router port])
-AT_KEYWORDS([ovnlb])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# One LR R1 with switches foo (192.168.1.0/24), bar (192.168.2.0/24),
-# and alice (172.16.1.0/24) connected to it. The port between R1 and
-# alice is the router gateway port where the R1 LB rules are applied.
-#
-# foo -- R1 -- bar
-# |
-# alice ----
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \
- -- set Logical_Router_Port alice options:redirect-chassis=hv1
-
-# Connect foo to R1
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect bar to R1
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar \
- -- lsp-set-addresses rp-bar router
-
-# Connect alice to R1
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'foo2' in switch 'foo'.
-ADD_NAMESPACES(foo2)
-ADD_VETH(foo2, foo2, br-int, "192.168.1.3/24", "f0:00:00:01:02:06", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo2 \
--- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:04", \
- "192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:05", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
-
-# Config OVN load-balancer with a VIP.
-uuid=`ovn-nbctl create load_balancer vips:172.16.1.10="192.168.1.2,192.168.2.2"`
-ovn-nbctl set logical_router R1 load_balancer=$uuid
-
-# Config OVN load-balancer with another VIP (this time with ports).
-ovn-nbctl set load_balancer $uuid vips:'"172.16.1.11:8000"'='"192.168.1.2:80,192.168.2.2:80"'
-
-# Wait for ovn-controller to catch up.
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl -O OpenFlow13 dump-groups br-int | \
-grep 'nat(dst=192.168.2.2:80)'])
-
-# Start webservers in 'foo1', 'bar1'.
-OVS_START_L7([foo1], [http])
-OVS_START_L7([bar1], [http])
-
-dnl Should work with the virtual IP address through NAT
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([alice1], [wget 172.16.1.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.10) |
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.2,dst=172.16.1.10,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-dnl Test load-balancing that includes L4 ports in NAT.
-for i in `seq 1 20`; do
- echo Request $i
- NS_CHECK_EXEC([alice1], [wget 172.16.1.11:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
-done
-
-dnl Each server should have at least one connection.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.11) |
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=<cleared>,dport=<cleared>),reply=(src=192.168.1.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-tcp,orig=(src=172.16.1.2,dst=172.16.1.11,sport=<cleared>,dport=<cleared>),reply=(src=192.168.2.2,dst=172.16.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- DNAT and SNAT on distributed router - N/S])
-AT_KEYWORDS([ovnnat])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# One LR R1 with switches foo (192.168.1.0/24), bar (192.168.2.0/24),
-# and alice (172.16.1.0/24) connected to it. The port between R1 and
-# alice is the router gateway port where the R1 NAT rules are applied.
-#
-# foo -- R1 -- alice
-# |
-# bar ----
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \
- -- set Logical_Router_Port alice options:redirect-chassis=hv1
-
-# Connect foo to R1
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect bar to R1
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar \
- -- lsp-set-addresses rp-bar router
-
-# Connect alice to R1
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'foo2' in switch 'foo'.
-ADD_NAMESPACES(foo2)
-ADD_VETH(foo2, foo2, br-int, "192.168.1.3/24", "f0:00:00:01:02:06", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo2 \
--- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:04", \
- "192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:05", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
-
-# Add DNAT rules
-AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04])
-AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05])
-
-# Add a SNAT rule
-AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
-
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)'])
-
-# North-South DNAT: 'alice1' pings 'foo1' using 172.16.1.3.
-NS_CHECK_EXEC([alice1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that DNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.3) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.2,dst=172.16.1.3,id=<cleared>,type=8,code=0),reply=(src=192.168.1.2,dst=172.16.1.2,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-# South-North SNAT: 'foo2' pings 'alice1'. But 'alice1' receives traffic
-# from 172.16.1.4
-NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that SNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.1.3,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-
-# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic
-# from 172.16.1.1
-NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that SNAT indeed happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- DNAT and SNAT on distributed router - E/W])
-AT_KEYWORDS([ovnnat])
-
-CHECK_CONNTRACK()
-CHECK_CONNTRACK_NAT()
-ovn_start
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# One LR R1 with switches foo (192.168.1.0/24), bar (192.168.2.0/24),
-# and alice (172.16.1.0/24) connected to it. The port between R1 and
-# alice is the router gateway port where the R1 NAT rules are applied.
-#
-# foo -- R1 -- alice
-# |
-# bar ----
-
-ovn-nbctl lr-add R1
-
-ovn-nbctl ls-add foo
-ovn-nbctl ls-add bar
-ovn-nbctl ls-add alice
-
-ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 192.168.1.1/24
-ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 192.168.2.1/24
-ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 172.16.1.1/24 \
- -- set Logical_Router_Port alice options:redirect-chassis=hv1
-
-# Connect foo to R1
-ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
- type=router options:router-port=foo \
- -- lsp-set-addresses rp-foo router
-
-# Connect bar to R1
-ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
- type=router options:router-port=bar \
- -- lsp-set-addresses rp-bar router
-
-# Connect alice to R1
-ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
- type=router options:router-port=alice \
- -- lsp-set-addresses rp-alice router
-
-# Logical port 'foo1' in switch 'foo'.
-ADD_NAMESPACES(foo1)
-ADD_VETH(foo1, foo1, br-int, "192.168.1.2/24", "f0:00:00:01:02:03", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo1 \
--- lsp-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
-
-# Logical port 'foo2' in switch 'foo'.
-ADD_NAMESPACES(foo2)
-ADD_VETH(foo2, foo2, br-int, "192.168.1.3/24", "f0:00:00:01:02:06", \
- "192.168.1.1")
-ovn-nbctl lsp-add foo foo2 \
--- lsp-set-addresses foo2 "f0:00:00:01:02:06 192.168.1.3"
-
-# Logical port 'bar1' in switch 'bar'.
-ADD_NAMESPACES(bar1)
-ADD_VETH(bar1, bar1, br-int, "192.168.2.2/24", "f0:00:00:01:02:04", \
- "192.168.2.1")
-ovn-nbctl lsp-add bar bar1 \
--- lsp-set-addresses bar1 "f0:00:00:01:02:04 192.168.2.2"
-
-# Logical port 'alice1' in switch 'alice'.
-ADD_NAMESPACES(alice1)
-ADD_VETH(alice1, alice1, br-int, "172.16.1.2/24", "f0:00:00:01:02:05", \
- "172.16.1.1")
-ovn-nbctl lsp-add alice alice1 \
--- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
-
-# Add DNAT rules
-AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04])
-AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.2.2 bar1 00:00:02:02:03:05])
-
-# Add a SNAT rule
-AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
-
-ovn-nbctl --wait=hv sync
-OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)'])
-
-echo "------ hv dump ------"
-ovs-ofctl show br-int
-ovs-ofctl dump-flows br-int
-echo "---------------------"
-
-# East-West No NAT: 'foo1' pings 'bar1' using 192.168.2.2.
-NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that no NAT happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | wc -l], [0], [0
-])
-
-# East-West No NAT: 'foo2' pings 'bar1' using 192.168.2.2.
-NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 192.168.2.2 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that no NAT happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | wc -l], [0], [0
-])
-
-# East-West No NAT: 'bar1' pings 'foo2' using 192.168.1.3.
-NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 192.168.1.3 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# We verify that no NAT happened via 'dump-conntrack' command.
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(192.168.2.2) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | wc -l], [0], [0
-])
-
-# East-West NAT: 'foo1' pings 'bar1' using 172.16.1.4.
-NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# Check conntrack entries. First SNAT of 'foo1' address happens.
-# Then DNAT of 'bar1' address happens (listed first below).
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.4) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-icmp,orig=(src=192.168.1.2,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-AT_CHECK([ovs-appctl dpctl/flush-conntrack])
-
-# East-West NAT: 'foo2' pings 'bar1' using 172.16.1.4.
-NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 172.16.1.4 | FORMAT_PING], \
-[0], [dnl
-3 packets transmitted, 3 received, 0% packet loss, time 0ms
-])
-
-# Check conntrack entries. First SNAT of 'foo2' address happens.
-# Then DNAT of 'bar1' address happens (listed first below).
-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(172.16.1.1) | \
-sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
-icmp,orig=(src=172.16.1.1,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=192.168.2.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-icmp,orig=(src=192.168.1.3,dst=172.16.1.4,id=<cleared>,type=8,code=0),reply=(src=172.16.1.4,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
-
-AT_SETUP([ovn -- 2 LSs IGMP])
-AT_KEYWORDS([ovnigmp])
-
-ovn_start
-
-OVS_TRAFFIC_VSWITCHD_START()
-ADD_BR([br-int])
-
-# Set external-ids in br-int needed for ovn-controller
-ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=hv1 \
- -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
-
-# Start ovn-controller
-start_daemon ovn-controller
-
-# Logical network:
-# Two independent logical switches (sw1 and sw2).
-# sw1:
-# - subnet 10.0.0.0/8
-# - 2 ports (sw1-p1 - sw1-p2)
-# sw2:
-# - subnet 20.0.0.0/8
-# - 2 port (sw2-p1 - sw2-p2)
-# - IGMP Querier from 20.0.0.254
-
-ovn-nbctl ls-add sw1
-ovn-nbctl ls-add sw2
-
-for i in `seq 1 2`
-do
- ADD_NAMESPACES(sw1-p$i)
- ADD_VETH(sw1-p$i, sw1-p$i, br-int, "10.0.0.$i/24", "00:00:00:00:01:0$i", \
- "10.0.0.254")
- ovn-nbctl lsp-add sw1 sw1-p$i \
- -- lsp-set-addresses sw1-p$i "00:00:00:00:01:0$i 10.0.0.$i"
-done
-
-for i in `seq 1 2`
-do
- ADD_NAMESPACES(sw2-p$i)
- ADD_VETH(sw2-p$i, sw2-p$i, br-int, "20.0.0.$i/24", "00:00:00:00:02:0$i", \
- "20.0.0.254")
- ovn-nbctl lsp-add sw2 sw2-p$i \
- -- lsp-set-addresses sw2-p$i "00:00:00:00:02:0$i 20.0.0.$i"
-done
-
-# Enable IGMP snooping on sw1.
-ovn-nbctl set Logical_Switch sw1 other_config:mcast_querier="false"
-ovn-nbctl set Logical_Switch sw1 other_config:mcast_snoop="true"
-
-# Inject IGMP Join for 239.0.1.68 on sw1-p1.
-NS_CHECK_EXEC([sw1-p1], [ip addr add dev sw1-p1 239.0.1.68/32 autojoin], [0])
-
-# Inject IGMP Join for 239.0.1.68 on sw1-p2
-NS_CHECK_EXEC([sw1-p2], [ip addr add dev sw1-p2 239.0.1.68/32 autojoin], [0])
-
-# Check that the IGMP Group is learned.
-OVS_WAIT_UNTIL([
- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
- ports=`ovn-sbctl find IGMP_Group | grep ports | cut -f 2 -d ":" | wc -w`
- test "${total_entries}" = "1"
- test "${ports}" = "2"
-])
-
-# Inject IGMP Leave for 239.0.1.68 on sw1-p2.
-NS_CHECK_EXEC([sw1-p2], [ip addr del dev sw1-p2 239.0.1.68/32], [0])
-
-# Check that only one port is left in the group.
-OVS_WAIT_UNTIL([
- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
- ports=`ovn-sbctl find IGMP_Group | grep ports | cut -f 2 -d ":" | wc -w`
- test "${total_entries}" = "1"
- test "${ports}" = "1"
-])
-
-# Flush IGMP groups.
-ovn-sbctl ip-multicast-flush sw1
-ovn-nbctl --wait=hv -t 3 sync
-OVS_WAIT_UNTIL([
- total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
- test "${total_entries}" = "0"
-])
-
-# Enable IGMP snooping and querier on sw2 and set query interval to minimum.
-ovn-nbctl set Logical_Switch sw2 \
- other_config:mcast_snoop="true" \
- other_config:mcast_querier="true" \
- other_config:mcast_query_interval=1 \
- other_config:mcast_eth_src="00:00:00:00:02:fe" \
- other_config:mcast_ip4_src="20.0.0.254"
-
-# Check that queries are generated.
-NS_CHECK_EXEC([sw2-p1], [tcpdump -n -c 2 -i sw2-p1 igmp > sw2-p1.pcap &])
-
-OVS_WAIT_UNTIL([
- total_queries=`cat sw2-p1.pcap | grep "igmp query" | wc -l`
- test "${total_queries}" = "2"
-])
-
-OVS_APP_EXIT_AND_WAIT([ovn-controller])
-
-as ovn-sb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as ovn-nb
-OVS_APP_EXIT_AND_WAIT([ovsdb-server])
-
-as northd
-OVS_APP_EXIT_AND_WAIT([ovn-northd])
-
-as
-OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
-/connection dropped.*/d"])
-AT_CLEANUP
diff --git a/tests/system-userspace-testsuite.at b/tests/system-userspace-testsuite.at
index 4af36bef0..b40da9579 100644
--- a/tests/system-userspace-testsuite.at
+++ b/tests/system-userspace-testsuite.at
@@ -19,12 +19,10 @@ m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS])
m4_include([tests/ovs-macros.at])
m4_include([tests/ovsdb-macros.at])
m4_include([tests/ofproto-macros.at])
-m4_include([tests/ovn-macros.at])
m4_include([tests/system-userspace-macros.at])
m4_include([tests/system-common-macros.at])
m4_include([tests/system-traffic.at])
m4_include([tests/system-layer3-tunnels.at])
-m4_include([tests/system-ovn.at])
m4_include([tests/system-interface.at])
m4_include([tests/system-userspace-packet-type-aware.at])
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
deleted file mode 100644
index 0b9e8246e..000000000
--- a/tests/test-ovn.c
+++ /dev/null
@@ -1,1584 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include <errno.h>
-#include <getopt.h>
-#include <sys/wait.h>
-
-#include "command-line.h"
-#include "dp-packet.h"
-#include "fatal-signal.h"
-#include "flow.h"
-#include "openvswitch/dynamic-string.h"
-#include "openvswitch/match.h"
-#include "openvswitch/ofp-actions.h"
-#include "openvswitch/ofpbuf.h"
-#include "openvswitch/vlog.h"
-#include "ovn/actions.h"
-#include "ovn/expr.h"
-#include "ovn/lex.h"
-#include "ovn/logical-fields.h"
-#include "ovn/lib/ovn-l7.h"
-#include "ovn/lib/extend-table.h"
-#include "ovs-thread.h"
-#include "ovstest.h"
-#include "openvswitch/shash.h"
-#include "simap.h"
-#include "util.h"
-
-/* --relops: Bitmap of the relational operators to test, in exhaustive test. */
-static unsigned int test_relops;
-
-/* --nvars: Number of numeric variables to test, in exhaustive test. */
-static int test_nvars = 2;
-
-/* --svars: Number of string variables to test, in exhaustive test. */
-static int test_svars = 2;
-
-/* --bits: Number of bits per variable, in exhaustive test. */
-static int test_bits = 3;
-
-/* --operation: The operation to test, in exhaustive test. */
-static enum { OP_CONVERT, OP_SIMPLIFY, OP_NORMALIZE, OP_FLOW } operation
- = OP_FLOW;
-
-/* --parallel: Number of parallel processes to use in test. */
-static int test_parallel = 1;
-
-/* -m, --more: Message verbosity */
-static int verbosity;
-
-static void
-compare_token(const struct lex_token *a, const struct lex_token *b)
-{
- if (a->type != b->type) {
- fprintf(stderr, "type differs: %d -> %d\n", a->type, b->type);
- return;
- }
-
- if (!((a->s && b->s && !strcmp(a->s, b->s))
- || (!a->s && !b->s))) {
- fprintf(stderr, "string differs: %s -> %s\n",
- a->s ? a->s : "(null)",
- b->s ? b->s : "(null)");
- return;
- }
-
- if (a->type == LEX_T_INTEGER || a->type == LEX_T_MASKED_INTEGER) {
- if (memcmp(&a->value, &b->value, sizeof a->value)) {
- fprintf(stderr, "value differs\n");
- return;
- }
-
- if (a->type == LEX_T_MASKED_INTEGER
- && memcmp(&a->mask, &b->mask, sizeof a->mask)) {
- fprintf(stderr, "mask differs\n");
- return;
- }
-
- if (a->format != b->format
- && !(a->format == LEX_F_HEXADECIMAL
- && b->format == LEX_F_DECIMAL
- && a->value.integer == 0)) {
- fprintf(stderr, "format differs: %d -> %d\n",
- a->format, b->format);
- }
- }
-}
-
-static void
-test_lex(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- struct ds input;
- struct ds output;
-
- ds_init(&input);
- ds_init(&output);
- while (!ds_get_test_line(&input, stdin)) {
- struct lexer lexer;
-
- lexer_init(&lexer, ds_cstr(&input));
- ds_clear(&output);
- while (lexer_get(&lexer) != LEX_T_END) {
- size_t len = output.length;
- lex_token_format(&lexer.token, &output);
-
- /* Check that the formatted version can really be parsed back
- * losslessly. */
- if (lexer.token.type != LEX_T_ERROR) {
- const char *s = ds_cstr(&output) + len;
- struct lexer l2;
-
- lexer_init(&l2, s);
- lexer_get(&l2);
- compare_token(&lexer.token, &l2.token);
- lexer_destroy(&l2);
- }
- ds_put_char(&output, ' ');
- }
- lexer_destroy(&lexer);
-
- ds_chomp(&output, ' ');
- puts(ds_cstr(&output));
- }
- ds_destroy(&input);
- ds_destroy(&output);
-}
-
-static void
-create_symtab(struct shash *symtab)
-{
- ovn_init_symtab(symtab);
-
- /* For negative testing. */
- expr_symtab_add_field(symtab, "bad_prereq", MFF_XREG0, "xyzzy", false);
- expr_symtab_add_field(symtab, "self_recurse", MFF_XREG0,
- "self_recurse != 0", false);
- expr_symtab_add_field(symtab, "mutual_recurse_1", MFF_XREG0,
- "mutual_recurse_2 != 0", false);
- expr_symtab_add_field(symtab, "mutual_recurse_2", MFF_XREG0,
- "mutual_recurse_1 != 0", false);
- expr_symtab_add_string(symtab, "big_string", MFF_XREG0, NULL);
-}
-
-static void
-create_gen_opts(struct hmap *dhcp_opts, struct hmap *dhcpv6_opts,
- struct hmap *nd_ra_opts,
- struct controller_event_options *event_opts)
-{
- hmap_init(dhcp_opts);
- dhcp_opt_add(dhcp_opts, "offerip", 0, "ipv4");
- dhcp_opt_add(dhcp_opts, "netmask", 1, "ipv4");
- dhcp_opt_add(dhcp_opts, "router", 3, "ipv4");
- dhcp_opt_add(dhcp_opts, "dns_server", 6, "ipv4");
- dhcp_opt_add(dhcp_opts, "log_server", 7, "ipv4");
- dhcp_opt_add(dhcp_opts, "lpr_server", 9, "ipv4");
- dhcp_opt_add(dhcp_opts, "domain_name", 15, "str");
- dhcp_opt_add(dhcp_opts, "swap_server", 16, "ipv4");
- dhcp_opt_add(dhcp_opts, "policy_filter", 21, "ipv4");
- dhcp_opt_add(dhcp_opts, "router_solicitation", 32, "ipv4");
- dhcp_opt_add(dhcp_opts, "nis_server", 41, "ipv4");
- dhcp_opt_add(dhcp_opts, "ntp_server", 42, "ipv4");
- dhcp_opt_add(dhcp_opts, "server_id", 54, "ipv4");
- dhcp_opt_add(dhcp_opts, "tftp_server", 66, "ipv4");
- dhcp_opt_add(dhcp_opts, "classless_static_route", 121, "static_routes");
- dhcp_opt_add(dhcp_opts, "ip_forward_enable", 19, "bool");
- dhcp_opt_add(dhcp_opts, "router_discovery", 31, "bool");
- dhcp_opt_add(dhcp_opts, "ethernet_encap", 36, "bool");
- dhcp_opt_add(dhcp_opts, "default_ttl", 23, "uint8");
- dhcp_opt_add(dhcp_opts, "tcp_ttl", 37, "uint8");
- dhcp_opt_add(dhcp_opts, "mtu", 26, "uint16");
- dhcp_opt_add(dhcp_opts, "lease_time", 51, "uint32");
- dhcp_opt_add(dhcp_opts, "wpad", 252, "str");
- dhcp_opt_add(dhcp_opts, "bootfile_name", 67, "str");
- dhcp_opt_add(dhcp_opts, "path_prefix", 210, "str");
- dhcp_opt_add(dhcp_opts, "tftp_server_address", 150, "ipv4");
-
- /* DHCPv6 options. */
- hmap_init(dhcpv6_opts);
- dhcp_opt_add(dhcpv6_opts, "server_id", 2, "mac");
- dhcp_opt_add(dhcpv6_opts, "ia_addr", 5, "ipv6");
- dhcp_opt_add(dhcpv6_opts, "dns_server", 23, "ipv6");
- dhcp_opt_add(dhcpv6_opts, "domain_search", 24, "str");
-
- /* IPv6 ND RA options. */
- hmap_init(nd_ra_opts);
- nd_ra_opts_init(nd_ra_opts);
-
- /* OVN controller events options. */
- controller_event_opts_init(event_opts);
-}
-
-static void
-create_addr_sets(struct shash *addr_sets)
-{
- shash_init(addr_sets);
-
- static const char *const addrs1[] = {
- "10.0.0.1", "10.0.0.2", "10.0.0.3",
- };
- static const char *const addrs2[] = {
- "::1", "::2", "::3",
- };
- static const char *const addrs3[] = {
- "00:00:00:00:00:01", "00:00:00:00:00:02", "00:00:00:00:00:03",
- };
- static const char *const addrs4[] = { NULL };
-
- expr_const_sets_add(addr_sets, "set1", addrs1, 3, true);
- expr_const_sets_add(addr_sets, "set2", addrs2, 3, true);
- expr_const_sets_add(addr_sets, "set3", addrs3, 3, true);
- expr_const_sets_add(addr_sets, "set4", addrs4, 0, true);
-}
-
-static void
-create_port_groups(struct shash *port_groups)
-{
- shash_init(port_groups);
-
- static const char *const pg1[] = {
- "lsp1", "lsp2", "lsp3",
- };
- static const char *const pg2[] = { NULL };
-
- expr_const_sets_add(port_groups, "pg1", pg1, 3, false);
- expr_const_sets_add(port_groups, "pg_empty", pg2, 0, false);
-}
-
-static bool
-lookup_port_cb(const void *ports_, const char *port_name, unsigned int *portp)
-{
- const struct simap *ports = ports_;
- const struct simap_node *node = simap_find(ports, port_name);
- if (!node) {
- return false;
- }
- *portp = node->data;
- return true;
-}
-
-static bool
-is_chassis_resident_cb(const void *ports_, const char *port_name)
-{
- const struct simap *ports = ports_;
- const struct simap_node *node = simap_find(ports, port_name);
- if (node) {
- return true;
- }
- return false;
-}
-
-static void
-test_parse_expr__(int steps)
-{
- struct shash symtab;
- struct shash addr_sets;
- struct shash port_groups;
- struct simap ports;
- struct ds input;
-
- create_symtab(&symtab);
- create_addr_sets(&addr_sets);
- create_port_groups(&port_groups);
-
- simap_init(&ports);
- simap_put(&ports, "eth0", 5);
- simap_put(&ports, "eth1", 6);
- simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
- simap_put(&ports, "lsp1", 0x11);
- simap_put(&ports, "lsp2", 0x12);
- simap_put(&ports, "lsp3", 0x13);
-
- ds_init(&input);
- while (!ds_get_test_line(&input, stdin)) {
- struct expr *expr;
- char *error;
-
- expr = expr_parse_string(ds_cstr(&input), &symtab, &addr_sets,
- &port_groups, NULL, &error);
- if (!error && steps > 0) {
- expr = expr_annotate(expr, &symtab, &error);
- }
- if (!error) {
- if (steps > 1) {
- expr = expr_simplify(expr, is_chassis_resident_cb, &ports);
- }
- if (steps > 2) {
- expr = expr_normalize(expr);
- ovs_assert(expr_is_normalized(expr));
- }
- }
- if (!error) {
- if (steps > 3) {
- struct hmap matches;
-
- expr_to_matches(expr, lookup_port_cb, &ports, &matches);
- expr_matches_print(&matches, stdout);
- expr_matches_destroy(&matches);
- } else {
- struct ds output = DS_EMPTY_INITIALIZER;
- expr_format(expr, &output);
- puts(ds_cstr(&output));
- ds_destroy(&output);
- }
- } else {
- puts(error);
- free(error);
- }
- expr_destroy(expr);
- }
- ds_destroy(&input);
-
- simap_destroy(&ports);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
- expr_const_sets_destroy(&addr_sets);
- shash_destroy(&addr_sets);
- expr_const_sets_destroy(&port_groups);
- shash_destroy(&port_groups);
-}
-
-static void
-test_parse_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- test_parse_expr__(0);
-}
-
-static void
-test_annotate_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- test_parse_expr__(1);
-}
-
-static void
-test_simplify_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- test_parse_expr__(2);
-}
-
-static void
-test_normalize_expr(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- test_parse_expr__(3);
-}
-
-static void
-test_expr_to_flows(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- test_parse_expr__(4);
-}
-
-/* Print the symbol table. */
-
-static void
-test_dump_symtab(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- struct shash symtab;
- create_symtab(&symtab);
-
- const struct shash_node **nodes = shash_sort(&symtab);
- for (size_t i = 0; i < shash_count(&symtab); i++) {
- const struct expr_symbol *symbol = nodes[i]->data;
- struct ds s = DS_EMPTY_INITIALIZER;
- expr_symbol_format(symbol, &s);
- puts(ds_cstr(&s));
- ds_destroy(&s);
- }
-
- free(nodes);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
-}
-
-/* Evaluate an expression. */
-
-static bool
-lookup_atoi_cb(const void *aux OVS_UNUSED, const char *port_name,
- unsigned int *portp)
-{
- *portp = atoi(port_name);
- return true;
-}
-
-static void
-test_evaluate_expr(struct ovs_cmdl_context *ctx)
-{
- struct shash symtab;
- struct ds input;
-
- ovn_init_symtab(&symtab);
-
- struct flow uflow;
- char *error = expr_parse_microflow(ctx->argv[1], &symtab, NULL, NULL,
- lookup_atoi_cb, NULL, &uflow);
- if (error) {
- ovs_fatal(0, "%s", error);
- }
-
- ds_init(&input);
- while (!ds_get_test_line(&input, stdin)) {
- struct expr *expr;
-
- expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, NULL, NULL,
- &error);
- if (!error) {
- expr = expr_annotate(expr, &symtab, &error);
- }
- if (!error) {
- printf("%d\n", expr_evaluate(expr, &uflow, lookup_atoi_cb, NULL));
- } else {
- puts(error);
- free(error);
- }
- expr_destroy(expr);
- }
- ds_destroy(&input);
-
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
-}
-
-/* Compositions.
- *
- * The "compositions" of a positive integer N are all of the ways that one can
- * add up positive integers to sum to N. For example, the compositions of 3
- * are 3, 2+1, 1+2, and 1+1+1.
- *
- * We use compositions to find all the ways to break up N terms of a Boolean
- * expression into subexpressions. Suppose we want to generate all expressions
- * with 3 terms. The compositions of 3 (ignoring 3 itself) provide the
- * possibilities (x && x) || x, x || (x && x), and x || x || x. (Of course one
- * can exchange && for || in each case.) One must recursively compose the
- * sub-expressions whose values are 3 or greater; that is what the "tree shape"
- * concept later covers.
- *
- * To iterate through all compositions of, e.g., 5:
- *
- * unsigned int state;
- * int s[5];
- * int n;
- *
- * for (n = first_composition(ARRAY_SIZE(s), &state, s); n > 0;
- * n = next_composition(&state, s, n)) {
- * // Do something with composition 's' with 'n' elements.
- * }
- *
- * Algorithm from D. E. Knuth, _The Art of Computer Programming, Vol. 4A:
- * Combinatorial Algorithms, Part 1_, section 7.2.1.1, answer to exercise
- * 12(a).
- */
-
-/* Begins iteration through the compositions of 'n'. Initializes 's' to the
- * number of elements in the first composition of 'n' and returns that number
- * of elements. The first composition in fact is always 'n' itself, so the
- * return value will be 1.
- *
- * Initializes '*state' to some internal state information. The caller must
- * maintain this state (and 's') for use by next_composition().
- *
- * 's' must have room for at least 'n' elements. */
-static int
-first_composition(int n, unsigned int *state, int s[])
-{
- *state = 0;
- s[0] = n;
- return 1;
-}
-
-/* Advances 's', with 'sn' elements, to the next composition and returns the
- * number of elements in this new composition, or 0 if no compositions are
- * left. 'state' is the same internal state passed to first_composition(). */
-static int
-next_composition(unsigned int *state, int s[], int sn)
-{
- int j = sn - 1;
- if (++*state & 1) {
- if (s[j] > 1) {
- s[j]--;
- s[j + 1] = 1;
- j++;
- } else {
- j--;
- s[j]++;
- }
- } else {
- if (s[j - 1] > 1) {
- s[j - 1]--;
- s[j + 1] = s[j];
- s[j] = 1;
- j++;
- } else {
- j--;
- if (!j) {
- return 0;
- }
- s[j] = s[j + 1];
- s[j - 1]++;
- }
- }
- return j + 1;
-}
-
-static void
-test_composition(struct ovs_cmdl_context *ctx)
-{
- int n = atoi(ctx->argv[1]);
- unsigned int state;
- int s[50];
-
- for (int sn = first_composition(n, &state, s); sn;
- sn = next_composition(&state, s, sn)) {
- for (int i = 0; i < sn; i++) {
- printf("%d%c", s[i], i == sn - 1 ? '\n' : ' ');
- }
- }
-}
-
-/* Tree shapes.
- *
- * This code generates all possible Boolean expressions with a specified number
- * of terms N (equivalent to the number of external nodes in a tree).
- *
- * See test_tree_shape() for a simple example. */
-
-/* An array of these structures describes the shape of a tree.
- *
- * A single element of struct tree_shape describes a single node in the tree.
- * The node has 'sn' direct children. From left to right, for i in 0...sn-1,
- * s[i] is 1 if the child is a leaf node, otherwise the child is a subtree and
- * s[i] is the number of leaf nodes within that subtree. In the latter case,
- * the subtree is described by another struct tree_shape within the enclosing
- * array. The tree_shapes are ordered in the array in in-order.
- */
-struct tree_shape {
- unsigned int state;
- int s[50];
- int sn;
-};
-
-static int
-init_tree_shape__(struct tree_shape ts[], int n)
-{
- if (n <= 2) {
- return 0;
- }
-
- int n_tses = 1;
- /* Skip the first composition intentionally. */
- ts->sn = first_composition(n, &ts->state, ts->s);
- ts->sn = next_composition(&ts->state, ts->s, ts->sn);
- for (int i = 0; i < ts->sn; i++) {
- n_tses += init_tree_shape__(&ts[n_tses], ts->s[i]);
- }
- return n_tses;
-}
-
-/* Initializes 'ts[]' as the first in the set of all of possible shapes of
- * trees with 'n' leaves. Returns the number of "struct tree_shape"s in the
- * first tree shape. */
-static int
-init_tree_shape(struct tree_shape ts[], int n)
-{
- switch (n) {
- case 1:
- ts->sn = 1;
- ts->s[0] = 1;
- return 1;
- case 2:
- ts->sn = 2;
- ts->s[0] = 1;
- ts->s[1] = 1;
- return 1;
- default:
- return init_tree_shape__(ts, n);
- }
-}
-
-/* Advances 'ts', which currently has 'n_tses' elements, to the next possible
- * tree shape with the number of leaves passed to init_tree_shape(). Returns
- * the number of "struct tree_shape"s in the next shape, or 0 if all tree
- * shapes have been visited. */
-static int
-next_tree_shape(struct tree_shape ts[], int n_tses)
-{
- if (n_tses == 1 && ts->sn == 2 && ts->s[0] == 1 && ts->s[1] == 1) {
- return 0;
- }
- while (n_tses > 0) {
- struct tree_shape *p = &ts[n_tses - 1];
- p->sn = p->sn > 1 ? next_composition(&p->state, p->s, p->sn) : 0;
- if (p->sn) {
- for (int i = 0; i < p->sn; i++) {
- n_tses += init_tree_shape__(&ts[n_tses], p->s[i]);
- }
- break;
- }
- n_tses--;
- }
- return n_tses;
-}
-
-static void
-print_tree_shape(const struct tree_shape ts[], int n_tses)
-{
- for (int i = 0; i < n_tses; i++) {
- if (i) {
- printf(", ");
- }
- for (int j = 0; j < ts[i].sn; j++) {
- int k = ts[i].s[j];
- if (k > 9) {
- printf("(%d)", k);
- } else {
- printf("%d", k);
- }
- }
- }
-}
-
-static void
-test_tree_shape(struct ovs_cmdl_context *ctx)
-{
- int n = atoi(ctx->argv[1]);
- struct tree_shape ts[50];
- int n_tses;
-
- for (n_tses = init_tree_shape(ts, n); n_tses;
- n_tses = next_tree_shape(ts, n_tses)) {
- print_tree_shape(ts, n_tses);
- putchar('\n');
- }
-}
-
-/* Iteration through all possible terminal expressions (e.g. EXPR_T_CMP and
- * EXPR_T_BOOLEAN expressions).
- *
- * Given a tree shape, this allows the code to try all possible ways to plug in
- * terms.
- *
- * Example use:
- *
- * struct expr terminal;
- * const struct expr_symbol *vars = ...;
- * int n_vars = ...;
- * int n_bits = ...;
- *
- * init_terminal(&terminal, vars[0]);
- * do {
- * // Something with 'terminal'.
- * } while (next_terminal(&terminal, vars, n_vars, n_bits));
- */
-
-/* Sets 'expr' to the first possible terminal expression. 'var' should be the
- * first variable in the ones to be tested. */
-static void
-init_terminal(struct expr *expr, int phase,
- const struct expr_symbol *nvars[], int n_nvars,
- const struct expr_symbol *svars[], int n_svars)
-{
- if (phase < 1 && n_nvars) {
- expr->type = EXPR_T_CMP;
- expr->cmp.symbol = nvars[0];
- expr->cmp.relop = rightmost_1bit_idx(test_relops);
- memset(&expr->cmp.value, 0, sizeof expr->cmp.value);
- memset(&expr->cmp.mask, 0, sizeof expr->cmp.mask);
- expr->cmp.value.integer = htonll(0);
- expr->cmp.mask.integer = htonll(0);
- return;
- }
-
- if (phase < 2 && n_svars) {
- expr->type = EXPR_T_CMP;
- expr->cmp.symbol = svars[0];
- expr->cmp.relop = EXPR_R_EQ;
- expr->cmp.string = xstrdup("0");
- return;
- }
-
- expr->type = EXPR_T_BOOLEAN;
- expr->boolean = false;
-}
-
-/* Returns 'x' with the rightmost contiguous string of 1s changed to 0s,
- * e.g. 01011100 => 01000000. See H. S. Warren, Jr., _Hacker's Delight_, 2nd
- * ed., section 2-1. */
-static unsigned int
-turn_off_rightmost_1s(unsigned int x)
-{
- return ((x & -x) + x) & x;
-}
-
-static const struct expr_symbol *
-next_var(const struct expr_symbol *symbol,
- const struct expr_symbol *vars[], int n_vars)
-{
- for (int i = 0; i < n_vars; i++) {
- if (symbol == vars[i]) {
- return i + 1 >= n_vars ? NULL : vars[i + 1];
- }
- }
- OVS_NOT_REACHED();
-}
-
-static enum expr_relop
-next_relop(enum expr_relop relop)
-{
- unsigned int remaining_relops = test_relops & ~((1u << (relop + 1)) - 1);
- return (remaining_relops
- ? rightmost_1bit_idx(remaining_relops)
- : rightmost_1bit_idx(test_relops));
-}
-
-/* Advances 'expr' to the next possible terminal expression within the 'n_vars'
- * variables of 'n_bits' bits each in 'vars[]'. */
-static bool
-next_terminal(struct expr *expr,
- const struct expr_symbol *nvars[], int n_nvars, int n_bits,
- const struct expr_symbol *svars[], int n_svars)
-{
- if (expr->type == EXPR_T_BOOLEAN) {
- if (expr->boolean) {
- return false;
- } else {
- expr->boolean = true;
- return true;
- }
- }
-
- if (!expr->cmp.symbol->width) {
- int next_value = atoi(expr->cmp.string) + 1;
- free(expr->cmp.string);
- if (next_value > 1) {
- expr->cmp.symbol = next_var(expr->cmp.symbol, svars, n_svars);
- if (!expr->cmp.symbol) {
- init_terminal(expr, 2, nvars, n_nvars, svars, n_svars);
- return true;
- }
- next_value = 0;
- }
- expr->cmp.string = xasprintf("%d", next_value);
- return true;
- }
-
- unsigned int next;
-
- next = (ntohll(expr->cmp.value.integer)
- + (ntohll(expr->cmp.mask.integer) << n_bits));
- for (;;) {
- next++;
- unsigned m = next >> n_bits;
- unsigned v = next & ((1u << n_bits) - 1);
- if (next >= (1u << (2 * n_bits))) {
- enum expr_relop old_relop = expr->cmp.relop;
- expr->cmp.relop = next_relop(old_relop);
- if (expr->cmp.relop <= old_relop) {
- expr->cmp.symbol = next_var(expr->cmp.symbol, nvars, n_nvars);
- if (!expr->cmp.symbol) {
- init_terminal(expr, 1, nvars, n_nvars, svars, n_svars);
- return true;
- }
- }
- next = UINT_MAX;
- } else if (v & ~m) {
- /* Skip: 1-bits in value correspond to 0-bits in mask. */
- } else if ((!m || turn_off_rightmost_1s(m))
- && (expr->cmp.relop != EXPR_R_EQ &&
- expr->cmp.relop != EXPR_R_NE)) {
- /* Skip: can't have discontiguous or all-0 mask for > >= < <=. */
- } else {
- expr->cmp.value.integer = htonll(v);
- expr->cmp.mask.integer = htonll(m);
- return true;
- }
- }
-}
-
-static struct expr *
-make_terminal(struct expr ***terminalp)
-{
- struct expr *e = expr_create_boolean(true);
- **terminalp = e;
- (*terminalp)++;
- return e;
-}
-
-static struct expr *
-build_simple_tree(enum expr_type type, int n, struct expr ***terminalp)
-{
- if (n == 2) {
- struct expr *e = expr_create_andor(type);
- for (int i = 0; i < 2; i++) {
- struct expr *sub = make_terminal(terminalp);
- ovs_list_push_back(&e->andor, &sub->node);
- }
- return e;
- } else if (n == 1) {
- return make_terminal(terminalp);
- } else {
- OVS_NOT_REACHED();
- }
-}
-
-static struct expr *
-build_tree_shape(enum expr_type type, const struct tree_shape **tsp,
- struct expr ***terminalp)
-{
- const struct tree_shape *ts = *tsp;
- (*tsp)++;
-
- struct expr *e = expr_create_andor(type);
- enum expr_type t = type == EXPR_T_AND ? EXPR_T_OR : EXPR_T_AND;
- for (int i = 0; i < ts->sn; i++) {
- struct expr *sub = (ts->s[i] > 2
- ? build_tree_shape(t, tsp, terminalp)
- : build_simple_tree(t, ts->s[i], terminalp));
- ovs_list_push_back(&e->andor, &sub->node);
- }
- return e;
-}
-
-struct test_rule {
- struct cls_rule cr;
-};
-
-static void
-free_rule(struct test_rule *test_rule)
-{
- cls_rule_destroy(&test_rule->cr);
- free(test_rule);
-}
-
-static bool
-tree_shape_is_chassis_resident_cb(const void *c_aux OVS_UNUSED,
- const char *port_name OVS_UNUSED)
-{
- return true;
-}
-
-static int
-test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
- struct expr *terminals[], int n_terminals,
- const struct expr_symbol *nvars[], int n_nvars,
- int n_bits,
- const struct expr_symbol *svars[], int n_svars)
-{
- int n_tested = 0;
-
- const unsigned int var_mask = (1u << n_bits) - 1;
- for (int i = 0; i < n_terminals; i++) {
- init_terminal(terminals[i], 0, nvars, n_nvars, svars, n_svars);
- }
-
- struct ds s = DS_EMPTY_INITIALIZER;
- struct flow f;
- memset(&f, 0, sizeof f);
- for (;;) {
- for (int i = n_terminals - 1; ; i--) {
- if (!i) {
- ds_destroy(&s);
- return n_tested;
- }
- if (next_terminal(terminals[i], nvars, n_nvars, n_bits,
- svars, n_svars)) {
- break;
- }
- init_terminal(terminals[i], 0, nvars, n_nvars, svars, n_svars);
- }
- ovs_assert(expr_honors_invariants(expr));
-
- n_tested++;
-
- struct expr *modified;
- if (operation == OP_CONVERT) {
- ds_clear(&s);
- expr_format(expr, &s);
-
- char *error;
- modified = expr_parse_string(ds_cstr(&s), symtab, NULL,
- NULL, NULL, &error);
- if (error) {
- fprintf(stderr, "%s fails to parse (%s)\n",
- ds_cstr(&s), error);
- exit(EXIT_FAILURE);
- }
- } else if (operation >= OP_SIMPLIFY) {
- modified = expr_simplify(expr_clone(expr),
- tree_shape_is_chassis_resident_cb,
- NULL);
- ovs_assert(expr_honors_invariants(modified));
-
- if (operation >= OP_NORMALIZE) {
- modified = expr_normalize(modified);
- ovs_assert(expr_honors_invariants(modified));
- ovs_assert(expr_is_normalized(modified));
- }
- }
-
- struct hmap matches;
- struct classifier cls;
- if (operation >= OP_FLOW) {
- struct expr_match *m;
- struct test_rule *test_rule;
-
- expr_to_matches(modified, lookup_atoi_cb, NULL, &matches);
-
- classifier_init(&cls, NULL);
- HMAP_FOR_EACH (m, hmap_node, &matches) {
- test_rule = xmalloc(sizeof *test_rule);
- cls_rule_init(&test_rule->cr, &m->match, 0);
- classifier_insert(&cls, &test_rule->cr, OVS_VERSION_MIN,
- m->conjunctions, m->n);
- }
- }
- for (int subst = 0; subst < 1 << (n_bits * n_nvars + n_svars);
- subst++) {
- for (int i = 0; i < n_nvars; i++) {
- f.regs[i] = (subst >> (i * n_bits)) & var_mask;
- }
- for (int i = 0; i < n_svars; i++) {
- f.regs[n_nvars + i] = ((subst >> (n_nvars * n_bits + i))
- & 1);
- }
-
- bool expected = expr_evaluate(expr, &f, lookup_atoi_cb, NULL);
- bool actual = expr_evaluate(modified, &f, lookup_atoi_cb, NULL);
- if (actual != expected) {
- struct ds expr_s, modified_s;
-
- ds_init(&expr_s);
- expr_format(expr, &expr_s);
-
- ds_init(&modified_s);
- expr_format(modified, &modified_s);
-
- fprintf(stderr,
- "%s evaluates to %d, but %s evaluates to %d, for",
- ds_cstr(&expr_s), expected,
- ds_cstr(&modified_s), actual);
- for (int i = 0; i < n_nvars; i++) {
- if (i > 0) {
- fputs(",", stderr);
- }
- fprintf(stderr, " n%d = 0x%x", i,
- (subst >> (n_bits * i)) & var_mask);
- }
- for (int i = 0; i < n_svars; i++) {
- fprintf(stderr, ", s%d = \"%d\"", i,
- (subst >> (n_bits * n_nvars + i)) & 1);
- }
- putc('\n', stderr);
- exit(EXIT_FAILURE);
- }
-
- if (operation >= OP_FLOW) {
- bool found = classifier_lookup(&cls, OVS_VERSION_MIN,
- &f, NULL) != NULL;
- if (expected != found) {
- struct ds expr_s, modified_s;
-
- ds_init(&expr_s);
- expr_format(expr, &expr_s);
-
- ds_init(&modified_s);
- expr_format(modified, &modified_s);
-
- fprintf(stderr,
- "%s and %s evaluate to %d, for",
- ds_cstr(&expr_s), ds_cstr(&modified_s), expected);
- for (int i = 0; i < n_nvars; i++) {
- if (i > 0) {
- fputs(",", stderr);
- }
- fprintf(stderr, " n%d = 0x%x", i,
- (subst >> (n_bits * i)) & var_mask);
- }
- for (int i = 0; i < n_svars; i++) {
- fprintf(stderr, ", s%d = \"%d\"", i,
- (subst >> (n_bits * n_nvars + i)) & 1);
- }
- fputs(".\n", stderr);
-
- fprintf(stderr, "Converted to classifier:\n");
- expr_matches_print(&matches, stderr);
- fprintf(stderr,
- "However, %s flow was found in the classifier.\n",
- found ? "a" : "no");
- exit(EXIT_FAILURE);
- }
- }
- }
- if (operation >= OP_FLOW) {
- struct test_rule *test_rule;
-
- CLS_FOR_EACH (test_rule, cr, &cls) {
- classifier_remove_assert(&cls, &test_rule->cr);
- ovsrcu_postpone(free_rule, test_rule);
- }
- classifier_destroy(&cls);
- ovsrcu_quiesce();
-
- expr_matches_destroy(&matches);
- }
- expr_destroy(modified);
- }
-}
-
-#ifndef _WIN32
-static void
-wait_pid(pid_t *pids, int *n)
-{
- int status;
- pid_t pid;
-
- pid = waitpid(-1, &status, 0);
- if (pid < 0) {
- ovs_fatal(errno, "waitpid failed");
- } else if (WIFEXITED(status)) {
- if (WEXITSTATUS(status)) {
- exit(WEXITSTATUS(status));
- }
- } else if (WIFSIGNALED(status)) {
- raise(WTERMSIG(status));
- exit(1);
- } else {
- OVS_NOT_REACHED();
- }
-
- for (int i = 0; i < *n; i++) {
- if (pids[i] == pid) {
- pids[i] = pids[--*n];
- return;
- }
- }
- ovs_fatal(0, "waitpid returned unknown child");
-}
-#endif
-
-static void
-test_exhaustive(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- int n_terminals = atoi(ctx->argv[1]);
- struct tree_shape ts[50];
- int n_tses;
-
- struct shash symtab;
- const struct expr_symbol *nvars[4];
- const struct expr_symbol *svars[4];
-
- ovs_assert(test_nvars <= ARRAY_SIZE(nvars));
- ovs_assert(test_svars <= ARRAY_SIZE(svars));
- ovs_assert(test_nvars + test_svars <= FLOW_N_REGS);
-
- shash_init(&symtab);
- for (int i = 0; i < test_nvars; i++) {
- char *name = xasprintf("n%d", i);
- nvars[i] = expr_symtab_add_field(&symtab, name, MFF_REG0 + i, NULL,
- false);
- free(name);
- }
- for (int i = 0; i < test_svars; i++) {
- char *name = xasprintf("s%d", i);
- svars[i] = expr_symtab_add_string(&symtab, name,
- MFF_REG0 + test_nvars + i, NULL);
- free(name);
- }
-
-#ifndef _WIN32
- pid_t *children = xmalloc(test_parallel * sizeof *children);
- int n_children = 0;
-#endif
-
- int n_tested = 0;
- for (int i = 0; i < 2; i++) {
- enum expr_type base_type = i ? EXPR_T_OR : EXPR_T_AND;
-
- for (n_tses = init_tree_shape(ts, n_terminals); n_tses;
- n_tses = next_tree_shape(ts, n_tses)) {
- const struct tree_shape *tsp = ts;
- struct expr *terminals[50];
- struct expr **terminalp = terminals;
- struct expr *expr = build_tree_shape(base_type, &tsp, &terminalp);
- ovs_assert(terminalp == &terminals[n_terminals]);
-
- if (verbosity > 0) {
- print_tree_shape(ts, n_tses);
- printf(": ");
- struct ds s = DS_EMPTY_INITIALIZER;
- expr_format(expr, &s);
- puts(ds_cstr(&s));
- ds_destroy(&s);
- }
-
-#ifndef _WIN32
- if (test_parallel > 1) {
- pid_t pid = xfork();
- if (!pid) {
- test_tree_shape_exhaustively(expr, &symtab,
- terminals, n_terminals,
- nvars, test_nvars, test_bits,
- svars, test_svars);
- expr_destroy(expr);
- exit(0);
- } else {
- if (n_children >= test_parallel) {
- wait_pid(children, &n_children);
- }
- children[n_children++] = pid;
- }
- } else
-#endif
- {
- n_tested += test_tree_shape_exhaustively(
- expr, &symtab, terminals, n_terminals,
- nvars, test_nvars, test_bits,
- svars, test_svars);
- }
- expr_destroy(expr);
- }
- }
-#ifndef _WIN32
- while (n_children > 0) {
- wait_pid(children, &n_children);
- }
- free(children);
-#endif
-
- printf("Tested ");
- switch (operation) {
- case OP_CONVERT:
- printf("converting");
- break;
- case OP_SIMPLIFY:
- printf("simplifying");
- break;
- case OP_NORMALIZE:
- printf("normalizing");
- break;
- case OP_FLOW:
- printf("converting to flows");
- break;
- }
- if (n_tested) {
- printf(" %d expressions of %d terminals", n_tested, n_terminals);
- } else {
- printf(" all %d-terminal expressions", n_terminals);
- }
- if (test_nvars || test_svars) {
- printf(" with");
- if (test_nvars) {
- printf(" %d numeric vars (each %d bits) in terms of operators",
- test_nvars, test_bits);
- for (unsigned int relops = test_relops; relops;
- relops = zero_rightmost_1bit(relops)) {
- enum expr_relop r = rightmost_1bit_idx(relops);
- printf(" %s", expr_relop_to_string(r));
- }
- }
- if (test_nvars && test_svars) {
- printf (" and");
- }
- if (test_svars) {
- printf(" %d string vars", test_svars);
- }
- } else {
- printf(" in terms of Boolean constants only");
- }
- printf(".\n");
-
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
-}
-
-static void
-test_expr_to_packets(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- struct shash symtab;
- struct ds input;
-
- create_symtab(&symtab);
-
- ds_init(&input);
- while (!ds_get_test_line(&input, stdin)) {
- struct flow uflow;
- char *error = expr_parse_microflow(ds_cstr(&input), &symtab, NULL,
- NULL, lookup_atoi_cb, NULL, &uflow);
- if (error) {
- puts(error);
- free(error);
- continue;
- }
-
- uint64_t packet_stub[128 / 8];
- struct dp_packet packet;
- dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
- flow_compose(&packet, &uflow, NULL, 64);
-
- struct ds output = DS_EMPTY_INITIALIZER;
- const uint8_t *buf = dp_packet_data(&packet);
- for (int i = 0; i < dp_packet_size(&packet); i++) {
- uint8_t val = buf[i];
- ds_put_format(&output, "%02"PRIx8, val);
- }
- puts(ds_cstr(&output));
- ds_destroy(&output);
-
- dp_packet_uninit(&packet);
- }
- ds_destroy(&input);
-
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
-}
-
-/* Actions. */
-
-static void
-test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
-{
- struct shash symtab;
- struct hmap dhcp_opts;
- struct hmap dhcpv6_opts;
- struct hmap nd_ra_opts;
- struct controller_event_options event_opts;
- struct simap ports;
- struct ds input;
- bool ok = true;
-
- create_symtab(&symtab);
- create_gen_opts(&dhcp_opts, &dhcpv6_opts, &nd_ra_opts, &event_opts);
-
- /* Initialize group ids. */
- struct ovn_extend_table group_table;
- ovn_extend_table_init(&group_table);
-
- /* Initialize meter ids for QoS. */
- struct ovn_extend_table meter_table;
- ovn_extend_table_init(&meter_table);
-
- simap_init(&ports);
- simap_put(&ports, "eth0", 5);
- simap_put(&ports, "eth1", 6);
- simap_put(&ports, "LOCAL", ofp_to_u16(OFPP_LOCAL));
-
- ds_init(&input);
- while (!ds_get_test_line(&input, stdin)) {
- struct ofpbuf ovnacts;
- struct expr *prereqs;
- char *error;
-
- puts(ds_cstr(&input));
-
- ofpbuf_init(&ovnacts, 0);
-
- const struct ovnact_parse_params pp = {
- .symtab = &symtab,
- .dhcp_opts = &dhcp_opts,
- .dhcpv6_opts = &dhcpv6_opts,
- .nd_ra_opts = &nd_ra_opts,
- .controller_event_opts = &event_opts,
- .n_tables = 24,
- .cur_ltable = 10,
- };
- error = ovnacts_parse_string(ds_cstr(&input), &pp, &ovnacts, &prereqs);
- if (!error) {
- /* Convert the parsed representation back to a string and print it,
- * if it's different from the input. */
- struct ds ovnacts_s = DS_EMPTY_INITIALIZER;
- ovnacts_format(ovnacts.data, ovnacts.size, &ovnacts_s);
- if (strcmp(ds_cstr(&input), ds_cstr(&ovnacts_s))) {
- printf(" formats as %s\n", ds_cstr(&ovnacts_s));
- }
-
- /* Encode the actions into OpenFlow and print. */
- const struct ovnact_encode_params ep = {
- .lookup_port = lookup_port_cb,
- .aux = &ports,
- .is_switch = true,
- .group_table = &group_table,
- .meter_table = &meter_table,
-
- .pipeline = OVNACT_P_INGRESS,
- .ingress_ptable = 8,
- .egress_ptable = 40,
- .output_ptable = 64,
- .mac_bind_ptable = 65,
- };
- struct ofpbuf ofpacts;
- ofpbuf_init(&ofpacts, 0);
- ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
- struct ds ofpacts_s = DS_EMPTY_INITIALIZER;
- struct ofpact_format_params fp = { .s = &ofpacts_s };
- ofpacts_format(ofpacts.data, ofpacts.size, &fp);
- printf(" encodes as %s\n", ds_cstr(&ofpacts_s));
- ds_destroy(&ofpacts_s);
- ofpbuf_uninit(&ofpacts);
-
- /* Print prerequisites if any. */
- if (prereqs) {
- struct ds prereqs_s = DS_EMPTY_INITIALIZER;
- expr_format(prereqs, &prereqs_s);
- printf(" has prereqs %s\n", ds_cstr(&prereqs_s));
- ds_destroy(&prereqs_s);
- }
-
- /* Now re-parse and re-format the string to verify that it's
- * round-trippable. */
- struct ofpbuf ovnacts2;
- struct expr *prereqs2;
- ofpbuf_init(&ovnacts2, 0);
- error = ovnacts_parse_string(ds_cstr(&ovnacts_s), &pp, &ovnacts2,
- &prereqs2);
- if (!error) {
- struct ds ovnacts2_s = DS_EMPTY_INITIALIZER;
- ovnacts_format(ovnacts2.data, ovnacts2.size, &ovnacts2_s);
- if (strcmp(ds_cstr(&ovnacts_s), ds_cstr(&ovnacts2_s))) {
- printf(" bad reformat: %s\n", ds_cstr(&ovnacts2_s));
- ok = false;
- }
- ds_destroy(&ovnacts2_s);
- } else {
- printf(" reparse error: %s\n", error);
- free(error);
- ok = false;
- }
- expr_destroy(prereqs2);
-
- ovnacts_free(ovnacts2.data, ovnacts2.size);
- ofpbuf_uninit(&ovnacts2);
- ds_destroy(&ovnacts_s);
- } else {
- printf(" %s\n", error);
- free(error);
- }
-
- expr_destroy(prereqs);
- ovnacts_free(ovnacts.data, ovnacts.size);
- ofpbuf_uninit(&ovnacts);
- }
- ds_destroy(&input);
-
- simap_destroy(&ports);
- expr_symtab_destroy(&symtab);
- shash_destroy(&symtab);
- dhcp_opts_destroy(&dhcp_opts);
- dhcp_opts_destroy(&dhcpv6_opts);
- nd_ra_opts_destroy(&nd_ra_opts);
- controller_event_opts_destroy(&event_opts);
- ovn_extend_table_destroy(&group_table);
- ovn_extend_table_destroy(&meter_table);
- exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
-}
-
-static unsigned int
-parse_relops(const char *s)
-{
- unsigned int relops = 0;
- struct lexer lexer;
-
- lexer_init(&lexer, s);
- lexer_get(&lexer);
- do {
- enum expr_relop relop;
-
- if (expr_relop_from_token(lexer.token.type, &relop)) {
- relops |= 1u << relop;
- lexer_get(&lexer);
- } else {
- ovs_fatal(0, "%s: relational operator expected at `%.*s'",
- s, (int) (lexer.input - lexer.start), lexer.start);
- }
- lexer_match(&lexer, LEX_T_COMMA);
- } while (lexer.token.type != LEX_T_END);
- lexer_destroy(&lexer);
-
- return relops;
-}
-
-static void
-usage(void)
-{
- printf("\
-%s: OVN test utility\n\
-usage: test-ovn %s [OPTIONS] COMMAND [ARG...]\n\
-\n\
-lex\n\
- Lexically analyzes OVN input from stdin and print them back on stdout.\n\
-\n\
-parse-expr\n\
-annotate-expr\n\
-simplify-expr\n\
-normalize-expr\n\
-expr-to-flows\n\
- Parses OVN expressions from stdin and prints them back on stdout after\n\
- differing degrees of analysis. Available fields are based on packet\n\
- headers.\n\
-\n\
-expr-to-packets\n\
- Parses OVN expressions from stdin and prints out matching packets in\n\
- hexadecimal on stdout.\n\
-\n\
-evaluate-expr MICROFLOW\n\
- Parses OVN expressions from stdin and evaluates them against the flow\n\
- specified in MICROFLOW, which must be an expression that constrains\n\
- the packet, e.g. \"ip4 && tcp.src == 80\" for a TCP packet with source\n\
- port 80, and prints the results on stdout, either 1 for true or 0 for\n\
- false. Use quoted integers, e.g. \"123\", for string fields.\n\
-\n\
- Example: for MICROFLOW of \"ip4 && tcp.src == 80\", \"eth.type == 0x800\"\n\
- evaluates to true, \"udp\" evaluates to false, and \"udp || tcp\"\n\
- evaluates to true.\n\
-\n\
-composition N\n\
- Prints all the compositions of N on stdout.\n\
-\n\
-tree-shape N\n\
- Prints all the tree shapes with N terminals on stdout.\n\
-\n\
-exhaustive N\n\
- Tests that all possible Boolean expressions with N terminals are properly\n\
- simplified, normalized, and converted to flows. Available options:\n\
- Overall options:\n\
- --operation=OPERATION Operation to test, one of: convert, simplify,\n\
- normalize, flow. Default: flow. 'normalize' includes 'simplify',\n\
- 'flow' includes 'simplify' and 'normalize'.\n\
- --parallel=N Number of processes to use in parallel, default 1.\n\
- Numeric vars:\n\
- --nvars=N Number of numeric vars to test, in range 0...4, default 2.\n\
- --bits=N Number of bits per variable, in range 1...3, default 3.\n\
- --relops=OPERATORS Test only the specified Boolean operators.\n\
- OPERATORS may include == != < <= > >=, space or\n\
- comma separated. Default is all operators.\n\
- String vars:\n\
- --svars=N Number of string vars to test, in range 0...4, default 2.\n\
-\n\
-parse-actions\n\
- Parses OVN actions from stdin and prints the equivalent OpenFlow actions\n\
- on stdout.\n\
-",
- program_name, program_name);
- exit(EXIT_SUCCESS);
-}
-
-static void
-test_ovn_main(int argc, char *argv[])
-{
- enum {
- OPT_RELOPS = UCHAR_MAX + 1,
- OPT_NVARS,
- OPT_SVARS,
- OPT_BITS,
- OPT_OPERATION,
- OPT_PARALLEL
- };
- static const struct option long_options[] = {
- {"relops", required_argument, NULL, OPT_RELOPS},
- {"nvars", required_argument, NULL, OPT_NVARS},
- {"svars", required_argument, NULL, OPT_SVARS},
- {"bits", required_argument, NULL, OPT_BITS},
- {"operation", required_argument, NULL, OPT_OPERATION},
- {"parallel", required_argument, NULL, OPT_PARALLEL},
- {"more", no_argument, NULL, 'm'},
- {"help", no_argument, NULL, 'h'},
- {NULL, 0, NULL, 0},
- };
- char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
-
- set_program_name(argv[0]);
-
- test_relops = parse_relops("== != < <= > >=");
- for (;;) {
- int option_index = 0;
- int c = getopt_long (argc, argv, short_options, long_options,
- &option_index);
-
- if (c == -1) {
- break;
- }
- switch (c) {
- case OPT_RELOPS:
- test_relops = parse_relops(optarg);
- break;
-
- case OPT_NVARS:
- test_nvars = atoi(optarg);
- if (test_nvars < 0 || test_nvars > 4) {
- ovs_fatal(0, "number of numeric variables must be "
- "between 0 and 4");
- }
- break;
-
- case OPT_SVARS:
- test_svars = atoi(optarg);
- if (test_svars < 0 || test_svars > 4) {
- ovs_fatal(0, "number of string variables must be "
- "between 0 and 4");
- }
- break;
-
- case OPT_BITS:
- test_bits = atoi(optarg);
- if (test_bits < 1 || test_bits > 3) {
- ovs_fatal(0, "number of bits must be between 1 and 3");
- }
- break;
-
- case OPT_OPERATION:
- if (!strcmp(optarg, "convert")) {
- operation = OP_CONVERT;
- } else if (!strcmp(optarg, "simplify")) {
- operation = OP_SIMPLIFY;
- } else if (!strcmp(optarg, "normalize")) {
- operation = OP_NORMALIZE;
- } else if (!strcmp(optarg, "flow")) {
- operation = OP_FLOW;
- } else {
- ovs_fatal(0, "%s: unknown operation", optarg);
- }
- break;
-
- case OPT_PARALLEL:
- test_parallel = atoi(optarg);
- break;
-
- case 'm':
- verbosity++;
- break;
-
- case 'h':
- usage();
- /* fall through */
-
- case '?':
- exit(1);
-
- default:
- abort();
- }
- }
- free(short_options);
-
- static const struct ovs_cmdl_command commands[] = {
- /* Lexer. */
- {"lex", NULL, 0, 0, test_lex, OVS_RO},
-
- /* Symbol table. */
- {"dump-symtab", NULL, 0, 0, test_dump_symtab, OVS_RO},
-
- /* Expressions. */
- {"parse-expr", NULL, 0, 0, test_parse_expr, OVS_RO},
- {"annotate-expr", NULL, 0, 0, test_annotate_expr, OVS_RO},
- {"simplify-expr", NULL, 0, 0, test_simplify_expr, OVS_RO},
- {"normalize-expr", NULL, 0, 0, test_normalize_expr, OVS_RO},
- {"expr-to-flows", NULL, 0, 0, test_expr_to_flows, OVS_RO},
- {"evaluate-expr", NULL, 1, 1, test_evaluate_expr, OVS_RO},
- {"composition", NULL, 1, 1, test_composition, OVS_RO},
- {"tree-shape", NULL, 1, 1, test_tree_shape, OVS_RO},
- {"exhaustive", NULL, 1, 1, test_exhaustive, OVS_RO},
- {"expr-to-packets", NULL, 0, 0, test_expr_to_packets, OVS_RO},
-
- /* Actions. */
- {"parse-actions", NULL, 0, 0, test_parse_actions, OVS_RO},
-
- {NULL, NULL, 0, 0, NULL, OVS_RO},
- };
- struct ovs_cmdl_context ctx;
- ctx.argc = argc - optind;
- ctx.argv = argv + optind;
- ovs_cmdl_run_command(&ctx, commands);
-}
-
-OVSTEST_REGISTER("test-ovn", test_ovn_main);
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 4d5e81618..e75912300 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -19,7 +19,6 @@ m4_ifdef([AT_COLOR_TESTS], [AT_COLOR_TESTS])
m4_include([tests/ovs-macros.at])
m4_include([tests/ovsdb-macros.at])
m4_include([tests/ofproto-macros.at])
-m4_include([tests/ovn-macros.at])
m4_include([tests/completion.at])
m4_include([tests/checkpatch.at])
@@ -74,13 +73,6 @@ m4_include([tests/rstp.at])
m4_include([tests/vlog.at])
m4_include([tests/vtep-ctl.at])
m4_include([tests/auto-attach.at])
-m4_include([tests/ovn.at])
-m4_include([tests/ovn-northd.at])
-m4_include([tests/ovn-nbctl.at])
-m4_include([tests/ovn-sbctl.at])
-m4_include([tests/ovn-controller.at])
-m4_include([tests/ovn-controller-vtep.at])
m4_include([tests/mcast-snooping.at])
m4_include([tests/packet-type-aware.at])
m4_include([tests/nsh.at])
-m4_include([tests/ovn-performance.at])
diff --git a/tutorial/automake.mk b/tutorial/automake.mk
index b7ea10c98..0f6b0fff5 100644
--- a/tutorial/automake.mk
+++ b/tutorial/automake.mk
@@ -5,8 +5,7 @@ EXTRA_DIST += \
tutorial/t-stage1 \
tutorial/t-stage2 \
tutorial/t-stage3 \
- tutorial/t-stage4 \
- tutorial/ovn-setup.sh
+ tutorial/t-stage4
sandbox: all
cd $(srcdir)/tutorial && MAKE=$(MAKE) HAVE_OPENSSL=$(HAVE_OPENSSL) \
./ovs-sandbox -b $(abs_builddir) $(SANDBOXFLAGS)
diff --git a/tutorial/ovn-setup.sh b/tutorial/ovn-setup.sh
deleted file mode 100755
index 969b2330f..000000000
--- a/tutorial/ovn-setup.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-
-# Create the first logical switch with one port
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 sw0-port1
-ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 192.168.0.2"
-
-# Create the second logical switch with one port
-ovn-nbctl ls-add sw1
-ovn-nbctl lsp-add sw1 sw1-port1
-ovn-nbctl lsp-set-addresses sw1-port1 "50:54:00:00:00:03 11.0.0.2"
-
-# Create a logical router and attach both logical switches
-ovn-nbctl lr-add lr0
-ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:ff:01 192.168.0.1/24
-ovn-nbctl lsp-add sw0 lrp0-attachment
-ovn-nbctl lsp-set-type lrp0-attachment router
-ovn-nbctl lsp-set-addresses lrp0-attachment 00:00:00:00:ff:01
-ovn-nbctl lsp-set-options lrp0-attachment router-port=lrp0
-ovn-nbctl lrp-add lr0 lrp1 00:00:00:00:ff:02 11.0.0.1/24
-ovn-nbctl lsp-add sw1 lrp1-attachment
-ovn-nbctl lsp-set-type lrp1-attachment router
-ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02
-ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1
-
-ovs-vsctl add-port br-int p1 -- \
- set Interface p1 external_ids:iface-id=sw0-port1
-ovs-vsctl add-port br-int p2 -- \
- set Interface p2 external_ids:iface-id=sw1-port1
-
-# View a summary of the configuration
-printf "\n=== ovn-nbctl show ===\n\n"
-ovn-nbctl show
-printf "\n=== ovn-nbctl show with wait hv ===\n\n"
-ovn-nbctl --wait=hv show
-printf "\n=== ovn-sbctl show ===\n\n"
-ovn-sbctl show
diff --git a/tutorial/ovs-sandbox b/tutorial/ovs-sandbox
index 601d0381f..09e9773ce 100755
--- a/tutorial/ovs-sandbox
+++ b/tutorial/ovs-sandbox
@@ -56,27 +56,11 @@ gdb_vswitchd=false
gdb_ovsdb=false
gdb_vswitchd_ex=false
gdb_ovsdb_ex=false
-gdb_ovn_northd=false
-gdb_ovn_northd_ex=false
-gdb_ovn_controller=false
-gdb_ovn_controller_ex=false
-gdb_ovn_controller_vtep=false
-gdb_ovn_controller_vtep_ex=false
builddir=
srcdir=
schema=
installed=false
built=false
-ovn=false
-ovnsb_schema=
-ovnnb_schema=
-ovn_rbac=true
-n_northds=1
-n_controllers=1
-nbdb_model=standalone
-nbdb_servers=3
-sbdb_model=backup
-sbdb_servers=3
dummy=override
for option; do
@@ -120,23 +104,11 @@ These options force ovs-sandbox to use an installed Open vSwitch:
General options:
-g, --gdb-vswitchd run ovs-vswitchd under gdb
-d, --gdb-ovsdb run ovsdb-server under gdb
- --gdb-ovn-northd run ovn-northd under gdb
- --gdb-ovn-controller run ovn-controller under gdb
- --gdb-ovn-controller-vtep run ovn-controller-vtep under gdb
--dummy=ARG pass --enable-dummy=ARG to vswitchd (default: override)
-R, --gdb-run automatically start running the daemon in gdb
for any daemon set to run under gdb
-S, --schema=FILE use FILE as vswitch.ovsschema
-OVN options:
- -o, --ovn enable OVN
- --no-ovn-rbac disable role-based access control for OVN
- --n-northds=NUMBER run NUMBER copies of northd (default: 1)
- --nbdb-model=standalone|backup|clustered northbound database model
- --nbdb-servers=N number of servers in nbdb cluster (default: 3)
- --sbdb-model=standalone|backup|clustered southbound database model
- --sbdb-servers=N number of servers in sbdb cluster (default: 3)
-
Other options:
-h, --help Print this usage message.
EOF
@@ -192,67 +164,9 @@ EOF
gdb_ovsdb=true
gdb_ovsdb_ex=true
;;
- --gdb-ovn-northd)
- gdb_ovn_northd=true
- ;;
- --gdb-ovn-controller)
- gdb_ovn_controller=true
- ;;
- --gdb-ovn-controller-vtep)
- gdb_ovn_controller_vtep=true
- ;;
- -o|--ovn)
- ovn=true
- ;;
- --no-ovn-rbac)
- ovn_rbac=false
- ;;
- --n-northd*=*)
- n_northds=$optarg
- ;;
- --n-northd*)
- prev=n_northds
- ;;
- --n-controller*=*)
- n_controllers=$optarg
- ;;
- --n-controller*)
- prev=n_controllers
- ;;
- --nbdb-s*=*)
- nbdb_servers=$optarg
- nbdb_model=clustered
- ;;
- --nbdb-s*)
- prev=nbdb_servers
- nbdb_model=clustered
- ;;
- --nbdb-m*=*)
- nbdb_model=$optarg
- ;;
- --nbdb-m*)
- prev=nbdb_model
- ;;
- --sbdb-s*=*)
- sbdb_servers=$optarg
- sbdb_model=clustered
- ;;
- --sbdb-s*)
- prev=sbdb_servers
- sbdb_model=clustered
- ;;
- --sbdb-m*=*)
- sbdb_model=$optarg
- ;;
- --sbdb-m*)
- prev=sbdb_model
- ;;
-R|--gdb-run)
gdb_vswitchd_ex=true
gdb_ovsdb_ex=true
- gdb_ovn_northd_ex=true
- gdb_ovn_controller_ex=true
- gdb_ovn_controller_vtep_ex=true
;;
-*)
echo "unrecognized option $option (use --help for help)" >&2
@@ -304,23 +218,6 @@ if $built; then
echo >&2 'source directory not found, please use --srcdir'
exit 1
fi
- if $ovn; then
- ovnsb_schema=$srcdir/ovn/ovn-sb.ovsschema
- if test ! -e "$ovnsb_schema"; then
- echo >&2 'source directory not found, please use --srcdir'
- exit 1
- fi
- ovnnb_schema=$srcdir/ovn/ovn-nb.ovsschema
- if test ! -e "$ovnnb_schema"; then
- echo >&2 'source directory not found, please use --srcdir'
- exit 1
- fi
- vtep_schema=$srcdir/vtep/vtep.ovsschema
- if test ! -e "$vtep_schema"; then
- echo >&2 'source directory not found, please use --srcdir'
- exit 1
- fi
- fi
# Put built tools early in $PATH.
if test ! -e $builddir/vswitchd/ovs-vswitchd; then
@@ -328,9 +225,6 @@ if $built; then
exit 1
fi
PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$builddir/vtep:$PATH
- if $ovn; then
- PATH=$builddir/ovn/controller:$builddir/ovn/controller-vtep:$builddir/ovn/northd:$builddir/ovn/utilities:$PATH
- fi
export PATH
else
case $schema in
@@ -351,10 +245,6 @@ else
echo "can't find vswitch.ovsschema, please specify --schema" >&2
exit 1
fi
- if $ovn; then
- echo "running with ovn is only supported from the build dir." >&2
- exit 1
- fi
fi
# Create sandbox.
@@ -381,109 +271,10 @@ trap 'kill `cat "$sandbox"/*.pid`' 0 1 2 3 13 14 15
touch "$sandbox"/.conf.db.~lock~
run ovsdb-tool create conf.db "$schema"
ovsdb_server_args=
-if $ovn; then
- touch "$sandbox"/.ovnnb.db.~lock~
- run ovsdb-tool create ovnnb.db "$ovnnb_schema"
- run ovsdb-tool create vtep.db "$vtep_schema"
- ovsdb_server_args="vtep.db conf.db"
- ovsdb_nb_server_args="ovnnb.db"
-
- if [ "$HAVE_OPENSSL" = yes ]; then
- OVS_PKI="run ovs-pki --dir=$sandbox/pki --log=$sandbox/ovs-pki.log"
- $OVS_PKI init
- $OVS_PKI req+sign ovnsb switch
- $OVS_PKI req+sign ovnnb switch
- for i in $(seq $n_controllers); do
- $OVS_PKI -u req+sign chassis-$i switch
- done
- fi
-fi
rungdb $gdb_ovsdb $gdb_ovsdb_ex ovsdb-server --detach --no-chdir --pidfile -vconsole:off --log-file -vsyslog:off \
--remote=punix:"$sandbox"/db.sock \
--remote=db:Open_vSwitch,Open_vSwitch,manager_options \
$ovsdb_server_args
-if $ovn; then
- ovn_start_db() {
- local db=$1 model=$2 servers=$3 schema=$4
- local DB=$(echo $db | tr a-z A-Z)
- local schema_name=$(ovsdb-tool schema-name $schema)
-
- case $model in
- standalone | backup) ;;
- clustered)
- case $servers in
- [1-9] | [1-9][0-9]) ;;
- *) echo "${db}db servers must be between 1 and 99" >&2
- exit 1
- ;;
- esac
- ;;
- *)
- echo "unknown ${db}db model \"$model\"" >&2
- exit 1
- ;;
- esac
-
- ovn_start_ovsdb_server() {
- local i=$1; shift
- rungdb $gdb_ovsdb $gdb_ovsdb_ex ovsdb-server --detach --no-chdir \
- --pidfile=$db$i.pid -vconsole:off --log-file=$db$i.log \
- -vsyslog:off \
- --remote=db:$schema_name,${DB}_Global,connections \
- --private-key=db:$schema_name,SSL,private_key \
- --certificate=db:$schema_name,SSL,certificate \
- --ca-cert=db:$schema_name,SSL,ca_cert \
- --ssl-protocols=db:$schema_name,SSL,ssl_protocols \
- --ssl-ciphers=db:$schema_name,SSL,ssl_ciphers \
- --unixctl=${db}$i --remote=punix:$db$i.ovsdb ${db}$i.db "$@"
- }
-
- case $model in
- standalone)
- run ovsdb-tool create ${db}1.db "$schema"
- ovn_start_ovsdb_server 1
- remote=unix:${db}1.ovsdb
- ;;
- backup)
- for i in 1 2; do
- run ovsdb-tool create $db$i.db "$schema"
- done
- ovn_start_ovsdb_server 1
- ovn_start_ovsdb_server 2 --sync-from=unix:${db}1.ovsdb
- remote=unix:${db}1.ovsdb
- backup_note="$backup_note
-The backup server of OVN $DB can be accessed by:
-* ovn-${db}ctl --db=unix:`pwd`/sandbox/${db}2.ovsdb
-* ovs-appctl -t `pwd`/sandbox/${db}2
-The backup database file is sandbox/${db}2.db
-"
- ;;
- clustered)
- for i in $(seq $servers); do
- if test $i = 1; then
- run ovsdb-tool create-cluster ${db}1.db "$schema" unix:${db}1.raft;
- else
- run ovsdb-tool join-cluster $db$i.db $schema_name unix:$db$i.raft unix:${db}1.raft
- fi
- ovn_start_ovsdb_server $i
- done
- remote=unix:${db}1.ovsdb
- for i in `seq 2 $servers`; do
- remote=$remote,unix:$db$i.ovsdb
- done
- for i in $(seq $servers); do
- run ovsdb-client wait unix:$db$i.ovsdb $schema_name connected
- done
- ;;
- esac
- eval OVN_${DB}_DB=\$remote
- eval export OVN_${DB}_DB
- }
-
- backup_note=
- ovn_start_db nb "$nbdb_model" "$nbdb_servers" "$ovnnb_schema"
- ovn_start_db sb "$sbdb_model" "$sbdb_servers" "$ovnsb_schema"
-fi
#Add a small delay to allow ovsdb-server to launch.
sleep 0.1
@@ -504,50 +295,6 @@ run ovs-vsctl --no-wait -- init
rungdb $gdb_vswitchd $gdb_vswitchd_ex ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --log-file -vsyslog:off \
--enable-dummy=$dummy -vvconn -vnetdev_dummy
-if $ovn; then
- ovn-nbctl init
- ovn-sbctl init
-
- ovs-vsctl set open . external-ids:system-id=chassis-1
- ovs-vsctl set open . external-ids:hostname=sandbox
- ovs-vsctl set open . external-ids:ovn-encap-type=geneve
- ovs-vsctl set open . external-ids:ovn-encap-ip=127.0.0.1
-
- if [ "$HAVE_OPENSSL" = yes ]; then
- ovn-nbctl set-ssl $sandbox/ovnnb-privkey.pem $sandbox/ovnnb-cert.pem $sandbox/pki/switchca/cacert.pem
- ovn-nbctl set-connection pssl:6641
- ovn-sbctl set-ssl $sandbox/ovnsb-privkey.pem $sandbox/ovnsb-cert.pem $sandbox/pki/switchca/cacert.pem
- if $ovn_rbac; then
- ovn-sbctl set-connection role=ovn-controller pssl:6642
- else
- ovn-sbctl set-connection pssl:6642
- fi
- ovs-vsctl set open . external-ids:ovn-remote=ssl:127.0.0.1:6642
- OVN_CTRLR_PKI="-p $sandbox/chassis-1-privkey.pem -c $sandbox/chassis-1-cert.pem -C $sandbox/pki/switchca/cacert.pem"
- else
- ovs-vsctl set open . external-ids:ovn-remote=$OVN_SB_DB
- OVN_CTRLR_PKI=""
- fi
- for i in $(seq $n_northds); do
- if [ $i -eq 1 ]; then inst=""; else inst=$i; fi
- rungdb $gdb_ovn_northd $gdb_ovn_northd_ex ovn-northd --detach \
- --no-chdir --pidfile=ovn-northd${inst}.pid -vconsole:off \
- --log-file=ovn-northd${inst}.log -vsyslog:off \
- --ovnsb-db="$OVN_SB_DB" --ovnnb-db="$OVN_NB_DB"
- done
- for i in $(seq $n_controllers); do
- if [ $i -eq 1 ]; then inst=""; else inst=$i; fi
- rungdb $gdb_ovn_controller $gdb_ovn_controller_ex ovn-controller \
- $OVN_CTRLR_PKI --detach --no-chdir -vsyslog:off \
- --log-file=ovn-controller${inst}.log \
- --pidfile=ovn-controller${inst}.pid -vconsole:off
- done
- rungdb $gdb_ovn_controller_vtep $gdb_ovn_controller_vtep_ex \
- ovn-controller-vtep --detach --no-chdir --pidfile -vconsole:off \
- $OVN_CTRLR_PKI --log-file -vsyslog:off \
- --ovnsb-db=unix:"$sandbox"/ovnsb_db.sock
-fi
-
cat <<EOF
@@ -557,14 +304,6 @@ You are running in a dummy Open vSwitch environment. You can use
ovs-vsctl, ovs-ofctl, ovs-appctl, and other tools to work with the
dummy switch.
-EOF
-if $ovn; then cat << EOF
-This environment also has the OVN daemons and databases enabled.
-You can use ovn-nbctl and ovn-sbctl to interact with the OVN databases.
-$backup_note
-EOF
-fi
-cat <<EOF
Log files, pidfiles, and the configuration database are in the
"sandbox" subdirectory.
diff --git a/utilities/bugtool/automake.mk b/utilities/bugtool/automake.mk
index 18fa3478e..40980b367 100644
--- a/utilities/bugtool/automake.mk
+++ b/utilities/bugtool/automake.mk
@@ -32,8 +32,7 @@ bugtoolpluginsdir = $(pkgdatadir)/bugtool-plugins
INSTALL_DATA_LOCAL += bugtool-install-data-local
bugtool-install-data-local:
for plugin in $(bugtool_plugins); do \
- stem=`echo "$$plugin" | sed 's,ovn/,,'`; \
- stem=`echo "$$stem" | sed 's,utilities/bugtool/plugins/,,'`; \
+ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \
dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \
$(MKDIR_P) "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \
$(INSTALL_DATA) "$(srcdir)/$$plugin" "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \
@@ -42,13 +41,11 @@ bugtool-install-data-local:
UNINSTALL_LOCAL += bugtool-uninstall-local
bugtool-uninstall-local:
for plugin in $(bugtool_plugins); do \
- stem=`echo "$$plugin" | sed 's,ovn/,,'`; \
- stem=`echo "$$stem" | sed 's,utilities/bugtool/plugins/,,'`; \
+ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \
rm -f "$(DESTDIR)$(bugtoolpluginsdir)/$$stem"; \
done
for plugin in $(bugtool_plugins); do \
- stem=`echo "$$plugin" | sed 's,ovn/,,'`; \
- stem=`echo "$$stem" | sed 's,utilities/bugtool/plugins/,,'`; \
+ stem=`echo "$$plugin" | sed 's,utilities/bugtool/plugins/,,'`; \
dir=`expr "$$stem" : '\(.*\)/[^/]*$$'`; \
if [ ! -z "$$dir" ]; then \
rm -rf "$(DESTDIR)$(bugtoolpluginsdir)/$$dir"; \
diff --git a/utilities/ovs-sim.in b/utilities/ovs-sim.in
index 47329da21..08957bdf4 100755
--- a/utilities/ovs-sim.in
+++ b/utilities/ovs-sim.in
@@ -70,7 +70,6 @@ fi
# Put built tools early in $PATH.
PATH=$sim_builddir/ovsdb:$sim_builddir/vswitchd:$sim_builddir/utilities:$PATH
-PATH=$sim_builddir/ovn/controller:$sim_builddir/ovn/northd:$sim_builddir/ovn/utilities:$PATH
export PATH
rm -rf sandbox
@@ -101,8 +100,6 @@ sim_setvars() {
export -f sim_setvars
ovs-vsctl () { command ovs-vsctl -vsyslog:off "$@"; }; export -f ovs-vsctl
-ovs-nbctl () { command ovs-nbctl -vsyslog:off "$@"; }; export -f ovs-nbctl
-ovs-sbctl () { command ovs-sbctl -vsyslog:off "$@"; }; export -f ovs-sbctl
vtep-ctl () { command vtep-ctl -vsyslog:off "$@"; }; export -f vtep-ctl
as() {
@@ -187,7 +184,7 @@ $FUNCNAME: create a new interconnection network
usage: $FUNCNAME NETWORK
where NETWORK is the name of the new network. Interconnection networks
-are used with net_attach and ovn_attach.
+are used with net_attach.
EOF
return 0
fi
@@ -235,234 +232,6 @@ EOF
}
export -f net_attach
-ovn_start_db() {
- local db=$1 model=$2 servers=$3 schema=$4
- local DB=$(echo $db | tr a-z A-Z)
- local schema_name=$(ovsdb-tool schema-name $schema)
-
- case $model in
- standalone | backup) ;;
- clustered)
- case $servers in
- [1-9] | [1-9][0-9]) ;;
- *) echo "${db}db servers must be between 1 and 99" >&2
- exit 1
- ;;
- esac
- ;;
- *)
- echo "unknown ${db}db model \"$model\"" >&2
- exit 1
- ;;
- esac
-
- ovn_start_ovsdb_server() {
- local i=$1; shift
- as ${db}$i ovsdb-server --detach --no-chdir --pidfile=$db.pid \
- -vsyslog:off -vconsole:off --log-file="$sim_base"/$db$i/$db.log \
- --remote=db:$schema_name,${DB}_Global,connections \
- --private-key=db:$schema_name,SSL,private_key \
- --certificate=db:$schema_name,SSL,certificate \
- --ca-cert=db:$schema_name,SSL,ca_cert \
- --ssl-protocols=db:$schema_name,SSL,ssl_protocols \
- --ssl-ciphers=db:$schema_name,SSL,ssl_ciphers \
- --unixctl=${db} --remote=punix:$db.ovsdb \
- "$sim_base"/$db$i/$db.db "$@"
- }
-
- ovn_prep_db() {
- local i=$1
- mkdir "$sim_base"/${db}$i
- touch "$sim_base"/${db}$i/.$db.db.~lock~
- }
-
- local n_remotes=1
- case $model in
- standalone)
- ovn_prep_db 1
- ovsdb-tool create "$sim_base"/${db}1/$db.db "$schema"
- ovn_start_ovsdb_server 1
- ;;
- backup)
- for i in 1 2; do
- ovn_prep_db $i
- ovsdb-tool create "$sim_base"/$db$i/$db.db "$schema"
- done
- ovn_start_ovsdb_server 1
- ovn_start_ovsdb_server 2 --sync-from=unix:"$sim_base"/${db}1/$db.ovsdb
- cat <<EOF
-The backup server of OVN $DB can be accessed by:
-* ovn-${db}ctl --db=unix:$sim_base/${db}2/$db.ovsdb
-* ovs-appctl -t $sim_base/${db}2/${db}
-The backup database file is $sim_base/${db}2/$db.db
-EOF
- ;;
- clustered)
- n_remotes=$servers
- for i in $(seq $servers); do
- ovn_prep_db $i
- if test $i = 1; then
- ovsdb-tool create-cluster "$sim_base"/$db$i/$db.db "$schema" unix:"$sim_base"/$db$i/db.raft
- else
- ovsdb-tool join-cluster "$sim_base"/$db$i/$db.db $schema_name unix:"$sim_base"/$db$i/db.raft unix:"$sim_base"/${db}1/db.raft
- fi
- ovn_start_ovsdb_server $i
- done
- for i in $(seq $servers); do
- ovsdb-client wait unix:"$sim_base"/${db}$i/$db.ovsdb $schema_name connected
- done
- ;;
- esac
-
- remote=unix:"$sim_base"/${db}1/$db.ovsdb
- for i in `seq 2 $n_remotes`; do
- remote=$remote,unix:"$sim_base"/${db}$i/$db.ovsdb
- done
- eval OVN_${DB}_DB=\$remote
- eval export OVN_${DB}_DB
-}
-export -f ovn_start_db
-
-ovn_start() {
- local nbdb_model=standalone
- local nbdb_servers=3
- local sbdb_model=standalone
- local sbdb_servers=3
- local prev=
- for option; do
- # This option-parsing mechanism borrowed from a Autoconf-generated
- # configure script under the following license:
-
- # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
- # 2002, 2003, 2004, 2005, 2006, 2009, 2013 Free Software Foundation, Inc.
- # This configure script is free software; the Free Software Foundation
- # gives unlimited permission to copy, distribute and modify it.
-
- # If the previous option needs an argument, assign it.
- if test -n "$prev"; then
- eval $prev=\$option
- prev=
- continue
- fi
- case $option in
- *=*) optarg=`expr "X$option" : '[^=]*=\(.*\)'` ;;
- *) optarg=yes ;;
- esac
-
- case $dashdash$option in
- --)
- dashdash=yes ;;
- -h|--help)
- cat <<EOF
-$FUNCNAME: start OVN central databases and daemons
-usage: $FUNCNAME [OPTION...]
-
-This creates and initializes the central OVN databases (northbound and
-southbound), starts their ovsdb-server daemons, and starts the ovn-northd
-daemon.
-
-Options:
- --nbdb-model=standalone|backup|clustered northbound database model
- --nbdb-servers=N number of servers in nbdb cluster (default: 3)
- --sbdb-model=standalone|backup|clustered southbound database model
- --sbdb-servers=N number of servers in sbdb cluster (default: 3)
- -h, --help Print this usage message.
-EOF
- return
- ;;
-
- --nbdb-s*=*)
- nbdb_servers=$optarg
- nbdb_model=clustered
- ;;
- --nbdb-s*)
- prev=nbdb_servers
- nbdb_model=clustered
- ;;
- --nbdb-m*=*)
- nbdb_model=$optarg
- ;;
- --nbdb-m*)
- prev=nbdb_model
- ;;
- --sbdb-s*=*)
- sbdb_servers=$optarg
- sbdb_model=clustered
- ;;
- --sbdb-s*)
- prev=sbdb_servers
- sbdb_model=clustered
- ;;
- --sbdb-m*=*)
- sbdb_model=$optarg
- ;;
- --sbdb-m*)
- prev=sbdb_model
- ;;
- -*)
- echo "unrecognized option $option (use --help for help)" >&2
- return 1
- ;;
- *)
- echo "$option: non-option arguments not supported (use --help for help)" >&2
- return 1
- ;;
- esac
- shift
- done
-
- if test -d ovn-sb || test -d ovn-nb; then
- echo >&2 "OVN already started"
- return 1
- fi
-
- ovn_start_db nb "$nbdb_model" "$nbdb_servers" "$sim_srcdir"/ovn/ovn-nb.ovsschema
- ovn_start_db sb "$sbdb_model" "$sbdb_servers" "$sim_srcdir"/ovn/ovn-sb.ovsschema
-
- ovn-nbctl init
- ovn-sbctl init
-
- mkdir "$sim_base"/northd
- as northd ovn-northd --ovnnb-db="$OVN_NB_DB" --ovnsb-db="$OVN_SB_DB" \
- $daemon_opts
-}
-export -f ovn_start
-
-ovn_attach() {
- if test "$1" == --help; then
- cat <<EOF
-$FUNCNAME: attach default sandbox to an interconnection network for OVN
-usage: $FUNCNAME NETWORK BRIDGE IP [MASKLEN]
-
-This starts by doing everything that net_attach does. Then it configures the
-specified IP and MASKLEN (e.g. 192.168.0.1 and 24) on BRIDGE and starts
-and configures ovn-controller.
-
-MASKLEN defaults to 24 if it is not specified.
-EOF
- return 0
- fi
- if test $# != 3 && test $# != 4; then
- echo >&2 "$FUNCNAME: wrong number of arguments (use --help for help)"
- return 1
- fi
-
- local net=$1 bridge=$2 ip=$3 masklen=${4-24}
- net_attach $net $bridge || return $?
-
- ovs-appctl netdev-dummy/ip4addr $bridge $ip/$masklen >/dev/null
- ovs-appctl ovs/route/add $ip/$masklen $bridge > /dev/null
- ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=$sandbox \
- -- set Open_vSwitch . external-ids:ovn-remote=$OVN_SB_DB \
- -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=$ip\
- -- add-br br-int \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
- ovn-controller --detach --no-chdir --pidfile -vconsole:off -vsyslog:off --log-file
-}
-export -f ovn_attach
-
# Easy access to OVS manpages.
mkdir $sim_base/man
mandir=`cd $sim_base/man && pwd`
@@ -494,8 +263,8 @@ rc='
______________________________________________________________________
|
| You are running in a nested shell environment meant for Open vSwitch
-| and OVN testing in simulation. The OVS manpages are available via
-| "man". Please see ovs-sim(1) for more information.
+| testing in simulation. The OVS manpages are available via "man".
+| Please see ovs-sim(1) for more information.
|
| Exit the shell to kill the running daemons and leave the simulation
| environment.
diff --git a/xenserver/openvswitch-xen.spec.in b/xenserver/openvswitch-xen.spec.in
index ba3580836..cdc341dcc 100644
--- a/xenserver/openvswitch-xen.spec.in
+++ b/xenserver/openvswitch-xen.spec.in
@@ -456,7 +456,6 @@ exit 0
/usr/share/openvswitch/scripts/ovs-ctl
/usr/share/openvswitch/scripts/ovs-lib
/usr/share/openvswitch/scripts/ovs-vtep
-/usr/share/openvswitch/scripts/ovndb-servers.ocf
/usr/share/openvswitch/vswitch.ovsschema
/usr/share/openvswitch/vtep.ovsschema
/usr/sbin/ovs-bugtool
@@ -507,12 +506,6 @@ exit 0
%exclude /usr/share/openvswitch/python/*.py[co]
%exclude /usr/share/openvswitch/python/ovs/*.py[co]
%exclude /usr/share/openvswitch/python/ovs/db/*.py[co]
-%exclude /usr/bin/ovn-*
-%exclude /usr/share/man/man5/ovn-*
-%exclude /usr/share/man/man7/ovn-*
-%exclude /usr/share/man/man8/ovn-*
-%exclude /usr/share/openvswitch/ovn-*
-%exclude /usr/share/openvswitch/scripts/ovn-*
%files %{module_package}
/lib/modules/%{xen_version}/extra/openvswitch/openvswitch.ko