2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 22:35:15 +00:00

Remove OVN.

OVN is separated into its own repo. This commit removes the OVN source,
OVN tests, and OVN documentation. It also removes mentions of OVN from
most documentation. The only place where OVN has been left is in
changelogs/NEWS, since we shouldn't mess with the history of the
project.

There is an exception here. The ovsdb-cluster tests rely on ovn-nbctl
and ovn-sbctl to run. Therefore those ovn utilities, as well as their
dependencies remain in the repo with this commit.

Acked-by: Numan Siddique <nusiddiq@redhat.com>
Signed-off-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
Mark Michelson
2019-09-06 10:33:03 -04:00
committed by Ben Pfaff
parent 9b0064a3ca
commit f3e24610ea
169 changed files with 47 additions and 75436 deletions

View File

@@ -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 \

View File

@@ -41,4 +41,3 @@ Open vSwitch FAQ
terminology
vlan
vxlan
ovn

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -50,12 +50,3 @@ OVS
sflow
dpdk
OVN
---
.. toctree::
:maxdepth: 1
docker
openstack-containers
firewalld

View File

@@ -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.

View File

@@ -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
-----------

View File

@@ -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.

View File

@@ -62,14 +62,6 @@ provided below.
fedora
rhel
Upgrades
--------
.. toctree::
:maxdepth: 2
ovn-upgrades
Others
------

View File

@@ -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.

View File

@@ -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
Heres 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
Heres 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'

View File

@@ -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.

View File

@@ -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>`__

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

5
NEWS
View File

@@ -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
---------------------

View File

@@ -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])

5
debian/.gitignore vendored
View File

@@ -22,10 +22,5 @@
/openvswitch-test
/openvswitch-testcontroller
/openvswitch-vtep
/ovn-common
/ovn-controller-vtep
/ovn-host
/ovn-central
/ovn-docker
/python-openvswitch
/tmp

22
debian/automake.mk vendored
View File

@@ -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 \

78
debian/control vendored
View File

@@ -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~),

View File

@@ -1 +0,0 @@
/usr/share/ovn/central

View File

@@ -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

View File

@@ -1,3 +0,0 @@
usr/bin/ovn-northd
usr/share/openvswitch/ovn-nb.ovsschema
usr/share/openvswitch/ovn-sb.ovsschema

View File

@@ -1 +0,0 @@
ovn/northd/ovn-northd.8

View File

@@ -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

View File

@@ -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

View File

@@ -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=

View File

@@ -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.*

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1 +0,0 @@
usr/bin/ovn-controller-vtep

View File

@@ -1 +0,0 @@
ovn/controller-vtep/ovn-controller-vtep.8

View File

@@ -1,2 +0,0 @@
usr/bin/ovn-docker-overlay-driver
usr/bin/ovn-docker-underlay-driver

View File

@@ -1 +0,0 @@
/usr/share/ovn/host

54
debian/ovn-host.init vendored
View File

@@ -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

View File

@@ -1 +0,0 @@
usr/bin/ovn-controller

View File

@@ -1 +0,0 @@
ovn/controller/ovn-controller.8

View File

@@ -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

View File

@@ -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

View File

@@ -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=

6
debian/rules vendored
View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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>

View File

@@ -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 \

View File

@@ -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.

View File

@@ -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

View File

@@ -1,2 +0,0 @@
/ovn-controller-vtep
/ovn-controller-vtep.8

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -1,2 +0,0 @@
/ovn-controller
/ovn-controller.8

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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();
}

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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>

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More