[ovs-dev] [PATCH v4] Remove OVN.

Numan Siddique nusiddiq at redhat.com
Tue Aug 6 14:33:16 UTC 2019


Tested-by: Numan Siddique <nusiddiq at redhat.com>
Acked-by: Numan Siddique <nusiddiq at redhat.com>

Numan


On Tue, Jul 30, 2019 at 1:13 AM Mark Michelson <mmichels at redhat.com> wrote:

> 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.
>
> Signed-off-by: Mark Michelson <mmichels at redhat.com>
> ---
> v3 -> v4 Fixed 0-day Robot problems with NEWS and documentation
> v2 -> v3 Added NEWS entry for removal of OVN
> v1 -> v2 Rebase
> ---
>  Documentation/automake.mk                          |    12 -
>  Documentation/faq/index.rst                        |     1 -
>  Documentation/faq/ovn.rst                          |    90 -
>  Documentation/howto/docker.rst                     |   326 -
>  Documentation/howto/firewalld.rst                  |   107 -
>  Documentation/howto/index.rst                      |     9 -
>  Documentation/howto/openstack-containers.rst       |   135 -
>  Documentation/index.rst                            |    20 +-
>  Documentation/intro/install/fedora.rst             |    12 -
>  Documentation/intro/install/index.rst              |     8 -
>  Documentation/intro/install/ovn-upgrades.rst       |   115 -
>  Documentation/ref/ovs-sim.1.rst                    |   100 -
>  Documentation/topics/high-availability.rst         |   440 -
>  Documentation/topics/index.rst                     |    18 +-
>  Documentation/topics/ovn-news-2.8.rst              |   278 -
>  Documentation/topics/role-based-access-control.rst |   101 -
>  Documentation/tutorials/index.rst                  |     7 +-
>  Documentation/tutorials/ovn-ipsec.rst              |   146 -
>  Documentation/tutorials/ovn-openstack.rst          |  1922 ---
>  Documentation/tutorials/ovn-rbac.rst               |   134 -
>  Documentation/tutorials/ovn-sandbox.rst            |   177 -
>  Makefile.am                                        |     1 -
>  NEWS                                               |     5 +-
>  configure.ac                                       |     3 -
>  debian/.gitignore                                  |     5 -
>  debian/automake.mk                                 |    22 -
>  debian/control                                     |    78 -
>  debian/ovn-central.dirs                            |     1 -
>  debian/ovn-central.init                            |    60 -
>  debian/ovn-central.install                         |     3 -
>  debian/ovn-central.manpages                        |     1 -
>  debian/ovn-central.postinst                        |    49 -
>  debian/ovn-central.postrm                          |    48 -
>  debian/ovn-central.template                        |     5 -
>  debian/ovn-common.install                          |     7 -
>  debian/ovn-common.manpages                         |     8 -
>  debian/ovn-common.postinst                         |    24 -
>  debian/ovn-common.postrm                           |    23 -
>  debian/ovn-controller-vtep.init                    |    54 -
>  debian/ovn-controller-vtep.install                 |     1 -
>  debian/ovn-controller-vtep.manpages                |     1 -
>  debian/ovn-docker.install                          |     2 -
>  debian/ovn-host.dirs                               |     1 -
>  debian/ovn-host.init                               |    54 -
>  debian/ovn-host.install                            |     1 -
>  debian/ovn-host.manpages                           |     1 -
>  debian/ovn-host.postinst                           |    49 -
>  debian/ovn-host.postrm                             |    44 -
>  debian/ovn-host.template                           |     5 -
>  debian/rules                                       |     6 -
>  include/automake.mk                                |     1 -
>  include/ovn/actions.h                              |   622 -
>  include/ovn/automake.mk                            |     6 -
>  include/ovn/expr.h                                 |   518 -
>  include/ovn/lex.h                                  |   152 -
>  include/ovn/logical-fields.h                       |   130 -
>  lib/db-ctl-base.xml                                |    12 +-
>  manpages.mk                                        |    28 -
>  ovn/.gitignore                                     |     8 -
>  ovn/TODO.rst                                       |   147 -
>  ovn/automake.mk                                    |    92 -
>  ovn/controller-vtep/.gitignore                     |     2 -
>  ovn/controller-vtep/automake.mk                    |    14 -
>  ovn/controller-vtep/binding.c                      |   274 -
>  ovn/controller-vtep/binding.h                      |    27 -
>  ovn/controller-vtep/gateway.c                      |   230 -
>  ovn/controller-vtep/gateway.h                      |    26 -
>  ovn/controller-vtep/ovn-controller-vtep.8.xml      |    80 -
>  ovn/controller-vtep/ovn-controller-vtep.c          |   272 -
>  ovn/controller-vtep/ovn-controller-vtep.h          |    51 -
>  ovn/controller-vtep/vtep.c                         |   600 -
>  ovn/controller-vtep/vtep.h                         |    27 -
>  ovn/controller/.gitignore                          |     2 -
>  ovn/controller/automake.mk                         |    32 -
>  ovn/controller/bfd.c                               |   268 -
>  ovn/controller/bfd.h                               |    41 -
>  ovn/controller/binding.c                           |   764 -
>  ovn/controller/binding.h                           |    57 -
>  ovn/controller/chassis.c                           |   671 -
>  ovn/controller/chassis.h                           |    46 -
>  ovn/controller/encaps.c                            |   409 -
>  ovn/controller/encaps.h                            |    48 -
>  ovn/controller/ha-chassis.c                        |   203 -
>  ovn/controller/ha-chassis.h                        |    50 -
>  ovn/controller/ip-mcast.c                          |   164 -
>  ovn/controller/ip-mcast.h                          |    52 -
>  ovn/controller/lflow.c                             |   898 --
>  ovn/controller/lflow.h                             |   184 -
>  ovn/controller/lport.c                             |   102 -
>  ovn/controller/lport.h                             |    52 -
>  ovn/controller/ofctrl.c                            |  1393 --
>  ovn/controller/ofctrl.h                            |    87 -
>  ovn/controller/ovn-controller.8.xml                |   456 -
>  ovn/controller/ovn-controller.c                    |  2366 ---
>  ovn/controller/ovn-controller.h                    |    85 -
>  ovn/controller/patch.c                             |   273 -
>  ovn/controller/patch.h                             |    42 -
>  ovn/controller/physical.c                          |  1459 --
>  ovn/controller/physical.h                          |    74 -
>  ovn/controller/pinctrl.c                           |  4343 ------
>  ovn/controller/pinctrl.h                           |    51 -
>  ovn/lib/.gitignore                                 |     7 -
>  ovn/lib/acl-log.c                                  |   105 -
>  ovn/lib/acl-log.h                                  |    54 -
>  ovn/lib/actions.c                                  |  2902 ----
>  ovn/lib/automake.mk                                |    57 -
>  ovn/lib/chassis-index.c                            |    67 -
>  ovn/lib/chassis-index.h                            |    30 -
>  ovn/lib/expr.c                                     |  3450 -----
>  ovn/lib/extend-table.c                             |   208 -
>  ovn/lib/extend-table.h                             |    82 -
>  ovn/lib/inc-proc-eng.c                             |   201 -
>  ovn/lib/inc-proc-eng.h                             |   234 -
>  ovn/lib/ip-mcast-index.c                           |    40 -
>  ovn/lib/ip-mcast-index.h                           |    36 -
>  ovn/lib/lex.c                                      |  1023 --
>  ovn/lib/libovn.sym.in                              |     4 -
>  ovn/lib/logical-fields.c                           |   261 -
>  ovn/lib/mcast-group-index.c                        |    43 -
>  ovn/lib/mcast-group-index.h                        |    32 -
>  ovn/lib/ovn-l7.h                                   |   322 -
>  ovn/lib/ovn-nb-idl.ann                             |     9 -
>  ovn/lib/ovn-sb-idl.ann                             |    29 -
>  ovn/lib/ovn-util.c                                 |   373 -
>  ovn/lib/ovn-util.h                                 |    84 -
>  ovn/northd/.gitignore                              |     2 -
>  ovn/northd/automake.mk                             |    10 -
>  ovn/northd/ovn-northd.8.xml                        |  2544 ----
>  ovn/northd/ovn-northd.c                            |  9447 ------------
>  ovn/ovn-architecture.7.xml                         |  2074 ---
>  ovn/ovn-nb.ovsschema                               |   449 -
>  ovn/ovn-nb.xml                                     |  2917 ----
>  ovn/ovn-sb.ovsschema                               |   404 -
>  ovn/ovn-sb.xml                                     |  3638 -----
>  ovn/utilities/.gitignore                           |    11 -
>  ovn/utilities/automake.mk                          |    57 -
>  ovn/utilities/bugtool/automake.mk                  |     9 -
>  ovn/utilities/bugtool/ovn-bugtool-nbctl-show       |    19 -
>  ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list |    19 -
>  ovn/utilities/bugtool/ovn-bugtool-sbctl-show       |    19 -
>  .../bugtool/plugins/network-status/ovn.xml         |    23 -
>  ovn/utilities/ovn-ctl                              |   822 --
>  ovn/utilities/ovn-ctl.8.xml                        |   215 -
>  ovn/utilities/ovn-detrace.1.in                     |    38 -
>  ovn/utilities/ovn-detrace.in                       |   215 -
>  ovn/utilities/ovn-docker-overlay-driver.in         |   442 -
>  ovn/utilities/ovn-docker-underlay-driver.in        |   677 -
>  ovn/utilities/ovn-nbctl.8.xml                      |  1228 --
>  ovn/utilities/ovn-nbctl.c                          |  6061 --------
>  ovn/utilities/ovn-sbctl.8.in                       |   303 -
>  ovn/utilities/ovn-sbctl.c                          |  1541 --
>  ovn/utilities/ovn-trace.8.xml                      |   485 -
>  ovn/utilities/ovn-trace.c                          |  2373 ---
>  ovn/utilities/ovndb-servers.ocf                    |   642 -
>  ovsdb/ovsdb-tool.1.in                              |    23 +-
>  rhel/automake.mk                                   |    16 +-
>  rhel/ovn-fedora.spec.in                            |   432 -
>  ...walld_services_ovn-central-firewall-service.xml |     7 -
>  ...irewalld_services_ovn-host-firewall-service.xml |     6 -
>  ..._lib_systemd_system_ovn-controller-vtep.service |    50 -
>  rhel/usr_lib_systemd_system_ovn-controller.service |    34 -
>  rhel/usr_lib_systemd_system_ovn-northd.service     |    35 -
>  tests/atlocal.in                                   |     4 -
>  tests/automake.mk                                  |    46 +-
>  tests/ofproto-macros.at                            |     2 +-
>  tests/oss-fuzz/automake.mk                         |    10 -
>  tests/oss-fuzz/config/expr.dict                    |   120 -
>  tests/oss-fuzz/config/expr_parse_target.options    |     3 -
>  tests/oss-fuzz/expr_parse_target.c                 |   464 -
>  tests/ovn-controller-vtep.at                       |   467 -
>  tests/ovn-controller.at                            |   294 -
>  tests/ovn-macros.at                                |   180 -
>  tests/ovn-nbctl.at                                 |  1660 ---
>  tests/ovn-northd.at                                |   900 --
>  tests/ovn-performance.at                           |   424 -
>  tests/ovn-sbctl.at                                 |   150 -
>  tests/ovn.at                                       | 14702
> -------------------
>  tests/ovsdb-cluster-testsuite.at                   |    10 -
>  tests/ovsdb-cluster.at                             |   450 -
>  tests/system-kmod-testsuite.at                     |     2 -
>  tests/system-ovn.at                                |  1663 ---
>  tests/system-userspace-testsuite.at                |     2 -
>  tests/test-ovn.c                                   |  1584 --
>  tests/testsuite.at                                 |     8 -
>  tutorial/automake.mk                               |     3 +-
>  tutorial/ovn-setup.sh                              |    37 -
>  tutorial/ovs-sandbox                               |   261 -
>  utilities/bugtool/automake.mk                      |     9 +-
>  utilities/ovs-sim.in                               |   237 +-
>  xenserver/openvswitch-xen.spec.in                  |     7 -
>  190 files changed, 43 insertions(+), 93307 deletions(-)
>  delete mode 100644 Documentation/faq/ovn.rst
>  delete mode 100644 Documentation/howto/docker.rst
>  delete mode 100644 Documentation/howto/firewalld.rst
>  delete mode 100644 Documentation/howto/openstack-containers.rst
>  delete mode 100644 Documentation/intro/install/ovn-upgrades.rst
>  delete mode 100644 Documentation/topics/high-availability.rst
>  delete mode 100644 Documentation/topics/ovn-news-2.8.rst
>  delete mode 100644 Documentation/topics/role-based-access-control.rst
>  delete mode 100644 Documentation/tutorials/ovn-ipsec.rst
>  delete mode 100644 Documentation/tutorials/ovn-openstack.rst
>  delete mode 100644 Documentation/tutorials/ovn-rbac.rst
>  delete mode 100644 Documentation/tutorials/ovn-sandbox.rst
>  delete mode 100644 debian/ovn-central.dirs
>  delete mode 100755 debian/ovn-central.init
>  delete mode 100644 debian/ovn-central.install
>  delete mode 100644 debian/ovn-central.manpages
>  delete mode 100755 debian/ovn-central.postinst
>  delete mode 100755 debian/ovn-central.postrm
>  delete mode 100644 debian/ovn-central.template
>  delete mode 100644 debian/ovn-common.install
>  delete mode 100644 debian/ovn-common.manpages
>  delete mode 100644 debian/ovn-common.postinst
>  delete mode 100644 debian/ovn-common.postrm
>  delete mode 100755 debian/ovn-controller-vtep.init
>  delete mode 100644 debian/ovn-controller-vtep.install
>  delete mode 100644 debian/ovn-controller-vtep.manpages
>  delete mode 100644 debian/ovn-docker.install
>  delete mode 100644 debian/ovn-host.dirs
>  delete mode 100755 debian/ovn-host.init
>  delete mode 100644 debian/ovn-host.install
>  delete mode 100644 debian/ovn-host.manpages
>  delete mode 100755 debian/ovn-host.postinst
>  delete mode 100755 debian/ovn-host.postrm
>  delete mode 100644 debian/ovn-host.template
>  delete mode 100644 include/ovn/actions.h
>  delete mode 100644 include/ovn/automake.mk
>  delete mode 100644 include/ovn/expr.h
>  delete mode 100644 include/ovn/lex.h
>  delete mode 100644 include/ovn/logical-fields.h
>  delete mode 100644 ovn/.gitignore
>  delete mode 100644 ovn/TODO.rst
>  delete mode 100644 ovn/automake.mk
>  delete mode 100644 ovn/controller-vtep/.gitignore
>  delete mode 100644 ovn/controller-vtep/automake.mk
>  delete mode 100644 ovn/controller-vtep/binding.c
>  delete mode 100644 ovn/controller-vtep/binding.h
>  delete mode 100644 ovn/controller-vtep/gateway.c
>  delete mode 100644 ovn/controller-vtep/gateway.h
>  delete mode 100644 ovn/controller-vtep/ovn-controller-vtep.8.xml
>  delete mode 100644 ovn/controller-vtep/ovn-controller-vtep.c
>  delete mode 100644 ovn/controller-vtep/ovn-controller-vtep.h
>  delete mode 100644 ovn/controller-vtep/vtep.c
>  delete mode 100644 ovn/controller-vtep/vtep.h
>  delete mode 100644 ovn/controller/.gitignore
>  delete mode 100644 ovn/controller/automake.mk
>  delete mode 100644 ovn/controller/bfd.c
>  delete mode 100644 ovn/controller/bfd.h
>  delete mode 100644 ovn/controller/binding.c
>  delete mode 100644 ovn/controller/binding.h
>  delete mode 100644 ovn/controller/chassis.c
>  delete mode 100644 ovn/controller/chassis.h
>  delete mode 100644 ovn/controller/encaps.c
>  delete mode 100644 ovn/controller/encaps.h
>  delete mode 100644 ovn/controller/ha-chassis.c
>  delete mode 100644 ovn/controller/ha-chassis.h
>  delete mode 100644 ovn/controller/ip-mcast.c
>  delete mode 100644 ovn/controller/ip-mcast.h
>  delete mode 100644 ovn/controller/lflow.c
>  delete mode 100644 ovn/controller/lflow.h
>  delete mode 100644 ovn/controller/lport.c
>  delete mode 100644 ovn/controller/lport.h
>  delete mode 100644 ovn/controller/ofctrl.c
>  delete mode 100644 ovn/controller/ofctrl.h
>  delete mode 100644 ovn/controller/ovn-controller.8.xml
>  delete mode 100644 ovn/controller/ovn-controller.c
>  delete mode 100644 ovn/controller/ovn-controller.h
>  delete mode 100644 ovn/controller/patch.c
>  delete mode 100644 ovn/controller/patch.h
>  delete mode 100644 ovn/controller/physical.c
>  delete mode 100644 ovn/controller/physical.h
>  delete mode 100644 ovn/controller/pinctrl.c
>  delete mode 100644 ovn/controller/pinctrl.h
>  delete mode 100644 ovn/lib/.gitignore
>  delete mode 100644 ovn/lib/acl-log.c
>  delete mode 100644 ovn/lib/acl-log.h
>  delete mode 100644 ovn/lib/actions.c
>  delete mode 100644 ovn/lib/automake.mk
>  delete mode 100644 ovn/lib/chassis-index.c
>  delete mode 100644 ovn/lib/chassis-index.h
>  delete mode 100644 ovn/lib/expr.c
>  delete mode 100644 ovn/lib/extend-table.c
>  delete mode 100644 ovn/lib/extend-table.h
>  delete mode 100644 ovn/lib/inc-proc-eng.c
>  delete mode 100644 ovn/lib/inc-proc-eng.h
>  delete mode 100644 ovn/lib/ip-mcast-index.c
>  delete mode 100644 ovn/lib/ip-mcast-index.h
>  delete mode 100644 ovn/lib/lex.c
>  delete mode 100644 ovn/lib/libovn.sym.in
>  delete mode 100644 ovn/lib/logical-fields.c
>  delete mode 100644 ovn/lib/mcast-group-index.c
>  delete mode 100644 ovn/lib/mcast-group-index.h
>  delete mode 100644 ovn/lib/ovn-l7.h
>  delete mode 100644 ovn/lib/ovn-nb-idl.ann
>  delete mode 100644 ovn/lib/ovn-sb-idl.ann
>  delete mode 100644 ovn/lib/ovn-util.c
>  delete mode 100644 ovn/lib/ovn-util.h
>  delete mode 100644 ovn/northd/.gitignore
>  delete mode 100644 ovn/northd/automake.mk
>  delete mode 100644 ovn/northd/ovn-northd.8.xml
>  delete mode 100644 ovn/northd/ovn-northd.c
>  delete mode 100644 ovn/ovn-architecture.7.xml
>  delete mode 100644 ovn/ovn-nb.ovsschema
>  delete mode 100644 ovn/ovn-nb.xml
>  delete mode 100644 ovn/ovn-sb.ovsschema
>  delete mode 100644 ovn/ovn-sb.xml
>  delete mode 100644 ovn/utilities/.gitignore
>  delete mode 100644 ovn/utilities/automake.mk
>  delete mode 100644 ovn/utilities/bugtool/automake.mk
>  delete mode 100644 ovn/utilities/bugtool/ovn-bugtool-nbctl-show
>  delete mode 100644 ovn/utilities/bugtool/ovn-bugtool-sbctl-lflow-list
>  delete mode 100644 ovn/utilities/bugtool/ovn-bugtool-sbctl-show
>  delete mode 100644 ovn/utilities/bugtool/plugins/network-status/ovn.xml
>  delete mode 100755 ovn/utilities/ovn-ctl
>  delete mode 100644 ovn/utilities/ovn-ctl.8.xml
>  delete mode 100644 ovn/utilities/ovn-detrace.1.in
>  delete mode 100755 ovn/utilities/ovn-detrace.in
>  delete mode 100755 ovn/utilities/ovn-docker-overlay-driver.in
>  delete mode 100755 ovn/utilities/ovn-docker-underlay-driver.in
>  delete mode 100644 ovn/utilities/ovn-nbctl.8.xml
>  delete mode 100644 ovn/utilities/ovn-nbctl.c
>  delete mode 100644 ovn/utilities/ovn-sbctl.8.in
>  delete mode 100644 ovn/utilities/ovn-sbctl.c
>  delete mode 100644 ovn/utilities/ovn-trace.8.xml
>  delete mode 100644 ovn/utilities/ovn-trace.c
>  delete mode 100755 ovn/utilities/ovndb-servers.ocf
>  delete mode 100644 rhel/ovn-fedora.spec.in
>  delete mode 100644
> rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml
>  delete mode 100644
> rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml
>  delete mode 100644 rhel/usr_lib_systemd_system_ovn-controller-vtep.service
>  delete mode 100644 rhel/usr_lib_systemd_system_ovn-controller.service
>  delete mode 100644 rhel/usr_lib_systemd_system_ovn-northd.service
>  delete mode 100644 tests/oss-fuzz/config/expr.dict
>  delete mode 100644 tests/oss-fuzz/config/expr_parse_target.options
>  delete mode 100644 tests/oss-fuzz/expr_parse_target.c
>  delete mode 100644 tests/ovn-controller-vtep.at
>  delete mode 100644 tests/ovn-controller.at
>  delete mode 100644 tests/ovn-macros.at
>  delete mode 100644 tests/ovn-nbctl.at
>  delete mode 100644 tests/ovn-northd.at
>  delete mode 100644 tests/ovn-performance.at
>  delete mode 100644 tests/ovn-sbctl.at
>  delete mode 100644 tests/ovn.at
>  delete mode 100644 tests/ovsdb-cluster-testsuite.at
>  delete mode 100644 tests/ovsdb-cluster.at
>  delete mode 100644 tests/system-ovn.at
>  delete mode 100644 tests/test-ovn.c
>  delete mode 100755 tutorial/ovn-setup.sh
>
> diff --git a/Documentation/automake.mk b/Documentation/automake.mk
> index 2a3214a3c..cd68f3b15 100644
> --- a/Documentation/automake.mk
> +++ b/Documentation/automake.mk
> @@ -18,7 +18,6 @@ DOC_SOURCE = \
>         Documentation/intro/install/fedora.rst \
>         Documentation/intro/install/general.rst \
>         Documentation/intro/install/netbsd.rst \
> -       Documentation/intro/install/ovn-upgrades.rst \
>         Documentation/intro/install/rhel.rst \
>         Documentation/intro/install/userspace.rst \
>         Documentation/intro/install/windows.rst \
> @@ -26,12 +25,8 @@ DOC_SOURCE = \
>         Documentation/tutorials/index.rst \
>         Documentation/tutorials/faucet.rst \
>         Documentation/tutorials/ovs-advanced.rst \
> -       Documentation/tutorials/ovn-openstack.rst \
> -       Documentation/tutorials/ovn-sandbox.rst \
>         Documentation/tutorials/ovs-conntrack.rst \
>         Documentation/tutorials/ipsec.rst \
> -       Documentation/tutorials/ovn-ipsec.rst \
> -       Documentation/tutorials/ovn-rbac.rst \
>         Documentation/topics/index.rst \
>         Documentation/topics/bonding.rst \
>         Documentation/topics/idl-compound-indexes.rst \
> @@ -54,28 +49,22 @@ DOC_SOURCE = \
>         Documentation/topics/fuzzing/ovs-fuzzers.rst \
>         Documentation/topics/fuzzing/security-analysis-of-ovs-fuzzers.rst \
>         Documentation/topics/testing.rst \
> -       Documentation/topics/high-availability.rst \
>         Documentation/topics/integration.rst \
>         Documentation/topics/language-bindings.rst \
>         Documentation/topics/networking-namespaces.rst \
>         Documentation/topics/openflow.rst \
> -       Documentation/topics/ovn-news-2.8.rst \
>         Documentation/topics/ovsdb-replication.rst \
>         Documentation/topics/porting.rst \
> -       Documentation/topics/role-based-access-control.rst \
>         Documentation/topics/tracing.rst \
>         Documentation/topics/windows.rst \
>         Documentation/howto/index.rst \
> -       Documentation/howto/docker.rst \
>         Documentation/howto/dpdk.rst \
> -       Documentation/howto/firewalld.rst \
>         Documentation/howto/ipsec.rst \
>         Documentation/howto/kvm.rst \
>         Documentation/howto/libvirt.rst \
>         Documentation/howto/selinux.rst \
>         Documentation/howto/ssl.rst \
>         Documentation/howto/lisp.rst \
> -       Documentation/howto/openstack-containers.rst \
>         Documentation/howto/qos.png \
>         Documentation/howto/qos.rst \
>         Documentation/howto/sflow.png \
> @@ -94,7 +83,6 @@ DOC_SOURCE = \
>         Documentation/faq/general.rst \
>         Documentation/faq/issues.rst \
>         Documentation/faq/openflow.rst \
> -       Documentation/faq/ovn.rst \
>         Documentation/faq/qos.rst \
>         Documentation/faq/releases.rst \
>         Documentation/faq/terminology.rst \
> diff --git a/Documentation/faq/index.rst b/Documentation/faq/index.rst
> index ad3cc2b6f..334b828b2 100644
> --- a/Documentation/faq/index.rst
> +++ b/Documentation/faq/index.rst
> @@ -41,4 +41,3 @@ Open vSwitch FAQ
>     terminology
>     vlan
>     vxlan
> -   ovn
> diff --git a/Documentation/faq/ovn.rst b/Documentation/faq/ovn.rst
> deleted file mode 100644
> index 4d96b4aa5..000000000
> --- a/Documentation/faq/ovn.rst
> +++ /dev/null
> @@ -1,90 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -===
> -OVN
> -===
> -
> -Q: Why does OVN use STT and Geneve instead of VLANs or VXLAN (or GRE)?
> -
> -    A: OVN implements a fairly sophisticated packet processing pipeline in
> -    "logical datapaths" that can implement switching or routing
> functionality.
> -    A logical datapath has an ingress pipeline and an egress pipeline,
> and each
> -    of these pipelines can include logic based on packet fields as well as
> -    packet metadata such as the logical ingress and egress ports (the
> latter
> -    only in the egress pipeline).
> -
> -    The processing for a logical datapath can be split across
> hypervisors.  In
> -    particular, when a logical ingress pipeline executes an "output"
> action,
> -    OVN passes the packet to the egress pipeline on the hypervisor (or,
> in the
> -    case of output to a logical multicast group, hypervisors) on which the
> -    logical egress port is located.  If this hypervisor is not the same
> as the
> -    ingress hypervisor, then the packet has to be transmitted across a
> physical
> -    network.
> -
> -    This situation is where tunneling comes in.  To send the packet to
> another
> -    hypervisor, OVN encapsulates it with a tunnel protocol and sends the
> -    encapsulated packet across the physical network.  When the remote
> -    hypervisor receives the tunnel packet, it decapsulates it and passes
> it
> -    through the logical egress pipeline.  To do so, it also needs the
> metadata,
> -    that is, the logical ingress and egress ports.
> -
> -    Thus, to implement OVN logical packet processing, at least the
> following
> -    metadata must pass across the physical network:
> -
> -    * Logical datapath ID, a 24-bit identifier.  In Geneve, OVN uses the
> VNI to
> -      hold the logical datapath ID; in STT, OVN uses 24 bits of STT's
> 64-bit
> -      context ID.
> -
> -    * Logical ingress port, a 15-bit identifier.  In Geneve, OVN uses an
> option
> -      to hold the logical ingress port; in STT, 15 bits of the context ID.
> -
> -    * Logical egress port, a 16-bit identifier.  In Geneve, OVN uses an
> option
> -      to hold the logical egress port; in STT, 16 bits of the context ID.
> -
> -    See ``ovn-architecture(7)``, under "Tunnel Encapsulations", for
> details.
> -
> -    Together, these metadata require 24 + 15 + 16 = 55 bits.  GRE
> provides 32
> -    bits, VXLAN provides 24, and VLAN only provides 12.  Most notably, if
> -    logical egress pipelines do not match on the logical ingress port,
> thereby
> -    restricting the class of ACLs available to users, then this
> eliminates 15
> -    bits, bringing the requirement down to 40 bits.  At this point, one
> can
> -    choose to limit the size of the OVN logical network in various ways,
> e.g.:
> -
> -    * 16 bits of logical datapaths + 16 bits of logical egress ports.
> This
> -      combination fits within a 32-bit GRE tunnel key.
> -
> -    * 12 bits of logical datapaths + 12 bits of logical egress ports.
> This
> -      combination fits within a 24-bit VXLAN VNI.
> -
> -    * It's difficult to identify an acceptable compromise for a VLAN-based
> -      deployment.
> -
> -    These compromises wouldn't suit every site, since some deployments
> -    may need to allocate more bits to the datapath or egress port
> -    identifiers.
> -
> -    As a side note, OVN does support VXLAN for use with ASIC-based top of
> rack
> -    switches, using ``ovn-controller-vtep(8)`` and the OVSDB VTEP schema
> -    described in ``vtep(5)``, but this limits the features available from
> OVN
> -    to the subset available from the VTEP schema.
> diff --git a/Documentation/howto/docker.rst
> b/Documentation/howto/docker.rst
> deleted file mode 100644
> index a68b02fdb..000000000
> --- a/Documentation/howto/docker.rst
> +++ /dev/null
> @@ -1,326 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -===================================
> -Open Virtual Networking With Docker
> -===================================
> -
> -This document describes how to use Open Virtual Networking with Docker
> 1.9.0
> -or later.
> -
> -.. important::
> -
> -  Requires Docker version 1.9.0 or later. Only Docker 1.9.0+ comes with
> support
> -  for multi-host networking. Consult www.docker.com for instructions on
> how to
> -  install Docker.
> -
> -.. note::
> -
> -  You must build and install Open vSwitch before proceeding with the below
> -  guide. Refer to :doc:`/intro/install/index` for more information.
> -
> -Setup
> ------
> -
> -For multi-host networking with OVN and Docker, Docker has to be started
> with a
> -distributed key-value store. For example, if you decide to use consul as
> your
> -distributed key-value store and your host IP address is ``$HOST_IP``,
> start
> -your Docker daemon with::
> -
> -    $ docker daemon --cluster-store=consul://127.0.0.1:8500 \
> -        --cluster-advertise=$HOST_IP:0
> -
> -OVN provides network virtualization to containers. OVN's integration with
> -Docker currently works in two modes - the "underlay" mode or the "overlay"
> -mode.
> -
> -In the "underlay" mode, OVN requires a OpenStack setup to provide
> container
> -networking. In this mode, one can create logical networks and can have
> -containers running inside VMs, standalone VMs (without having any
> containers
> -running inside them) and physical machines connected to the same logical
> -network. This is a multi-tenant, multi-host solution.
> -
> -In the "overlay" mode, OVN can create a logical network amongst containers
> -running on multiple hosts. This is a single-tenant (extendable to
> multi-tenants
> -depending on the security characteristics of the workloads), multi-host
> -solution. In this mode, you do not need a pre-created OpenStack setup.
> -
> -For both the modes to work, a user has to install and start Open vSwitch
> in
> -each VM/host that they plan to run their containers on.
> -
> -.. _docker-overlay:
> -
> -The "overlay" mode
> -------------------
> -
> -.. note::
> -
> -  OVN in "overlay" mode needs a minimum Open vSwitch version of 2.5.
> -
> -1. Start the central components.
> -
> -  OVN architecture has a central component which stores your networking
> intent
> -  in a database. On one of your machines, with an IP Address of
> -  ``$CENTRAL_IP``, where you have installed and started Open vSwitch, you
> will
> -  need to start some central components.
> -
> -  Start ovn-northd daemon. This daemon translates networking intent from
> Docker
> -  stored in the OVN\_Northbound database to logical flows in
> ``OVN_Southbound``
> -  database. For example::
> -
> -      $ /usr/share/openvswitch/scripts/ovn-ctl start_northd
> -
> -  With Open vSwitch version of 2.7 or greater, you need to run the
> following
> -  additional commands (Please read the manpages of ovn-nb for more control
> -  on the types of connection allowed.) ::
> -
> -      $ ovn-nbctl set-connection ptcp:6641
> -      $ ovn-sbctl set-connection ptcp:6642
> -
> -2. One time setup
> -
> -   On each host, where you plan to spawn your containers, you will need
> to run
> -   the below command once. You may need to run it again if your OVS
> database
> -   gets cleared. It is harmless to run it again in any case::
> -
> -       $ ovs-vsctl set Open_vSwitch . \
> -           external_ids:ovn-remote="tcp:$CENTRAL_IP:6642" \
> -           external_ids:ovn-nb="tcp:$CENTRAL_IP:6641" \
> -           external_ids:ovn-encap-ip=$LOCAL_IP \
> -           external_ids:ovn-encap-type="$ENCAP_TYPE"
> -
> -   where:
> -
> -   ``$LOCAL_IP``
> -     is the IP address via which other hosts can reach this host.  This
> acts as
> -     your local tunnel endpoint.
> -
> -   ``$ENCAP_TYPE``
> -     is the type of tunnel that you would like to use for overlay
> networking.
> -     The options are ``geneve`` or ``stt``. Your kernel must have support
> for
> -     your chosen ``$ENCAP_TYPE``. Both ``geneve`` and ``stt`` are part of
> the
> -     Open vSwitch kernel module that is compiled from this repo. If you
> use the
> -     Open vSwitch kernel module from upstream Linux, you will need a
> minimum
> -     kernel version of 3.18 for ``geneve``. There is no ``stt`` support in
> -     upstream Linux. You can verify whether you have the support in your
> kernel
> -     as follows::
> -
> -         $ lsmod | grep $ENCAP_TYPE
> -
> -   In addition, each Open vSwitch instance in an OVN deployment needs a
> unique,
> -   persistent identifier, called the ``system-id``.  If you install OVS
> from
> -   distribution packaging for Open vSwitch (e.g. .deb or .rpm packages),
> or if
> -   you use the ovs-ctl utility included with Open vSwitch, it
> automatically
> -   configures a system-id.  If you start Open vSwitch manually, you
> should set
> -   one up yourself. For example::
> -
> -       $ id_file=/etc/openvswitch/system-id.conf
> -       $ test -e $id_file || uuidgen > $id_file
> -       $ ovs-vsctl set Open_vSwitch . external_ids:system-id=$(cat
> $id_file)
> -
> -3. Start the ``ovn-controller``.
> -
> -   You need to run the below command on every boot::
> -
> -       $ /usr/share/openvswitch/scripts/ovn-ctl start_controller
> -
> -4. Start the Open vSwitch network driver.
> -
> -   By default Docker uses Linux bridge for networking. But it has support
> for
> -   external drivers. To use Open vSwitch instead of the Linux bridge, you
> will
> -   need to start the Open vSwitch driver.
> -
> -   The Open vSwitch driver uses the Python's flask module to listen to
> Docker's
> -   networking api calls. So, if your host does not have Python's flask
> module,
> -   install it::
> -
> -       $ sudo pip install Flask
> -
> -   Start the Open vSwitch driver on every host where you plan to create
> your
> -   containers. Refer to the note on ``$OVS_PYTHON_LIBS_PATH`` that is
> used below
> -   at the end of this document::
> -
> -       $ PYTHONPATH=$OVS_PYTHON_LIBS_PATH ovn-docker-overlay-driver
> --detach
> -
> -   .. note::
> -
> -     The ``$OVS_PYTHON_LIBS_PATH`` variable should point to the directory
> where
> -     Open vSwitch Python modules are installed. If you installed Open
> vSwitch
> -     Python modules via the Debian package of ``python-openvswitch`` or
> via pip
> -     by running ``pip install ovs``, you do not need to specify the PATH.
> If
> -     you installed it by following the instructions in
> -     :doc:`/intro/install/general`, then you should specify the PATH. In
> this
> -     case, the PATH depends on the options passed to ``./configure``. It
> is
> -     usually either ``/usr/share/openvswitch/python`` or
> -     ``/usr/local/share/openvswitch/python``
> -
> -Docker has inbuilt primitives that closely match OVN's logical switches
> and
> -logical port concepts. Consult Docker's documentation for all the possible
> -commands. Here are some examples.
> -
> -Create a logical switch
> -~~~~~~~~~~~~~~~~~~~~~~~
> -
> -To create a logical switch with name 'foo', on subnet '192.168.1.0/24',
> run::
> -
> -    $ NID=`docker network create -d openvswitch --subnet=192.168.1.0/24
> foo`
> -
> -List all logical switches
> -~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -::
> -
> -    $ docker network ls
> -
> -You can also look at this logical switch in OVN's northbound database by
> -running the following command::
> -
> -    $ ovn-nbctl --db=tcp:$CENTRAL_IP:6640 ls-list
> -
> -Delete a logical switch
> -~~~~~~~~~~~~~~~~~~~~~~~
> -
> -::
> -
> -    $ docker network rm bar
> -
> -
> -Create a logical port
> -~~~~~~~~~~~~~~~~~~~~~
> -
> -Docker creates your logical port and attaches it to the logical network
> in a
> -single step. For example, to attach a logical port to network ``foo``
> inside
> -container busybox, run::
> -
> -    $ docker run -itd --net=foo --name=busybox busybox
> -
> -List all logical ports
> -~~~~~~~~~~~~~~~~~~~~~~
> -
> -Docker does not currently have a CLI command to list all logical ports
> but you
> -can look at them in the OVN database by running::
> -
> -    $ ovn-nbctl --db=tcp:$CENTRAL_IP:6640 lsp-list $NID
> -
> -Create and attach a logical port to a running container
> -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -::
> -
> -    $ docker network create -d openvswitch --subnet=192.168.2.0/24 bar
> -    $ docker network connect bar busybox
> -
> -Detach and delete a logical port from a running container
> -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -You can delete your logical port and detach it from a running container
> -by running:
> -
> -::
> -
> -    $ docker network disconnect bar busybox
> -
> -.. _docker-underlay:
> -
> -The "underlay" mode
> --------------------
> -
> -.. note::
> -
> -  This mode requires that you have a OpenStack setup pre-installed with
> -  OVN providing the underlay networking.
> -
> -1. One time setup
> -
> -   A OpenStack tenant creates a VM with a single network interface (or
> multiple)
> -   that belongs to management logical networks. The tenant needs to fetch
> the
> -   port-id associated with the interface via which he plans to send the
> container
> -   traffic inside the spawned VM. This can be obtained by running the
> below
> -   command to fetch the 'id' associated with the VM::
> -
> -       $ nova list
> -
> -   and then by running::
> -
> -       $ neutron port-list --device_id=$id
> -
> -   Inside the VM, download the OpenStack RC file that contains the tenant
> -   information (henceforth referred to as ``openrc.sh``). Edit the file
> and add the
> -   previously obtained port-id information to the file by appending the
> following
> -   line::
> -
> -       $ export OS_VIF_ID=$port_id
> -
> -   After this edit, the file will look something like::
> -
> -       #!/bin/bash
> -       export OS_AUTH_URL=http://10.33.75.122:5000/v2.0
> -       export OS_TENANT_ID=fab106b215d943c3bad519492278443d
> -       export OS_TENANT_NAME="demo"
> -       export OS_USERNAME="demo"
> -       export OS_VIF_ID=e798c371-85f4-4f2d-ad65-d09dd1d3c1c9
> -
> -2. Create the Open vSwitch bridge
> -
> -   If your VM has one ethernet interface (e.g.: 'eth0'), you will need to
> add
> -   that device as a port to an Open vSwitch bridge 'breth0' and move its
> IP
> -   address and route related information to that bridge. (If it has
> multiple
> -   network interfaces, you will need to create and attach an Open vSwitch
> -   bridge for the interface via which you plan to send your container
> -   traffic.)
> -
> -   If you use DHCP to obtain an IP address, then you should kill the DHCP
> -   client that was listening on the physical Ethernet interface (e.g.
> eth0) and
> -   start one listening on the Open vSwitch bridge (e.g. breth0).
> -
> -   Depending on your VM, you can make the above step persistent across
> reboots.
> -   For example, if your VM is Debian/Ubuntu-based, read
> -   `openvswitch-switch.README.Debian` found in `debian` folder. If your
> VM is
> -   RHEL-based, refer to :doc:`/intro/install/rhel`.
> -
> -3. Start the Open vSwitch network driver
> -
> -   The Open vSwitch driver uses the Python's flask module to listen to
> Docker's
> -   networking api calls. The driver also uses OpenStack's
> -   ``python-neutronclient`` libraries. If your host does not have Python's
> -   ``flask`` module or ``python-neutronclient`` you must install them. For
> -   example::
> -
> -       $ pip install python-neutronclient
> -       $ pip install Flask
> -
> -   Once installed, source the ``openrc`` file::
> -
> -       $ . ./openrc.sh
> -
> -   Start the network driver and provide your OpenStack tenant password
> when
> -   prompted::
> -
> -       $ PYTHONPATH=$OVS_PYTHON_LIBS_PATH ovn-docker-underlay-driver \
> -           --bridge breth0 --detach
> -
> -From here-on you can use the same Docker commands as described in
> -`docker-overlay`_.
> -
> -Refer to the ovs-architecture man pages (``man ovn-architecture``) to
> -understand OVN's architecture in detail.
> diff --git a/Documentation/howto/firewalld.rst
> b/Documentation/howto/firewalld.rst
> deleted file mode 100644
> index 0dc455ea8..000000000
> --- a/Documentation/howto/firewalld.rst
> +++ /dev/null
> @@ -1,107 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -===================================
> -Open Virtual Network With firewalld
> -===================================
> -
> -firewalld is a service that allows for easy administration of firewalls.
> OVN
> -ships with a set of service files that can be used with firewalld to allow
> -for remote connections to the northbound and southbound databases.
> -
> -This guide will describe how you can use these files with your existing
> -firewalld setup. Setup and administration of firewalld is outside the
> scope
> -of this document.
> -
> -Installation
> -------------
> -
> -If you have installed OVN from an RPM, then the service files for
> firewalld
> -will automatically be installed in ``/usr/lib/firewalld/services``.
> -Installation from RPM includes installation from the yum or dnf package
> -managers.
> -
> -If you have installed OVN from source, then from the top level source
> -directory, issue the following commands to copy the firewalld service
> files:
> -
> -::
> -
> -    $ cp rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml
> \
> -    /etc/firewalld/services/
> -    $ cp rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
> -    /etc/firewalld/services/
> -
> -
> -Activation
> -----------
> -
> -Assuming you are already running firewalld, you can issue the following
> -commands to enable the OVN services.
> -
> -On the central server (the one running ``ovn-northd``), issue the
> following::
> -
> -$ firewall-cmd --zone=public --add-service=ovn-central-firewall-service
> -
> -This will open TCP ports 6641 and 6642, allowing for remote connections
> to the
> -northbound and southbound databases.
> -
> -On the OVN hosts (the ones running ``ovn-controller``), issue the
> following::
> -
> -$ firewall-cmd --zone=public --add-service=ovn-host-firewall-service
> -
> -This will open UDP port 6081, allowing for geneve traffic to flow between
> the
> -controllers.
> -
> -Variations
> -----------
> -
> -When installing the XML service files, you have the choice of copying
> them to
> -``/etc/firewalld/services`` or ``/usr/lib/firewalld/services``. The
> former is
> -recommend since the latter can be overwritten if firewalld is upgraded.
> -
> -The above commands assumed your underlay network interfaces are in the
> -"public" firewalld zone. If your underlay network interfaces are in a
> separate
> -zone, then adjust the above commands accordingly.
> -
> -The ``--permanent`` option may be passed to the above firewall-cmd
> invocations
> -in order for the services to be permanently added to the firewalld
> -configuration. This way it is not necessary to re-issue the commands each
> -time the firewalld service restarts.
> -
> -The ovn-host-firewall-service only opens port 6081. This is because the
> -default protocol for OVN tunnels is geneve. If you are using a different
> -encapsulation protocol, you will need to modify the XML service file to
> open
> -the appropriate port(s). For VXLAN, open port 4789. For STT, open port
> 7471.
> -
> -Recommendations
> ----------------
> -
> -The firewalld service files included with the OVS repo are meant as a
> -convenience for firewalld users. All that the service files do is to open
> -the common ports used by OVN. No additional security is provided. To
> ensure a
> -more secure environment, it is a good idea to do the following
> -
> -* Use tools such as iptables or nftables to restrict access to known
> hosts.
> -* Use SSL for all remote connections to OVN databases.
> -* Use role-based access control for connections to the OVN southbound
> -  database.
> diff --git a/Documentation/howto/index.rst b/Documentation/howto/index.rst
> index 9a3487be3..60fb8a717 100644
> --- a/Documentation/howto/index.rst
> +++ b/Documentation/howto/index.rst
> @@ -50,12 +50,3 @@ OVS
>     sflow
>     dpdk
>
> -OVN
> ----
> -
> -.. toctree::
> -   :maxdepth: 1
> -
> -   docker
> -   openstack-containers
> -   firewalld
> diff --git a/Documentation/howto/openstack-containers.rst
> b/Documentation/howto/openstack-containers.rst
> deleted file mode 100644
> index 692fe25e5..000000000
> --- a/Documentation/howto/openstack-containers.rst
> +++ /dev/null
> @@ -1,135 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -================================================
> -Integration of Containers with OVN and OpenStack
> -================================================
> -
> -Isolation between containers is weaker than isolation between VMs, so some
> -environments deploy containers for different tenants in separate VMs as an
> -additional security measure.  This document describes creation of
> containers
> -inside VMs and how they can be made part of the logical networks
> securely.  The
> -created logical network can include VMs, containers and physical machines
> as
> -endpoints.  To better understand the proposed integration of containers
> with
> -OVN and OpenStack, this document describes the end to end workflow with an
> -example.
> -
> -* A OpenStack tenant creates a VM (say VM-A) with a single network
> interface
> -  that belongs to a management logical network.  The VM is meant to host
> -  containers.  OpenStack Nova chooses the hypervisor on which VM-A is
> created.
> -
> -* A Neutron port may have been created in advance and passed in to Nova
> with
> -  the request to create a new VM.  If not, Nova will issue a request to
> Neutron
> -  to create a new port.  The ID of the logical port from Neutron will
> also be
> -  used as the vif-id for the virtual network interface (VIF) of VM-A.
> -
> -* When VM-A is created on a hypervisor, its VIF gets added to the Open
> vSwitch
> -  integration bridge.  This creates a row in the Interface table of the
> -  ``Open_vSwitch`` database.  As explained in the :doc:`integration guide
> -  </topics/integration>`, the vif-id associated with the VM network
> interface
> -  gets added in the ``external_ids:iface-id`` column of the newly created
> row
> -  in the Interface table.
> -
> -* Since VM-A belongs to a logical network, it gets an IP address.  This IP
> -  address is used to spawn containers (either manually or through
> container
> -  orchestration systems) inside that VM and to monitor the health of the
> -  created containers.
> -
> -* The vif-id associated with the VM's network interface can be obtained by
> -  making a call to Neutron using tenant credentials.
> -
> -* This flow assumes a component called a "container network plugin".  If
> you
> -  take Docker as an example for containers, you could envision the plugin
> to be
> -  either a wrapper around Docker or a feature of Docker itself that
> understands
> -  how to perform part of this workflow to get a container connected to a
> -  logical network managed by Neutron.  The rest of the flow refers to this
> -  logical component that does not yet exist as the "container network
> plugin".
> -
> -* All the calls to Neutron will need tenant credentials.  These calls can
> -  either be made from inside the tenant VM as part of a container network
> -  plugin or from outside the tenant VM (if the tenant is not comfortable
> using
> -  temporary Keystone tokens from inside the tenant VMs).  For simplicity,
> this
> -  document explains the work flow using the former method.
> -
> -* The container hosting VM will need Open vSwitch installed in it.  The
> only
> -  work for Open vSwitch inside the VM is to tag network traffic coming
> from
> -  containers.
> -
> -* When a container needs to be created inside the VM with a container
> network
> -  interface that is expected to be attached to a particular logical
> switch, the
> -  network plugin in that VM chooses any unused VLAN (This VLAN tag only
> needs
> -  to be unique inside that VM.  This limits the number of container
> interfaces
> -  to 4096 inside a single VM).  This VLAN tag is stripped out in the
> hypervisor
> -  by OVN and is only useful as a context (or metadata) for OVN.
> -
> -* The container network plugin then makes a call to Neutron to create a
> logical
> -  port.  In addition to all the inputs that a call to create a port in
> Neutron
> -  that are currently needed, it sends the vif-id and the VLAN tag as
> inputs.
> -
> -* Neutron in turn will verify that the vif-id belongs to the tenant in
> question
> -  and then uses the OVN specific plugin to create a new row in the
> -  Logical_Switch_Port table of the OVN Northbound Database.  Neutron
> responds
> -  back with an IP address and MAC address for that network interface.  So
> -  Neutron becomes the IPAM system and provides unique IP and MAC addresses
> -  across VMs and containers in the same logical network.
> -
> -* The Neutron API call above to create a logical port for the container
> could
> -  add a relatively significant amount of time to container creation.
> However,
> -  an optimization is possible here.  Logical ports could be created in
> advance
> -  and reused by the container system doing container orchestration.
> Additional
> -  Neutron API calls would only be needed if the port needs to be attached
> to a
> -  different logical network.
> -
> -* When a container is eventually deleted, the network plugin in that VM
> may
> -  make a call to Neutron to delete that port.  Neutron in turn will
> delete the
> -  entry in the ``Logical_Switch_Port`` table of the OVN Northbound
> Database.
> -
> -As an example, consider Docker containers.  Since Docker currently does
> not
> -have a network plugin feature, this example uses a hypothetical wrapper
> around
> -Docker to make calls to Neutron.
> -
> -* Create a Logical switch::
> -
> -      $ ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f create network
> LS1
> -
> -  The above command will make a call to Neutron with the credentials to
> create
> -  a logical switch.  The above is optional if the logical switch has
> already
> -  been created from outside the VM.
> -
> -* List networks available to the tenant::
> -
> -      $ ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f list networks
> -
> -* Create a container and attach a interface to the previously created
> switch as
> -  a logical port::
> -
> -      $ ovn-docker --cred=cca86bd13a564ac2a63ddf14bf45d37f
> --vif-id=$VIF_ID \
> -          --network=LS1 run -d --net=none ubuntu:14.04 /bin/sh -c \
> -          "while true; do echo hello world; sleep 1; done"
> -
> -  The above command will make a call to Neutron with all the inputs it
> -  currently needs to create a logical port.  In addition, it passes the
> $VIF_ID
> -  and a unused VLAN.  Neutron will add that information in OVN and return
> back
> -  a MAC address and IP address for that interface.  ovn-docker will then
> create
> -  a veth pair, insert one end inside the container as 'eth0' and the
> other end
> -  as a port of a local OVS bridge as an access port of the chosen VLAN.
> diff --git a/Documentation/index.rst b/Documentation/index.rst
> index bace34dbf..f18f8df1c 100644
> --- a/Documentation/index.rst
> +++ b/Documentation/index.rst
> @@ -33,22 +33,20 @@ How the Documentation is Organised
>  The Open vSwitch documentation is organised into multiple sections:
>
>  - :doc:`Installation guides </intro/install/index>` guide you through
> -  installing Open vSwitch (OVS) and Open Virtual Network (OVN) on a
> variety of
> -  different platforms
> +  installing Open vSwitch (OVS) on a variety of different platforms
>  - :doc:`Tutorials </tutorials/index>` take you through a series of steps
> to
> -  configure OVS and OVN in sandboxed environments
> -- :doc:`Topic guides </topics/index>` provide a high level overview of
> OVS and
> -  OVN internals and operation
> -- :doc:`How-to guides </howto/index>` are recipes or use-cases for OVS
> and OVN.
> +  configure OVS in sandboxed environments
> +- :doc:`Topic guides </topics/index>` provide a high level overview of OVS
> +  internals and operation
> +- :doc:`How-to guides </howto/index>` are recipes or use-cases for OVS.
>    They are more advanced than the tutorials.
>  - :doc:`Frequently Asked Questions </faq/index>` provide general insight
> into
> -  a variety of topics related to configuration and operation of OVS and
> OVN.
> +  a variety of topics related to configuration and operation of OVS.
>
>  First Steps
>  -----------
>
> -Getting started with Open vSwitch (OVS) or Open Virtual Network (OVN) for
> Open
> -vSwitch? Start here.
> +Getting started with Open vSwitch (OVS)? Start here.
>
>  - **Overview:** :doc:`intro/what-is-ovs` |
>    :doc:`intro/why-ovs`
> @@ -64,12 +62,8 @@ vSwitch? Start here.
>
>  - **Tutorials:** :doc:`tutorials/faucet` |
>    :doc:`tutorials/ovs-advanced` |
> -  :doc:`tutorials/ovn-sandbox` |
> -  :doc:`tutorials/ovn-openstack` |
>    :doc:`tutorials/ovs-conntrack` |
>    :doc:`tutorials/ipsec` |
> -  :doc:`tutorials/ovn-ipsec` |
> -  :doc:`tutorials/ovn-rbac`
>
>  Deeper Dive
>  -----------
> diff --git a/Documentation/intro/install/fedora.rst
> b/Documentation/intro/install/fedora.rst
> index 4e1a99766..f11d05a01 100644
> --- a/Documentation/intro/install/fedora.rst
> +++ b/Documentation/intro/install/fedora.rst
> @@ -119,16 +119,6 @@ tests.  This can take several minutes.
>
>      $ make rpm-fedora RPMBUILD_OPT="--with check"
>
> -To build OVN RPMs, execute the following from the directory in which
> -`./configure` was executed:
> -
> -::
> -
> -    $ make rpm-fedora-ovn
> -
> -This will create the RPMs `ovn`, `ovn-common`, `ovn-central`, `ovn-host`,
> -`ovn-docker` and `ovn-vtep`.
> -
>
>  Kernel OVS Tree Datapath RPM
>  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> @@ -165,8 +155,6 @@ In most cases only the `openvswitch` RPM will need to
> be installed. The
>  `openvswitch-debuginfo` RPMs are optional unless required for a specific
>  purpose.
>
> -The `ovn-*` packages are only needed when using OVN.
> -
>  Refer to the `RHEL README`__ for additional usage and configuration
>  information.
>
> diff --git a/Documentation/intro/install/index.rst
> b/Documentation/intro/install/index.rst
> index c27a9c9d1..586ced95f 100644
> --- a/Documentation/intro/install/index.rst
> +++ b/Documentation/intro/install/index.rst
> @@ -62,14 +62,6 @@ provided below.
>     fedora
>     rhel
>
> -Upgrades
> ---------
> -
> -.. toctree::
> -   :maxdepth: 2
> -
> -   ovn-upgrades
> -
>  Others
>  ------
>
> diff --git a/Documentation/intro/install/ovn-upgrades.rst
> b/Documentation/intro/install/ovn-upgrades.rst
> deleted file mode 100644
> index 3e6cd984e..000000000
> --- a/Documentation/intro/install/ovn-upgrades.rst
> +++ /dev/null
> @@ -1,115 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -============
> -OVN Upgrades
> -============
> -
> -Since OVN is a distributed system, special consideration must be given to
> -the process used to upgrade OVN across a deployment.  This document
> discusses
> -the recommended upgrade process.
> -
> -Release Notes
> --------------
> -
> -You should always check the OVS and OVN release notes (NEWS file) for any
> -release specific notes on upgrades.
> -
> -OVS
> ----
> -
> -OVN depends on and is included with OVS.  It's expected that OVS and OVN
> are
> -upgraded together, partly for convenience.  OVN is included in OVS
> releases
> -so it's easiest to upgrade them together.  OVN may also make use of new
> -features of OVS only available in that release.
> -
> -Upgrade ovn-controller
> -----------------------
> -
> -You should start by upgrading ovn-controller on each host it's running on.
> -First, you upgrade the OVS and OVN packages.  Then, restart the
> -ovn-controller service.  You can restart with ovn-ctl::
> -
> -    $ sudo /usr/share/openvswitch/scripts/ovn-ctl restart_controller
> -
> -or with systemd::
> -
> -    $ sudo systemd restart ovn-controller
> -
> -Upgrade OVN Databases and ovn-northd
> -------------------------------------
> -
> -The OVN databases and ovn-northd should be upgraded next.  Since
> ovn-controller
> -has already been upgraded, it will be ready to operate on any new
> functionality
> -specified by the database or logical flows created by ovn-northd.
> -
> -Upgrading the OVN packages installs everything needed for an upgrade.
> The only
> -step required after upgrading the packages is to restart ovn-northd, which
> -automatically restarts the databases and upgrades the database schema, as
> well.
> -
> -You may perform this restart using the ovn-ctl script::
> -
> -    $ sudo /usr/share/openvswitch/scripts/ovn-ctl restart_northd
> -
> -or if you're using a Linux distribution with systemd::
> -
> -    $ sudo systemctl restart ovn-northd
> -
> -Schema Change
> -^^^^^^^^^^^^^
> -
> -During database upgrading, if there is schema change, the DB file will be
> -converted to the new schema automatically, if the schema change is
> backward
> -compatible.  OVN tries the best to keep the DB schemas backward
> compatible.
> -
> -However, there can be situations that an incompatible change is
> reasonble.  An
> -example of such case is to add constraints in the table to ensure
> correctness.
> -If there were already data that violates the new constraints got added
> somehow,
> -it will result in DB upgrade failures.  In this case, user should manually
> -correct data using ovn-nbctl (for north-bound DB) or ovn-sbctl (for south-
> -bound DB), and then upgrade again following previous steps.  Below is a
> list
> -of known impactible schema changes and how to fix when error encountered.
> -
> -#. Release 2.11: index [type, ip] added for Encap table of south-bound DB
> to
> -   prevent duplicated IPs being used for same tunnel type.  If there are
> -   duplicated data added already (e.g. due to improper chassis
> management),
> -   a convenient way to fix is to find the chassis that is using the IP
> -   with command::
> -
> -    $ ovn-sbctl show
> -
> -   Then delete the chassis with command::
> -
> -    $ ovn-sbctl chassis-del <chassis>
> -
> -
> -Upgrading OVN Integration
> --------------------------
> -
> -Lastly, you may also want to upgrade integration with OVN that you may be
> -using.  For example, this could be the OpenStack Neutron driver or
> -ovn-kubernetes.
> -
> -OVN's northbound database schema is a backwards compatible interface, so
> -you should be able to safely complete an OVN upgrade before upgrading
> -any integration in use.
> diff --git a/Documentation/ref/ovs-sim.1.rst
> b/Documentation/ref/ovs-sim.1.rst
> index 4382598e1..f59cd7af7 100644
> --- a/Documentation/ref/ovs-sim.1.rst
> +++ b/Documentation/ref/ovs-sim.1.rst
> @@ -142,103 +142,3 @@ with ``main`` directly.
>      must already have been created by a previous invocation of
>      ``net_add``. The default sandbox must not be ``main``.
>
> -OVN Commands
> -------------
> -
> -These commands interact with OVN, the Open Virtual Network.
> -
> -``ovn_start`` [*options*]
> -    Creates and initializes the central OVN databases (both
> -    ``ovn-sb(5)`` and ``ovn-nb(5)``) and starts an instance of
> -    ``ovsdb-server`` for each one.  Also starts an instance of
> -    ``ovn-northd``.
> -
> -    The following options are available:
> -
> -       ``--nbdb-model`` *model*
> -           Uses the given database model for the northbound database.
> -           The *model* may be ``standalone`` (the default), ``backup``,
> -           or ``clustered``.
> -
> -       ``--nbdb-servers`` *n*
> -           For a clustered northbound database, the number of servers in
> -           the cluster.  The default is 3.
> -
> -       ``--sbdb-model`` *model*
> -           Uses the given database model for the southbound database.
> -           The *model* may be ``standalone`` (the default), ``backup``,
> -           or ``clustered``.
> -
> -       ``--sbdb-servers`` *n*
> -           For a clustered southbound database, the number of servers in
> -           the cluster.  The default is 3.
> -
> -``ovn_attach`` *network* *bridge* *ip* [*masklen*]
> -    First, this command attaches bridge to interconnection network
> -    network, just like ``net_attach`` *network* *bridge*.  Second, it
> -    configures (simulated) IP address *ip* (with network mask length
> -    *masklen*, which defaults to 24) on *bridge*. Finally, it
> -    configures the Open vSwitch database to work with OVN and starts
> -    ``ovn-controller``.
> -
> -Examples
> -========
> -
> -The following creates a pair of Open vSwitch instances ``hv0`` and
> -``hv1``, adds a port named ``vif0`` or ``vif1``, respectively, to each
> -one, and then connects the two through an interconnection network
> -``n1``::
> -
> -    net_add n1
> -    for i in 0 1; do
> -        sim_add hv$i
> -        as hv$i ovs-vsctl add-br br0 -- add-port br0 vif$i
> -        as hv$i net_attach n1 br0
> -    done
> -
> -Here’s an extended version that also starts OVN::
> -
> -    ovn_start
> -    ovn-nbctl ls-add lsw0
> -    net_add n1
> -    for i in 0 1; do
> -        sim_add hv$i
> -        as hv$i
> -        ovs-vsctl add-br br-phys
> -        ovn_attach n1 br-phys 192.168.0.`expr $i + 1`
> -        ovs-vsctl add-port br-int vif$i -- set Interface vif$i
> external-ids:iface-id=lp$i
> -        ovn-nbctl lsp-add lsw0 lp$i
> -        ovn-nbctl lsp-set-addresses lp$i f0:00:00:00:00:0$i
> -    done
> -
> -Here’s a primitive OVN "scale test" (adjust the scale by changing
> -``n`` in the first line::
> -
> -    n=200; export n
> -    ovn_start --sbdb-model=clustered
> -    net_add n1
> -    ovn-nbctl ls-add br0
> -    for i in `seq $n`; do
> -        (sim_add hv$i
> -        as hv$i
> -        ovs-vsctl add-br br-phys
> -        y=$(expr $i / 256)
> -        x=$(expr $i % 256)
> -        ovn_attach n1 br-phys 192.168.$y.$x
> -        ovs-vsctl add-port br-int vif$i -- set Interface vif$i
> external-ids:iface-id=lp$i) &
> -        case $i in
> -            *50|*00) echo $i; wait ;;
> -        esac
> -    done
> -    wait
> -    for i in `seq $n`; do
> -        yy=$(printf %02x $(expr $i / 256))
> -        xx=$(printf $02x $(expr $i % 256))
> -        ovn-nbctl lsp-add br0 lp$i
> -        ovn-nbctl lsp-set-addresses lp$i f0:00:00:00:$yy:$xx
> -    done
> -
> -When the scale test has finished initializing, you can watch the
> -logical ports come up with a command like this::
> -
> -    watch 'for i in `seq $n`; do if test `ovn-nbctl lsp-get-up lp$i` !=
> up; then echo $i; fi; done'
> diff --git a/Documentation/topics/high-availability.rst
> b/Documentation/topics/high-availability.rst
> deleted file mode 100644
> index a5cb76383..000000000
> --- a/Documentation/topics/high-availability.rst
> +++ /dev/null
> @@ -1,440 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -==================================
> -OVN Gateway High Availability Plan
> -==================================
> -
> -::
> -
> -    OVN Gateway
> -
> -         +---------------------------+
> -         |                           |
> -         |     External Network      |
> -         |                           |
> -         +-------------^-------------+
> -                       |
> -                       |
> -                 +-----------+
> -                 |           |
> -                 |  Gateway  |
> -                 |           |
> -                 +-----------+
> -                       ^
> -                       |
> -                       |
> -         +-------------v-------------+
> -         |                           |
> -         |    OVN Virtual Network    |
> -         |                           |
> -         +---------------------------+
> -
> -The OVN gateway is responsible for shuffling traffic between the tunneled
> -overlay network (governed by ovn-northd), and the legacy physical
> network.  In
> -a naive implementation, the gateway is a single x86 server, or hardware
> VTEP.
> -For most deployments, a single system has enough forwarding capacity to
> service
> -the entire virtualized network, however, it introduces a single point of
> -failure.  If this system dies, the entire OVN deployment becomes
> unavailable.
> -To mitigate this risk, an HA solution is critical -- by spreading
> -responsibility across multiple systems, no single server failure can take
> down
> -the network.
> -
> -An HA solution is both critical to the manageability of the system, and
> -extremely difficult to get right.  The purpose of this document, is to
> propose
> -a plan for OVN Gateway High Availability which takes into account our past
> -experience building similar systems.  It should be considered a fluid
> changing
> -proposal, not a set-in-stone decree.
> -
> -.. note::
> -    This document describes a range of options OVN could take to provide
> -    high availability for gateways.  The current implementation provides
> L3
> -    gateway high availability by the "Router Specific Active/Backup"
> -    approach described in this document.
> -
> -Basic Architecture
> -------------------
> -
> -In an OVN deployment, the set of hypervisors and network elements
> operating
> -under the guidance of ovn-northd are in what's called "logical space".
> These
> -servers use VXLAN, STT, or Geneve to communicate, oblivious to the
> details of
> -the underlying physical network.  When these systems need to communicate
> with
> -legacy networks, traffic must be routed through a Gateway which
> translates from
> -OVN controlled tunnel traffic, to raw physical network traffic.
> -
> -Since the gateway is typically the only system with a connection to the
> -physical network all traffic between logical space and the WAN must travel
> -through it.  This makes it a critical single point of failure -- if the
> gateway
> -dies, communication with the WAN ceases for all systems in logical space.
> -
> -To mitigate this risk, multiple gateways should be run in a "High
> Availability
> -Cluster" or "HA Cluster".  The HA cluster will be responsible for
> performing
> -the duties of a gateways,  while being able to recover gracefully from
> -individual member failures.
> -
> -::
> -
> -    OVN Gateway HA Cluster
> -
> -             +---------------------------+
> -             |                           |
> -             |     External Network      |
> -             |                           |
> -             +-------------^-------------+
> -                           |
> -                           |
> -    +----------------------v----------------------+
> -    |                                             |
> -    |          High Availability Cluster          |
> -    |                                             |
> -    | +-----------+  +-----------+  +-----------+ |
> -    | |           |  |           |  |           | |
> -    | |  Gateway  |  |  Gateway  |  |  Gateway  | |
> -    | |           |  |           |  |           | |
> -    | +-----------+  +-----------+  +-----------+ |
> -    +----------------------^----------------------+
> -                           |
> -                           |
> -             +-------------v-------------+
> -             |                           |
> -             |    OVN Virtual Network    |
> -             |                           |
> -             +---------------------------+
> -
> -L2 vs L3 High Availability
> -~~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -In order to achieve this goal, there are two broad approaches one can
> take.
> -The HA cluster can appear to the network like a giant Layer 2 Ethernet
> Switch,
> -or like a giant IP Router. These approaches are called L2HA, and L3HA
> -respectively.  L2HA allows ethernet broadcast domains to extend into
> logical
> -space, a significant advantage, but this comes at a cost.  The need to
> avoid
> -transient L2 loops during failover significantly complicates their
> design.  On
> -the other hand, L3HA works for most use cases, is simpler, and fails more
> -gracefully.  For these reasons, it is suggested that OVN supports an L3HA
> -model, leaving L2HA for future work (or third party VTEP providers).  Both
> -models are discussed further below.
> -
> -L3HA
> -----
> -
> -In this section, we'll work through a basic simple L3HA implementation,
> on top
> -of which we'll gradually build more sophisticated features explaining
> their
> -motivations and implementations as we go.
> -
> -Naive active-backup
> -~~~~~~~~~~~~~~~~~~~
> -
> -Let's assume that there are a collection of logical routers which a
> tenant has
> -asked for, our task is to schedule these logical routers on one of N
> gateways,
> -and gracefully redistribute the routers on gateways which have failed.
> The
> -absolute simplest way to achieve this is what we'll call
> "naive-active-backup".
> -
> -::
> -
> -    Naive Active Backup HA Implementation
> -
> -    +----------------+   +----------------+
> -    | Leader         |   | Backup         |
> -    |                |   |                |
> -    |      A B C     |   |                |
> -    |                |   |                |
> -    +----+-+-+-+----++   +-+--------------+
> -         ^ ^ ^ ^    |      |
> -         | | | |    |      |
> -         | | | |  +-+------+---+
> -         + + + +  | ovn-northd |
> -         Traffic  +------------+
> -
> -In a naive active-backup, one of the Gateways is chosen (arbitrarily) as a
> -leader.  All logical routers (A, B, C in the figure), are scheduled on
> this
> -leader gateway and all traffic flows through it.  ovn-northd monitors this
> -gateway via OpenFlow echo requests (or some equivalent), and if the
> gateway
> -dies, it recreates the routers on one of the backups.
> -
> -This approach basically works in most cases and should likely be the
> starting
> -point for OVN -- it's strictly better than no HA solution and is a good
> -foundation for more sophisticated solutions.  That said, it's not without
> it's
> -limitations. Specifically, this approach doesn't coordinate with the
> physical
> -network to minimize disruption during failures, and it tightly couples
> failover
> -to ovn-northd (we'll discuss why this is bad in a bit), and wastes
> resources by
> -leaving backup gateways completely unutilized.
> -
> -Router Failover
> -+++++++++++++++
> -
> -When ovn-northd notices the leader has died and decides to migrate
> routers to a
> -backup gateway, the physical network has to be notified to direct traffic
> to
> -the new gateway.  Otherwise, traffic could be blackholed for longer than
> -necessary making failovers worse than they need to be.
> -
> -For now, let's assume that OVN requires all gateways to be on the same IP
> -subnet on the physical network.  If this isn't the case, gateways would
> need to
> -participate in routing protocols to orchestrate failovers, something
> which is
> -difficult and out of scope of this document.
> -
> -Since all gateways are on the same IP subnet, we simply need to worry
> about
> -updating the MAC learning tables of the Ethernet switches on that subnet.
> -Presumably, they all have entries for each logical router pointing to the
> old
> -leader.  If these entries aren't updated, all traffic will be sent to the
> (now
> -defunct) old leader, instead of the new one.
> -
> -In order to mitigate this issue, it's recommended that the new gateway
> sends a
> -Reverse ARP (RARP) onto the physical network for each logical router it
> now
> -controls.  A Reverse ARP is a benign protocol used by many hypervisors
> when
> -virtual machines migrate to update L2 forwarding tables.  In this case,
> the
> -ethernet source address of the RARP is that of the logical router it
> -corresponds to, and its destination is the broadcast address.  This
> causes the
> -RARP to travel to every L2 switch in the broadcast domain, updating
> forwarding
> -tables accordingly.  This strategy is recommended in all failover
> mechanisms
> -discussed in this document -- when a router newly boots on a new leader,
> it
> -should RARP its MAC address.
> -
> -Controller Independent Active-backup
> -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -::
> -
> -    Controller Independent Active-Backup Implementation
> -
> -    +----------------+   +----------------+
> -    | Leader         |   | Backup         |
> -    |                |   |                |
> -    |      A B C     |   |                |
> -    |                |   |                |
> -    +----------------+   +----------------+
> -         ^ ^ ^ ^
> -         | | | |
> -         | | | |
> -         + + + +
> -         Traffic
> -
> -The fundamental problem with naive active-backup, is it tightly couples
> the
> -failover solution to ovn-northd.  This can significantly increase
> downtime in
> -the event of a failover as the (often already busy) ovn-northd controller
> has
> -to recompute state for the new leader. Worse, if ovn-northd goes down, we
> can't
> -perform gateway failover at all.  This violates the principle that control
> -plane outages should have no impact on dataplane functionality.
> -
> -In a controller independent active-backup configuration, ovn-northd is
> -responsible for initial configuration while the HA cluster is responsible
> for
> -monitoring the leader, and failing over to a backup if necessary.
> ovn-northd
> -sets HA policy, but doesn't actively participate when failovers occur.
> -
> -Of course, in this model, ovn-northd is not without some responsibility.
> Its
> -role is to pre-plan what should happen in the event of a failure, leaving
> it to
> -the individual switches to execute this plan.  It does this by assigning
> each
> -gateway a unique leadership priority.  Once assigned, it communicates this
> -priority to each node it controls.  Nodes use the leadership priority to
> -determine which gateway in the cluster is the active leader by using a
> simple
> -metric: the leader is the gateway that is healthy, with the highest
> priority.
> -If that gateway goes down, leadership falls to the next highest priority,
> and
> -conversely, if a new gateway comes up with a higher priority, it takes
> over
> -leadership.
> -
> -Thus, in this model, leadership of the HA cluster is determined simply by
> the
> -status of its members.  Therefore if we can communicate the status of each
> -gateway to each transport node, they can individually figure out which is
> the
> -leader, and direct traffic accordingly.
> -
> -Tunnel Monitoring
> -+++++++++++++++++
> -
> -Since in this model leadership is determined exclusively by the health
> status
> -of member gateways, a key problem is how do we communicate this
> information to
> -the relevant transport nodes.  Luckily, we can do this fairly cheaply
> using
> -tunnel monitoring protocols like BFD.
> -
> -The basic idea is pretty straightforward.  Each transport node maintains a
> -tunnel to every gateway in the HA cluster (not just the leader).  These
> tunnels
> -are monitored using the BFD protocol to see which are alive.  Given this
> -information, hypervisors can trivially compute the highest priority live
> -gateway, and thus the leader.
> -
> -In practice, this leadership computation can be performed trivially using
> the
> -bundle or group action.  Rather than using OpenFlow to simply output to
> the
> -leader, all gateways could be listed in an active-backup bundle action
> ordered
> -by their priority.  The bundle action will automatically take into
> account the
> -tunnel monitoring status to output the packet to the highest priority live
> -gateway.
> -
> -Inter-Gateway Monitoring
> -++++++++++++++++++++++++
> -
> -One somewhat subtle aspect of this model, is that failovers are not
> globally
> -atomic.  When a failover occurs, it will take some time for all
> hypervisors to
> -notice and adjust accordingly.  Similarly, if a new high priority Gateway
> comes
> -up, it may take some time for all hypervisors to switch over to the new
> leader.
> -In order to avoid confusing the physical network, under these
> circumstances
> -it's important for the backup gateways to drop traffic they've received
> -erroneously.  In order to do this, each Gateway must know whether or not
> it is,
> -in fact active.  This can be achieved by creating a mesh of tunnels
> between
> -gateways.  Each gateway monitors the other gateways its cluster to
> determine
> -which are alive, and therefore whether or not that gateway happens to be
> the
> -leader.  If leading, the gateway forwards traffic normally, otherwise it
> drops
> -all traffic.
> -
> -We should note that this method works well under the assumption that there
> -are no inter-gateway connectivity failures, in such case this method
> would fail
> -to elect a single master. The simplest example is two gateways which stop
> seeing
> -each other but can still reach the hypervisors. Protocols like VRRP or
> CARP
> -have the same issue. A mitigation for this type of failure mode could be
> -achieved by having all network elements (hypervisors and gateways)
> periodically
> -share their link status to other endpoints.
> -
> -Gateway Leadership Resignation
> -++++++++++++++++++++++++++++++
> -
> -Sometimes a gateway may be healthy, but still may not be suitable to lead
> the
> -HA cluster.  This could happen for several reasons including:
> -
> -* The physical network is unreachable
> -
> -* BFD (or ping) has detected the next hop router is unreachable
> -
> -* The Gateway recently booted and isn't fully configured
> -
> -In this case, the Gateway should resign leadership by holding its tunnels
> down
> -using the ``other_config:cpath_down`` flag.  This indicates to
> participating
> -hypervisors and Gateways that this gateway should be treated as if it's
> down,
> -even though its tunnels are still healthy.
> -
> -Router Specific Active-Backup
> -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -::
> -
> -    Router Specific Active-Backup
> -
> -    +----------------+ +----------------+
> -    |                | |                |
> -    |      A C       | |     B D E      |
> -    |                | |                |
> -    +----------------+ +----------------+
> -                  ^ ^   ^ ^
> -                  | |   | |
> -                  | |   | |
> -                  + +   + +
> -                   Traffic
> -
> -Controller independent active-backup is a great advance over naive
> -active-backup, but it still has one glaring problem -- it under-utilizes
> the
> -backup gateways.  In ideal scenario, all traffic would split evenly among
> the
> -live set of gateways.  Getting all the way there is somewhat tricky, but
> as a
> -step in the direction, one could use the "Router Specific Active-Backup"
> -algorithm.  This algorithm looks a lot like active-backup on a per logical
> -router basis, with one twist.  It chooses a different active Gateway for
> each
> -logical router.  Thus, in situations where there are several logical
> routers,
> -all with somewhat balanced load, this algorithm performs better.
> -
> -Implementation of this strategy is quite straightforward if built on top
> of
> -basic controller independent active-backup.  On a per logical router
> basis, the
> -algorithm is the same, leadership is determined by the liveness of the
> -gateways.  The key difference here is that the gateways must have a
> different
> -leadership priority for each logical router.  These leadership priorities
> can
> -be computed by ovn-northd just as they had been in the controller
> independent
> -active-backup model.
> -
> -Once we have these per logical router priorities, they simply need be
> -communicated to the members of the gateway cluster and the hypervisors.
> The
> -hypervisors in particular, need simply have an active-backup bundle
> action (or
> -group action) per logical router listing the gateways in priority order
> for
> -*that router*, rather than having a single bundle action shared for all
> the
> -routers.
> -
> -Additionally, the gateways need to be updated to take into account
> individual
> -router priorities.  Specifically, each gateway should drop traffic of
> backup
> -routers it's running, and forward traffic of active gateways, instead of
> simply
> -dropping or forwarding everything.  This should likely be done by having
> -ovn-controller recompute OpenFlow for the gateway, though other options
> exist.
> -
> -The final complication is that ovn-northd's logic must be updated to
> choose
> -these per logical router leadership priorities in a more sophisticated
> manner.
> -It doesn't matter much exactly what algorithm it chooses to do this,
> beyond
> -that it should provide good balancing in the common case.  I.E. each
> logical
> -routers priorities should be different enough that routers balance to
> different
> -gateways even when failures occur.
> -
> -Preemption
> -++++++++++
> -
> -In an active-backup setup, one issue that users will run into is that of
> -gateway leader preemption.  If a new Gateway is added to a cluster, or
> for some
> -reason an existing gateway is rebooted, we could end up in a situation
> where
> -the newly activated gateway has higher priority than any other in the HA
> -cluster.  In this case, as soon as that gateway appears, it will preempt
> -leadership from the currently active leader causing an unnecessary
> failover.
> -Since failover can be quite expensive, this preemption may be undesirable.
> -
> -The controller can optionally avoid preemption by cleverly tweaking the
> -leadership priorities.  For each router, new gateways should be assigned
> -priorities that put them second in line or later when they eventually
> come up.
> -Furthermore, if a gateway goes down for a significant period of time, its
> old
> -leadership priorities should be revoked and new ones should be assigned
> as if
> -it's a brand new gateway.  Note that this should only happen if a gateway
> has
> -been down for a while (several minutes), otherwise a flapping gateway
> could
> -have wide ranging, unpredictable, consequences.
> -
> -Note that preemption avoidance should be optional depending on the
> deployment.
> -One necessarily sacrifices optimal load balancing to satisfy these
> requirements
> -as new gateways will get no traffic on boot.  Thus, this feature
> represents a
> -trade-off which must be made on a per installation basis.
> -
> -Fully Active-Active HA
> -~~~~~~~~~~~~~~~~~~~~~~
> -
> -::
> -
> -    Fully Active-Active HA
> -
> -    +----------------+ +----------------+
> -    |                | |                |
> -    |   A B C D E    | |    A B C D E   |
> -    |                | |                |
> -    +----------------+ +----------------+
> -                  ^ ^   ^ ^
> -                  | |   | |
> -                  | |   | |
> -                  + +   + +
> -                   Traffic
> -
> -The final step in L3HA is to have true active-active HA.  In this
> scenario each
> -router has an instance on each Gateway, and a mechanism similar to ECMP
> is used
> -to distribute traffic evenly among all instances.  This mechanism would
> require
> -Gateways to participate in routing protocols with the physical network to
> -attract traffic and alert of failures.  It is out of scope of this
> document,
> -but may eventually be necessary.
> -
> -L2HA
> -----
> -
> -L2HA is very difficult to get right.  Unlike L3HA, where the consequences
> of
> -problems are minor, in L2HA if two gateways are both transiently active,
> an L2
> -loop triggers and a broadcast storm results.  In practice to get around
> this,
> -gateways end up implementing an overly conservative "when in doubt drop
> all
> -traffic" policy, or they implement something like MLAG.
> -
> -MLAG has multiple gateways work together to pretend to be a single L2
> switch
> -with a large LACP bond.  In principle, it's the right solution to the
> problem
> -as it solves the broadcast storm problem, and has been deployed
> successfully in
> -other contexts.  That said, it's difficult to get right and not
> recommended.
> diff --git a/Documentation/topics/index.rst
> b/Documentation/topics/index.rst
> index 057649dd7..fcb741637 100644
> --- a/Documentation/topics/index.rst
> +++ b/Documentation/topics/index.rst
> @@ -27,7 +27,7 @@
>  Deep Dive
>  =========
>
> -How Open vSwitch and OVN are implemented and, where necessary, why it was
> +How Open vSwitch is implemented and, where necessary, why it was
>  implemented that way.
>
>  OVS
> @@ -52,19 +52,3 @@ OVS
>     tracing
>     idl-compound-indexes
>
> -OVN
> ----
> -
> -.. toctree::
> -   :maxdepth: 2
> -
> -   high-availability
> -   role-based-access-control
> -   ovn-news-2.8
> -
> -.. list-table::
> -
> -   * - ovn-architecture(7)
> -     - `(pdf) <
> http://openvswitch.org/support/dist-docs/ovn-architecture.7.pdf>`__
> -     - `(html) <
> http://openvswitch.org/support/dist-docs/ovn-architecture.7.html>`__
> -     - `(plain text) <
> http://openvswitch.org/support/dist-docs/ovn-architecture.7.txt>`__
> diff --git a/Documentation/topics/ovn-news-2.8.rst
> b/Documentation/topics/ovn-news-2.8.rst
> deleted file mode 100644
> index fae0a4278..000000000
> --- a/Documentation/topics/ovn-news-2.8.rst
> +++ /dev/null
> @@ -1,278 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -===============================
> -What's New with OVS and OVN 2.8
> -===============================
> -
> -This document is about what was added in Open vSwitch 2.8, which was
> released
> -at the end of August 2017, concentrating on the new features in OVN.  It
> also
> -covers some of what is coming up in Open vSwitch and OVN 2.9, which is
> due to
> -be released in February 2018.  OVN has many features, and this document
> does
> -not cover every new or enhanced feature (but contributions are welcome).
> -
> -This document assumes a basic familiarity with Open vSwitch, OVN, and
> their
> -associated tools.  For more information, please refer to the Open vSwitch
> and
> -OVN documentation, such as the ``ovn-architecture``\(7) manpage.
> -
> -Debugging and Troubleshooting
> ------------------------------
> -
> -Before version 2.8, Open vSwitch command-line tools were far more painful
> to
> -use than they needed to be.  This section covers the improvements made to
> the
> -CLI in the 2.8 release.
> -
> -User-Hostile UUIDs
> -~~~~~~~~~~~~~~~~~~
> -
> -The OVN CLI, through ``ovn-nbctl``, ``ovn-nbctl``, and ``ovn-trace``, used
> -full-length UUIDs almost everywhere.  It didn't even provide any
> assistance
> -with completion, etc., which in practice meant always cutting and pasting
> UUIDs
> -from one command or window to another.  This problem wasn't limited to the
> -places where one would expect to have to see or use a UUID, either.  In
> many
> -places where one would expect to be able to use a network, router, or port
> -name, a UUID was required instead.  In many places where one would want
> to see
> -a name, the UUID was displayed instead.  More than anything else, these
> -shortcomings made the CLI user-hostile.
> -
> -There was an underlying problem that the southbound database didn't
> actually
> -contain all the information needed to provide a decent user interface.
> In some
> -cases, for example, the human-friendly names that one would want to use
> for
> -entities simply weren't part of the database.  These names weren't
> necessary
> -for correctness, only for usability.
> -
> -OVN 2.8 eased many of these problems.  Most parts of the CLI now allow
> the user
> -to abbreviate UUIDs, as long as the abbreviations are unique within the
> -database.  Some parts of the CLI where full-length UUIDs make output hard
> to
> -read now abbreviate them themselves.  Perhaps more importantly, in many
> places
> -the OVN CLI now displays and accepts human-friendly names for networks,
> -routers, ports, and other entities.  In the places where the names were
> not
> -previously available, OVN (through ``ovn-northd``) now copies the names
> into
> -the southbound database.
> -
> -The CLIs for layers below OVN, at the OpenFlow and datapath layers with
> -``ovs-ofctl`` and ``ovs-dpctl``, respectively, had some similar problems
> in
> -which numbers were used for entities that had human-friendly names.  Open
> -vSwitch 2.8 also solves some of those problems.  Other than that, the most
> -notable enhancement in this area was the ``--no-stats`` option to
> ``ovs-ofctl
> -dump-flows``, which made that command's output more readable for the cases
> -where per-flow statistics were not interesting to the reader.
> -
> -Connections Between Levels
> -~~~~~~~~~~~~~~~~~~~~~~~~~~
> -
> -OVN and Open vSwitch work almost like a stack of compilers: the OVN
> Neutron
> -plugin translates Neutron configuration into OVN northbound configuration,
> -which ``ovn-northd`` translates into logical flows, which
> ``ovn-controller``
> -translates into OpenFlow flows, which ``ovs-vswitchd`` translates into
> datapath
> -flows.  For debugging and troubleshooting it is often necessary to
> understand
> -exactly how these translations work.  The relationship from a logical
> flow to
> -its OpenFlow flows, or in the other direction, from an OpenFlow flow back
> to
> -the logical flow that produced it, was often of particular interest, but
> OVN
> -didn't provide good tools for the job.
> -
> -OVN 2.8 added some new features that ease these jobs.  ``ovn-sbctl
> lflow-list``
> -has a new option ``--ovs`` that lists the OpenFlow flows on a particular
> -chassis that were generated from the logical flows that it lists.
> -``ovn-trace`` also added a similar ``--ovs`` option that applies to the
> logical
> -flows it traces.
> -
> -In the other direction, OVN 2.8 added a new utility ``ovn-detrace`` that,
> given
> -an Open vSwitch trace of OpenFlow flows, annotates it with the logical
> flows
> -that yielded those OpenFlow flows.
> -
> -Distributed Firewall
> -~~~~~~~~~~~~~~~~~~~~
> -
> -OVN supports a distributed firewall with stateful connection tracking to
> ensure
> -that only packets for established connections, or those that the
> configuration
> -explicitly allows, can ingress a given VM or container.  Neutron uses this
> -feature by default.  Most packets in an OpenStack environment pass
> through it
> -twice, once after egress from the packet's source VM and once before
> ingress
> -into its destination VM.  Before OVN 2.8, the ``ovn-trace`` program, which
> -shows the path of a packet through an OVN logical network, did not
> support the
> -logical firewall, which in practice made it almost useless for Neutron.
> -
> -In OVN 2.8, ``ovn-trace`` adds support for the logical firewall.  By
> default it
> -assumes that packets are part of an established connection, which is
> usually
> -what the user wants as part of the trace.  It also accepts command-line
> options
> -to override that assumption, which allows the user to discover the
> treatment of
> -packets that the firewall should drop.
> -
> -At the next level deeper, prior to Open vSwitch 2.8, the OpenFlow tracing
> -command ``ofproto/trace`` also supported neither the connection tracking
> -feature underlying the OVN distributed firewall nor the "recirculation"
> feature
> -that accompanied it.  This meant that, even if the user tried to look
> deeper
> -into the distributed firewall mechanism, he or she would encounter a
> further
> -roadblock.  Open vSwitch 2.8 added support for both of these features as
> well.
> -
> -Summary Display
> -~~~~~~~~~~~~~~~
> -
> -``ovn-nbctl show`` and ``ovn-sbctl show``, for showing an overview of the
> OVN
> -configuration, didn't show a lot of important information.  OVN adds some
> more
> -useful information here.
> -
> -DNS, and IPAM
> --------------
> -
> -OVN 2.8 adds a built-in DNS server designed for assigning names to VMs and
> -containers within an OVN logical network.  DNS names are assigned using
> records
> -in the OVN northbound database and, like other OVN features, translated
> into
> -logical flows at the OVN southbound layer.  DNS requests directed to the
> OVN
> -DNS server never leave the hypervisor from which the request is sent;
> instead,
> -OVN processes and replies to the request from its ``ovn-controller`` local
> -agent.  The OVN DNS server is not a general-purpose DNS server and cannot
> be
> -used for that purpose.
> -
> -OVN includes simple built-in support for IP address management (IPAM), in
> which
> -OVN assigns IP addresses to VMs or containers from a pool or pools of IP
> -addresses delegated to it by the administrator.  Before OVN 2.8, OVN IPAM
> only
> -supported IPv4 addresses; OVN 2.8 adds support for IPv6.  OVN 2.8 also
> enhances
> -the address pool support to allow specific addresses to be excluded.
> Neutron
> -assigns IP addresses itself and does not use OVN IPAM.
> -
> -High Availability
> ------------------
> -
> -As a distributed system, in OVN a lot can go wrong.  As OVN advances, it
> adds
> -redundancy in places where currently a single failure could disrupt the
> -functioning of the system as a whole.  OVN 2.8 adds two new kinds of high
> -availability.
> -
> -ovn-northd HA
> -~~~~~~~~~~~~~
> -
> -The ``ovn-northd`` program sits between the OVN northbound and southbound
> -databases and translates from a logical network configuration into logical
> -flows.  If ``ovn-northd`` itself or the host on which it runs fails, then
> -updates to the OVN northbound configuration will not propagate to the
> -hypervisors and the OVN configuration freezes in place until
> ``ovn-northd``
> -restarts.
> -
> -OVN 2.8 adds support for active-backup HA to ``ovn-northd``.  When more
> than
> -one ``ovn-northd`` instance runs, it uses an OVSDB locking feature to
> -automatically choose a single active instance.  When that instance dies or
> -becomes nonresponsive, the OVSDB server automatically choose one of the
> -remaining instance(s) to take over.
> -
> -L3 Gateway HA
> -~~~~~~~~~~~~~
> -
> -In OVN 2.8, multiple chassis may now be specified for L3 gateways.  When
> more
> -than one chassis is specified, OVN manages high availability for that
> gateway.
> -Each hypervisor uses the BFD protocol to keep track of the gateway nodes
> that
> -are currently up.  At any given time, a hypervisor uses the
> highest-priority
> -gateway node that is currently up.
> -
> -OVSDB
> ------
> -
> -The OVN architecture relies heavily on OVSDB, the Open vSwitch database,
> for
> -hosting the northbound and southbound databases.  OVSDB was originally
> selected
> -for this purpose because it was already used in Open vSwitch for
> configuring
> -OVS itself and, thus, it was well integrated with OVS and well supported
> in C
> -and Python, the two languages that are used in Open vSwitch.
> -
> -OVSDB was well designed for its original purpose of configuring Open
> vSwitch.
> -It supports ACID transactions, has a small, efficient server, a flexible
> schema
> -system, and good support for troubleshooting and debugging.  However, it
> lacked
> -several features that are important for OVN but not for Open vSwitch.  As
> OVN
> -advances, these missing features have become more and more of a problem.
> One
> -option would be to switch to a different database that already has many of
> -these features, but despite a careful search, no ideal existing database
> was
> -identified, so the project chose instead to improve OVSDB where necessary
> to
> -bring it up to speed.  The following sections talk more about recent and
> future
> -improvements.
> -
> -High Availability
> -~~~~~~~~~~~~~~~~~
> -
> -When ``ovsdb-server`` was only used for OVS configuration, high
> availability
> -was not important.  ``ovsdb-server`` was capable of restarting itself
> -automatically if it crashed, and if the whole system went down then Open
> -vSwitch itself was dead too, so the database server's failure was not
> -important.
> -
> -In contrast, the northbound and southbound databases are centralized
> components
> -of a distributed system, so it is important that they not be a single
> point of
> -failure for the system as a whole.  In released versions of OVN,
> -``ovsdb-server`` supports only "active-backup replication" across a pair
> of
> -servers.  This means that if one server goes down, the other can pick it
> back
> -up approximately where the other one left off.  The servers do not have
> -built-in support for deciding at any given time which is the active and
> which
> -the backup, so the administrator must configure an external agent to do
> this
> -management.
> -
> -Active-backup replication is not entirely satisfactory, for multiple
> reasons.
> -Replication is only approximate.  Configuring the external agent requires
> extra
> -work.  There is no benefit from the backup server except when the active
> server
> -fails.  At most two servers can be used.
> -
> -A new form of high availability for OVSDB is under development for the
> OVN 2.9
> -release, based on the Raft algorithm for distributed consensus.  Whereas
> -replication uses two servers, clustering using Raft requires three or more
> -(typically an odd number) and continues functioning as long as more than
> half
> -of the servers are up.  The clustering implementation is built into
> -``ovsdb-server`` and does not require an external agent.  Clustering
> preserves
> -the ACID properties of the database, so that a transaction that commits is
> -guaranteed to persist.  Finally, reads (which are the bulk of the OVN
> workload)
> -scale with the size of the cluster, so that adding more servers should
> improve
> -performance as the number of hypervisors in an OVN deployment increases.
> As of
> -this writing, OVSDB support for clustering is undergoing development and
> early
> -deployment testing.
> -
> -RBAC security
> -~~~~~~~~~~~~~
> -
> -Until Open vSwitch 2.8, ``ovsdb-server`` had little support for access
> control
> -within a database.  If an OVSDB client could modify the database at all,
> it
> -could make arbitrary changes.  This was sufficient for most uses case to
> that
> -point.
> -
> -Hypervisors in an OVN deployment need access to the OVN southbound
> database.
> -Most of their access is reads, to find out about the OVN configuration.
> -Hypervisors do need some write access to the southbound database,
> primarily to
> -let the other hypervisors know what VMs and containers they are running
> and how
> -to reach them.  Thus, OVN gives all of the hypervisors in the OVN
> deployment
> -write access to the OVN southbound database.  This is fine when all is
> well,
> -but if any of the hypervisors were compromised then they could disrupt the
> -entire OVN deployment by corrupting the database.
> -
> -The OVN developers considered a few ways to solve this problem.  One way
> would
> -be to introduce a new central service (perhaps in ``ovn-northd``) that
> provided
> -only the kinds of writes that the hypervisors legitimately need, and then
> grant
> -hypervisors direct access to the southbound database only for reads.  But
> -ultimately the developers decided to introduce a new form of more access
> -control for OVSDB, called the OVSDB RBAC (role-based access control)
> feature.
> -OVSDB RBAC allows for granular enough control over access that
> hypervisors can
> -be granted only the ability to add, modify, and delete the records that
> relate
> -to themselves, preventing them from corrupting the database as a whole.
> -
> -Further Directions
> -------------------
> -
> -For more information about new features in OVN and Open vSwitch, please
> refer
> -to the NEWS file distributed with the source tree.  If you have questions
> about
> -Open vSwitch or OVN features, please feel free to write to the Open
> vSwitch
> -discussion mailing list at ovs-discuss at openvswitch.org.
> diff --git a/Documentation/topics/role-based-access-control.rst
> b/Documentation/topics/role-based-access-control.rst
> deleted file mode 100644
> index 8f2a3a998..000000000
> --- a/Documentation/topics/role-based-access-control.rst
> +++ /dev/null
> @@ -1,101 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -=========================
> -Role Based Access Control
> -=========================
> -
> -Where SSL provides authentication when connecting to an OVS database, role
> -based access control (RBAC) provides authorization to operations performed
> -by clients connecting to an OVS database. RBAC allows for administrators
> to
> -restrict the database operations a client may perform and thus enhance the
> -security already provided by SSL.
> -
> -In theory, any OVS database could define RBAC roles and permissions, but
> at
> -present only the OVN southbound database has the appropriate tables
> defined to
> -facilitate RBAC.
> -
> -Mechanics
> ----------
> -RBAC is intended to supplement SSL. In order to enable RBAC, the
> connection to
> -the database must use SSL. Some permissions in RBAC are granted based on
> the
> -certificate common name (CN) of the connecting client.
> -
> -RBAC is controlled with two database tables, RBAC_Role and
> RBAC_Permission.
> -The RBAC_Permission table contains records that describe a set of
> permissions
> -for a given table in the database.
> -
> -The RBAC_Permission table contains the following columns:
> -
> -table
> -  The table in the database for which permissions are being described.
> -insert_delete
> -  Describes whether insertion and deletion of records is allowed.
> -update
> -  A list of columns that are allowed to be updated.
> -authorization
> -  A list of column names. One of the listed columns must match the SSL
> -  certificate CN in order for the attempted operation on the table to
> -  succeed. If a key-value pair is provided, then the key is the column
> name,
> -  and the value is the name of a key in that column. An empty string gives
> -  permission to all clients to perform operations.
> -
> -The RBAC_Role table contains the following columns:
> -
> -name
> -  The name of the role being defined
> -permissions
> -  A list of key-value pairs. The key is the name of a table in the
> database,
> -  and the value is a UUID of a record in the RBAC_Permission table that
> -  describes the permissions the role has for that table.
> -
> -.. note::
> -
> -   All tables not explicitly referenced in an RBAC_Role record are
> read-only
> -
> -In order to enable RBAC, specify the role name as an argument to the
> -set-connection command for the database. As an example, to enable the
> -"ovn-controller" role on the OVN southbound database, use the following
> -command:
> -
> -::
> -
> -   $ ovn-sbctl set-connection role=ovn-controller ssl:192.168.0.1:6642
> -
> -Pre-defined Roles
> ------------------
> -This section describes roles that have been defined internally by OVS/OVN.
> -
> -ovn-controller
> -~~~~~~~~~~~~~~
> -The ovn-controller role is specified in the OVN southbound database and is
> -intended for use by hypervisors running the ovn-controller daemon.
> -ovn-controller connects to the OVN southbound database mostly to read
> -information, but there are a few cases where ovn-controller also needs to
> -write. The ovn-controller role was designed to allow for ovn-controllers
> -to write to the southbound database only in places where it makes sense
> to do
> -so. This way, if an intruder were to take over a hypervisor running
> -ovn-controller, it is more difficult to compromise the entire overlay
> network.
> -
> -It is strongly recommended to set the ovn-controller role for the OVN
> -southbound database to enhance security.
> diff --git a/Documentation/tutorials/index.rst
> b/Documentation/tutorials/index.rst
> index 35340ee56..5ec62beab 100644
> --- a/Documentation/tutorials/index.rst
> +++ b/Documentation/tutorials/index.rst
> @@ -27,8 +27,7 @@
>  Tutorials
>  =========
>
> -Getting started with Open vSwitch (OVS) and Open Virtual Network (OVN)
> for Open
> -vSwitch.
> +Getting started with Open vSwitch (OVS).
>
>  .. TODO(stephenfin): We could really do with a few basic tutorials, along
> with
>     some more specialized ones (DPDK, XenServer, Windows). The latter could
> @@ -42,8 +41,4 @@ vSwitch.
>     faucet
>     ipsec
>     ovs-advanced
> -   ovn-sandbox
> -   ovn-openstack
> -   ovn-rbac
> -   ovn-ipsec
>     ovs-conntrack
> diff --git a/Documentation/tutorials/ovn-ipsec.rst
> b/Documentation/tutorials/ovn-ipsec.rst
> deleted file mode 100644
> index feb695ea3..000000000
> --- a/Documentation/tutorials/ovn-ipsec.rst
> +++ /dev/null
> @@ -1,146 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -==================
> -OVN IPsec Tutorial
> -==================
> -
> -This document provides a step-by-step guide for encrypting tunnel traffic
> with
> -IPsec in Open Virtual Network (OVN). OVN tunnel traffic is transported by
> -physical routers and switches. These physical devices could be untrusted
> -(devices in public network) or might be compromised.  Enabling IPsec
> encryption
> -for the tunnel traffic can prevent the traffic data from being monitored
> and
> -manipulated. More details about the OVN IPsec design can be found in
> -``ovn-architecture``\(7) manpage.
> -
> -This document assumes OVN is installed in your system and runs normally.
> Also,
> -you need to install OVS IPsec packages in each chassis (refer to
> -:ref:`install-ovs-ipsec`).
> -
> -Generating Certificates and Keys
> ---------------------------------
> -
> -OVN chassis uses CA-signed certificate to authenticate peer chassis for
> -building IPsec tunnel. If you have enabled Role-Based Access Control
> (RBAC) in
> -OVN, you can use the RBAC SSL certificates and keys to set up OVN IPsec.
> Or you
> -can generate separate certificates and keys with ``ovs-pki`` (refer to
> -:ref:`gen-certs-keys`).
> -
> -.. note::
> -
> -   OVN IPsec requires x.509 version 3 certificate with the subjectAltName
> DNS
> -   field setting the same string as the common name (CN) field. CN should
> be
> -   set as the chassis name.  ``ovs-pki`` in Open vSwitch 2.10.90 and later
> -   generates such certificates.  Please generate compatible certificates
> if you
> -   use another PKI tool, or an older version of ``ovs-pki``, to manage
> -   certificates.
> -
> -Configuring OVN IPsec
> ----------------------
> -
> -You need to install the CA certificate, chassis certificate and private
> key in
> -each chassis. Use the following command::
> -
> -    $ ovs-vsctl set Open_vSwitch . \
> -            other_config:certificate=/path/to/chassis-cert.pem \
> -            other_config:private_key=/path/to/chassis-privkey.pem \
> -            other_config:ca_cert=/path/to/cacert.pem
> -
> -Enabling OVN IPsec
> -------------------
> -
> -To enable OVN IPsec, set ``ipsec`` column in ``NB_Global`` table of the
> -northbound database to true::
> -
> -    $ ovn-nbctl set nb_global . ipsec=true
> -
> -With OVN IPsec enabled, all tunnel traffic in OVN will be encrypted with
> IPsec.
> -To disable it, set ``ipsec`` column in ``NB_Global`` table of the
> northbound
> -database to false::
> -
> -    $ ovn-nbctl set nb_global . ipsec=false
> -
> -Troubleshooting
> ----------------
> -
> -The ``ovs-monitor-ipsec`` daemon in each chassis manages and monitors the
> IPsec
> -tunnel state. Use the following ``ovs-appctl`` command to view
> -``ovs-monitor-ipsec`` internal representation of tunnel configuration::
> -
> -    $ ovs-appctl -t ovs-monitor-ipsec tunnels/show
> -
> -If there is a misconfiguration, then ``ovs-appctl`` should indicate why.
> -For example::
> -
> -   Interface name: ovn-host_2-0 v1 (CONFIGURED) <--- Should be set
> -                                             to CONFIGURED. Otherwise,
> -                                             error message will be
> -                                             provided
> -   Tunnel Type:    geneve
> -   Remote IP:      2.2.2.2
> -   SKB mark:       None
> -   Local cert:     /path/to/chassis-cert.pem
> -   Local name:     host_1
> -   Local key:      /path/to/chassis-privkey.pem
> -   Remote cert:    None
> -   Remote name:    host_2
> -   CA cert:        /path/to/cacert.pem
> -   PSK:            None
> -   Ofport:         2          <--- Whether ovs-vswitchd has assigned
> Ofport
> -                                   number to this Tunnel Port
> -   CFM state:      Disabled     <--- Whether CFM declared this tunnel
> healthy
> -   Kernel policies installed:
> -   ...                          <--- IPsec policies for this OVS tunnel in
> -                                     Linux Kernel installed by strongSwan
> -   Kernel security associations installed:
> -   ...                          <--- IPsec security associations for this
> OVS
> -                                     tunnel in Linux Kernel installed by
> -                                     strongswan
> -   IPsec connections that are active:
> -   ...                          <--- IPsec "connections" for this OVS
> -                                     tunnel
> -
> -If you don't see any active connections, try to run the following command
> to
> -refresh the ``ovs-monitor-ipsec`` daemon::
> -
> -    $ ovs-appctl -t ovs-monitor-ipsec refresh
> -
> -You can also check the logs of the ``ovs-monitor-ipsec`` daemon and the
> IKE
> -daemon to locate issues.  ``ovs-monitor-ipsec`` outputs log messages to
> -``/var/log/openvswitch/ovs-monitor-ipsec.log``.
> -
> -Bug Reporting
> --------------
> -
> -If you think you may have found a bug with security implications, like
> -
> -1. IPsec protected tunnel accepted packets that came unencrypted; OR
> -2. IPsec protected tunnel allowed packets to leave unencrypted;
> -
> -Then report such bugs according to :doc:`/internals/security`.
> -
> -If bug does not have security implications, then report it according to
> -instructions in :doc:`/internals/bugs`.
> -
> -If you have suggestions to improve this tutorial, please send a email to
> -ovs-discuss at openvswitch.org.
> diff --git a/Documentation/tutorials/ovn-openstack.rst
> b/Documentation/tutorials/ovn-openstack.rst
> deleted file mode 100644
> index c6dff5e55..000000000
> --- a/Documentation/tutorials/ovn-openstack.rst
> +++ /dev/null
> @@ -1,1922 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -======================
> -OVN OpenStack Tutorial
> -======================
> -
> -This tutorial demonstrates how OVN works in an OpenStack "DevStack"
> -environment.  It was tested with the "master" branches of DevStack and
> -Open vSwitch near the beginning of May 2017.  Anyone using an earlier
> -version is likely to encounter some differences.  In particular, we
> -noticed some shortcomings in OVN utilities while writing the tutorial
> -and pushed out some improvements, so it's best to use recent Open
> -vSwitch at least from that point of view.
> -
> -The goal of this tutorial is to demonstrate OVN in an end-to-end way,
> -that is, to show how it works from the cloud management system at the
> -top (in this case, OpenStack and specifically its Neutron networking
> -subsystem), through the OVN northbound and southbound databases, to
> -the bottom at the OVN local controller and Open vSwitch data plane.
> -We hope that this demonstration makes it easier for users and
> -potential users to understand how OVN works and how to debug and
> -troubleshoot it.
> -
> -In addition to new material, this tutorial incorporates content from
> -``testing.rst`` in OpenStack networking-ovn, by Russell Bryant and
> -others.  Without that example, this tutorial could not have been
> -written.
> -
> -We provide enough details in the tutorial that you should be able to
> -fully follow along, by creating a DevStack VM and cloning DevStack and
> -so on.  If you want to do this, start out from `Setting Up DevStack`_
> -below.
> -
> -Setting Up DevStack
> --------------------
> -
> -This section explains how to install DevStack, a kind of OpenStack
> -packaging for developers, in a way that allows you to follow along
> -with the tutorial in full.
> -
> -Unless you have a spare computer laying about, it's easiest to install
> -DevStacck in a virtual machine.  This tutorial was built using a VM
> -implemented by KVM and managed by virt-manager.  I recommend
> -configuring the VM configured for the x86-64 architecture, 4 GB RAM, 2
> -VCPUs, and a 20 GB virtual disk.
> -
> -.. note::
> -
> -   If you happen to run your Linux-based host with 32-bit userspace,
> -   then you will have some special issues, even if you use a 64-bit
> -   kernel:
> -
> -   * You may find that you can get 32-bit DevStack VMs to work to some
> -     extent, but I personally got tired of finding workarounds.  I
> -     recommend running your VMs in 64-bit mode.  To get this to work,
> -     I had to go to the CPUs tab for the VM configuration in
> -     virt-manager and change the CPU model from the one originally
> -     listed to "Hypervisor Default' (it is curious that this is not
> -     the default!).
> -
> -   * On a host with 32-bit userspace, KVM supports VMs with at most
> -     2047 MB RAM.  This is adequate, barely, to start DevStack, but it
> -     is not enough to run multiple (nested) VMs.  To prevent
> -     out-of-memory failures, set up extra swap space in the guest.
> -     For example, to add 2 GB swap::
> -
> -       $ sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
> -       $ sudo mkswap /swapfile
> -       $ sudo swapon /swapfile
> -
> -     and then add a line like this to ``/etc/fstab`` to add the new
> -     swap automatically upon reboot::
> -
> -       /swapfile swap swap defaults 0 0
> -
> -Here are step-by-step instructions to get started:
> -
> -1. Install a VM.
> -
> -   I tested these instructions with Centos 7.3.  Download the "minimal
> -   install" ISO and booted it.  The install is straightforward.  Be
> -   sure to enable networking, and set a host name, such as
> -   "ovn-devstack-1".  Add a regular (non-root) user, and check the box
> -   "Make this user administrator".  Also, set your time zone.
> -
> -2. You can SSH into the DevStack VM, instead of running from a
> -   console.  I recommend it because it's easier to cut and paste
> -   commands into a terminal than a VM console.  You might also
> -   consider using a very wide terminal, perhaps 160 columns, to keep
> -   tables from wrapping.
> -
> -   To improve convenience further, you can make it easier to log in
> -   with the following steps, which are optional:
> -
> -   a. On your host, edit your ``~/.ssh/config``, adding lines like
> -      the following::
> -
> -        Host ovn-devstack-1
> -              Hostname VMIP
> -              User VMUSER
> -
> -      where VMIP is the VM's IP address and VMUSER is your username
> -      inside the VM.  (You can omit the ``User`` line if your
> -      username is the same in the host and the VM.)  After you do
> -      this, you can SSH to the VM by name, e.g. ``ssh
> -      ovn-devstack-1``, and if command-line completion is set up in
> -      your host shell, you can shorten that to something like ``ssh
> -      ovn`` followed by hitting the Tab key.
> -
> -   b. If you have SSH public key authentication set up, with an SSH
> -      agent, run on your host::
> -
> -        $ ssh-copy-id ovn-devstack-1
> -
> -      and type your password once.  Afterward, you can log in without
> -      typing your password again.
> -
> -      (If you don't already use SSH public key authentication and an
> -      agent, consider looking into it--it will save you time in the
> -      long run.)
> -
> -   c. Optionally, inside the VM, append the following to your
> -      ``~/.bash_profile``::
> -
> -        . $HOME/devstack/openrc admin
> -
> -      It will save you running it by hand each time you log in.  But
> -      it also prints garbage to the console, which can screw up
> -      services like ``ssh-copy-id``, so be careful.
> -
> -2. Boot into the installed system and log in as the regular user, then
> -   install Git::
> -
> -     $ sudo yum install git
> -
> -   .. note::
> -
> -      If you installed a 32-bit i386 guest (against the advice above),
> -      install a non-PAE kernel and reboot into it at this point::
> -
> -           $ sudo yum install kernel-core kernel-devel
> -           $ sudo reboot
> -
> -      Be sure to select the non-PAE kernel from the list at boot.
> -      Without this step, DevStack will fail to install properly later.
> -
> -3. Get copies of DevStack and OVN and set them up::
> -
> -     $ git clone http://git.openstack.org/openstack-dev/devstack.git
> -     $ git clone http://git.openstack.org/openstack/networking-ovn.git
> -     $ cd devstack
> -     $ cp ../networking-ovn/devstack/local.conf.sample local.conf
> -
> -   .. note::
> -
> -      If you installed a 32-bit i386 guest (against the advice above),
> -      at this point edit ``local.conf`` to add the following line::
> -
> -        CIRROS_ARCH=i386
> -
> -4. Initialize DevStack::
> -
> -     $ ./stack.sh
> -
> -   This will spew many screenfuls of text, and the first time you run
> -   it, it will download lots of software from the Internet.  The
> -   output should eventually end with something like this::
> -
> -     This is your host IP address: 172.16.189.6
> -     This is your host IPv6 address: ::1
> -     Horizon is now available at http://172.16.189.6/dashboard
> -     Keystone is serving at http://172.16.189.6/identity/
> -     The default users are: admin and demo
> -     The password: password
> -     2017-03-09 15:10:54.117 | stack.sh completed in 2110 seconds.
> -
> -   If there's some kind of failure, you can restart by running
> -   ``./stack.sh`` again.  It won't restart exactly where it left off,
> -   but steps up to the one where it failed will skip the download
> -   steps.  (Sometimes blindly restarting after a failure will allow it
> -   to succeed.)  If you reboot your VM, you need to rerun this
> -   command.  (If you run into trouble with ``stack.sh`` after
> -   rebooting your VM, try running ``./unstack.sh``.)
> -
> -   At this point you can navigate a web browser on your host to the
> -   Horizon dashboard URL.  Many OpenStack operations can be initiated
> -   from this UI.  Feel free to explore, but this tutorial focuses on
> -   the alternative command-line interfaces because they are easier to
> -   explain and to cut and paste.
> -
> -5. As of this writing, you need to run the following to fix a problem
> -   with using VM consoles from the OpenStack web instance::
> -
> -     $ (cd /opt/stack/noVNC && git checkout v0.6.0)
> -
> -   See
> -
> https://serenity-networks.com/how-to-fix-setkeycodes-00-and-unknown-key-pressed-console-errors-on-openstack/
> -   for more details.
> -
> -6. The firewall in the VM by default allows SSH access but not HTTP.
> -   You will probably want HTTP access to use the OpenStack web
> -   interface.  The following command enables that.  (It also enables
> -   every other kind of network access, so if you're concerned about
> -   security then you might want to find a more targeted approach.)
> -
> -   ::
> -
> -      $ sudo iptables -F
> -
> -   (You need to re-run this if you reboot the VM.)
> -
> -7. To use OpenStack command line utilities in the tutorial, run::
> -
> -     $ . ~/devstack/openrc admin
> -
> -   This needs to be re-run each time you log in (but see the following
> -   section).
> -
> -DevStack preliminaries
> -----------------------
> -
> -Before we really jump in, let's set up a couple of things in DevStack.
> -This is the first real test that DevStack is working, so if you get
> -errors from any of these commands, it's a sign that ``stack.sh``
> -didn't finish properly, or perhaps that you didn't run the ``openrc
> -admin`` command at the end of the previous instructions.
> -
> -If you stop and restart DevStack via ``unstack.sh`` followed by
> -``stack.sh``, you have to rerun these steps.
> -
> -1. For SSH access to the VMs we're going to create, we'll need a SSH
> -   keypair.  Later on, we'll get OpenStack to install this keypair
> -   into VMs.  Create one with::
> -
> -     $ openstack keypair create demo > ~/id_rsa_demo
> -     $ chmod 600 ~/id_rsa_demo
> -
> -2. By default, DevStack security groups drop incoming traffic, but to
> -   test networking in a reasonable way we need to enable it.  You only
> -   need to actually edit one particular security group, but DevStack
> -   creates multiple and it's somewhat difficult to figure out which
> -   one is important because all of them are named "default".  So, the
> -   following adds rules to allow SSH and ICMP traffic into **every**
> -   security group::
> -
> -     $ for group in $(openstack security group list -f value -c ID); do \
> -     openstack security group rule create --ingress --ethertype IPv4
> --dst-port 22 --protocol tcp $group; \
> -     openstack security group rule create --ingress --ethertype IPv4
> --protocol ICMP $group; \
> -     done
> -
> -3. Later on, we're going to create some VMs and we'll need an
> -   operating system image to install.  DevStack comes with a very
> -   simple image built-in, called "cirros", which works fine.  We need
> -   to get the UUID for this image.  Our later commands assume shell
> -   variable ``IMAGE_ID`` holds this UUID.  You can set this by hand,
> -   e.g.::
> -
> -     $ openstack image list
> -
>  +--------------------------------------+--------------------------+--------+
> -     | ID                                   | Name                     |
> Status |
> -
>  +--------------------------------------+--------------------------+--------+
> -     | 77f37d2c-3d6b-4e99-a01b-1fa5d78d1fa1 | cirros-0.3.5-x86_64-disk |
> active |
> -
>  +--------------------------------------+--------------------------+--------+
> -     $ IMAGE_ID=73ca34f3-63c4-4c10-a62f-4540afc24eaa
> -
> -   or by parsing CLI output::
> -
> -     $ IMAGE_ID=$(openstack image list -f value -c ID)
> -
> -   .. note::
> -
> -      Your image ID will differ from the one above, as will every UUID
> -      in this tutorial.  They will also change every time you run
> -      ``stack.sh``.  The UUIDs are generated randomly.
> -
> -Shortening UUIDs
> -----------------
> -
> -OpenStack, OVN, and Open vSwitch all really like UUIDs.  These are
> -great for uniqueness, but 36-character strings are terrible for
> -readability.  Statistically, just the first few characters are enough
> -for uniqueness in small environments, so let's define a helper to make
> -things more readable::
> -
> -  $ abbrev() { a='[0-9a-fA-F]' b=$a$a c=$b$b; sed
> "s/$b-$c-$c-$c-$c$c$c//g"; }
> -
> -You can use this as a filter to abbreviate UUIDs.  For example, use it
> -to abbreviate the above image list::
> -
> -  $ openstack image list -f yaml | abbrev
> -  - ID: 77f37d
> -    Name: cirros-0.3.5-x86_64-disk
> -    Status: active
> -
> -The command above also adds ``-f yaml`` to switch to YAML output
> -format, because abbreviating UUIDs screws up the default table-based
> -formatting and because YAML output doesn't produce wrap columns across
> -lines and therefore is easier to cut and paste.
> -
> -Overview
> ---------
> -
> -Now that DevStack is ready, with OVN set up as the networking
> -back-end, here's an overview of what we're going to do in the
> -remainder of the demo, all via OpenStack:
> -
> -1. Switching: Create an OpenStack network ``n1`` and VMs ``a`` and
> -   ``b`` attached to it.
> -
> -   An OpenStack network is a virtual switch; it corresponds to an OVN
> -   logical switch.
> -
> -2. Routing: Create a second OpenStack network ``n2`` and VM ``c``
> -   attached to it, then connect it to network ``n1`` by creating an
> -   OpenStack router and attaching ``n1`` and ``n2`` to it.
> -
> -3. Gateways: Make VMs ``a`` and ``b`` available via an external network.
> -
> -4. IPv6: Add IPv6 addresses to our VMs to demonstrate OVN support for
> -   IPv6 routing.
> -
> -5. ACLs: Add and modify OpenStack stateless and stateful rules in
> -   security groups.
> -
> -6. DHCP: How it works in OVN.
> -
> -7. Further directions: Adding more compute nodes.
> -
> -At each step, we will take a look at how the features in question work
> -from OpenStack's Neutron networking layer at the top to the data plane
> -layer at the bottom.  From the highest to lowest level, these layers
> -and the software components that connect them are:
> -
> -* OpenStack Neutron, which as the top level in the system is the
> -  authoritative source of the virtual network configuration.
> -
> -  We will use OpenStack's ``openstack`` utility to observe and modify
> -  Neutron and other OpenStack configuration.
> -
> -* networking-ovn, the Neutron driver that interfaces with OVN and
> -  translates the internal Neutron representation of the virtual
> -  network into OVN's representation and pushes that representation
> -  down the OVN northbound database.
> -
> -  In this tutorial it's rarely worth distinguishing Neutron from
> -  networking-ovn, so we usually don't break out this layer separately.
> -
> -* The OVN Northbound database, aka NB DB.  This is an instance of
> -  OVSDB, a simple general-purpose database that is used for multiple
> -  purposes in Open vSwitch and OVN.  The NB DB's schema is in terms of
> -  networking concepts such as switches and routers.  The NB DB serves
> -  the purpose that in other systems might be filled by some kind of
> -  API; for example, in place of calling an API to create or delete a
> -  logical switch, networking-ovn performs these operations by
> -  inserting or deleting a row in the NB DB's Logical_Switch table.
> -
> -  We will use OVN's ``ovn-nbctl`` utility to observe the NB DB.  (We
> -  won't directly modify data at this layer or below.  Because
> -  configuration trickles down from Neutron through the stack, the
> -  right way to make changes is to use the ``openstack`` utility or
> -  another OpenStack interface and then wait for them to percolate
> -  through to lower layers.)
> -
> -* The ovn-northd daemon, a program that runs centrally and translates
> -  the NB DB's network representation into the lower-level
> -  representation used by the OVN Southbound database in the next
> -  layer.  The details of this daemon are usually not of interest,
> -  although without it OVN will not work, so this tutorial does not
> -  often mention it.
> -
> -* The OVN Southbound database, aka SB DB, which is also an OVSDB
> -  database.  Its schema is very different from the NB DB.  Instead of
> -  familiar networking concepts, the SB DB defines the network in terms
> -  of collections of match-action rules called "logical flows", which
> -  while similar in concept to OpenFlow flows use logical concepts, such
> -  as virtual machine instances, in place of physical concepts like
> -  physical Ethernet ports.
> -
> -  We will use OVN's ``ovn-sbctl`` utility to observe the SB DB.
> -
> -* The ovn-controller daemon.  A copy of ovn-controller runs on each
> -  hypervisor.  It reads logical flows from the SB DB, translates them
> -  into OpenFlow flows, and sends them to Open vSwitch's ovs-vswitchd
> -  daemon.  Like ovn-northd, usually the details of what this daemon
> -  are not of interest, even though it's important to the operation of
> -  the system.
> -
> -* ovs-vswitchd.  This program runs on each hypervisor.  It is the core
> -  of Open vSwitch, which processes packets according to the OpenFlow
> -  flows set up by ovn-controller.
> -
> -* Open vSwitch datapath.  This is essentially a cache designed to
> -  accelerate packet processing.  Open vSwitch includes a few different
> -  datapaths but OVN installations typically use one based on the Open
> -  vSwitch Linux kernel module.
> -
> -Switching
> ----------
> -
> -Switching is the basis of networking in the real world and in virtual
> -networking as well.  OpenStack calls its concept of a virtual switch a
> -"network", and OVN calls its corresponding concept a "logical switch".
> -
> -In this step, we'll create an OpenStack network ``n1``, then create
> -VMs ``a`` and ``b`` and attach them to ``n1``.
> -
> -Creating network ``n1``
> -~~~~~~~~~~~~~~~~~~~~~~~
> -
> -Let's start by creating the network::
> -
> -  $ openstack network create --project admin --provider-network-type
> geneve n1
> -
> -OpenStack needs to know the subnets that a network serves.  We inform
> -it by creating subnet objects.  To keep it simple, let's give our
> -network a single subnet for the 10.1.1.0/24 network.  We have to give
> -it a name, in this case ``n1subnet``::
> -
> -  $ openstack subnet create --subnet-range 10.1.1.0/24 --network n1
> n1subnet
> -
> -If you ask Neutron to show us the available networks, we see ``n1`` as
> -well as the two networks that DevStack creates by default::
> -
> -  $ openstack network list -f yaml | abbrev
> -  - ID: 5b6baf
> -    Name: n1
> -    Subnets: 5e67e7
> -  - ID: c02c4d
> -    Name: private
> -    Subnets: d88a34, fd87f9
> -  - ID: d1ac28
> -    Name: public
> -    Subnets: 0b1e79, c87dc1
> -
> -Neutron pushes this network setup down to the OVN northbound
> -database.  We can use ``ovn-nbctl show`` to see an overview of what's
> -in the NB DB::
> -
> -  $ ovn-nbctl show | abbrev
> -  switch 5b3d5f (neutron-c02c4d) (aka private)
> -      port b256dd
> -          type: router
> -          router-port: lrp-b256dd
> -      port f264e7
> -          type: router
> -          router-port: lrp-f264e7
> -  switch 2579f4 (neutron-d1ac28) (aka public)
> -      port provnet-d1ac28
> -          type: localnet
> -          addresses: ["unknown"]
> -      port ae9b52
> -          type: router
> -          router-port: lrp-ae9b52
> -  switch 3eb263 (neutron-5b6baf) (aka n1)
> -  router c59ad2 (neutron-9b057f) (aka router1)
> -      port lrp-ae9b52
> -          mac: "fa:16:3e:b2:d2:67"
> -          networks: ["172.24.4.9/24", "2001:db8::b/64"]
> -      port lrp-b256dd
> -          mac: "fa:16:3e:35:33:db"
> -          networks: ["fdb0:5860:4ba8::1/64"]
> -      port lrp-f264e7
> -          mac: "fa:16:3e:fc:c8:da"
> -          networks: ["10.0.0.1/26"]
> -      nat 80914c
> -          external ip: "172.24.4.9"
> -          logical ip: "10.0.0.0/26"
> -          type: "snat"
> -
> -This output shows that OVN has three logical switches, each of which
> -corresponds to a Neutron network, and a logical router that
> -corresponds to the Neutron router that DevStack creates by default.
> -The logical switch that corresponds to our new network ``n1`` has no
> -ports yet, because we haven't added any.  The ``public`` and
> -``private`` networks that DevStack creates by default have router
> -ports that connect to the logical router.
> -
> -Using ovn-northd, OVN translates the NB DB's high-level switch and
> -router concepts into lower-level concepts of "logical datapaths" and
> -logical flows.  There's one logical datapath for each logical switch
> -or router::
> -
> -  $ ovn-sbctl list datapath_binding | abbrev
> -  _uuid               : 0ad69d
> -  external_ids        : {logical-switch="5b3d5f", name="neutron-c02c4d",
> "name2"=private}
> -  tunnel_key          : 1
> -
> -  _uuid               : a8a758
> -  external_ids        : {logical-switch="3eb263", name="neutron-5b6baf",
> "name2"="n1"}
> -  tunnel_key          : 4
> -
> -  _uuid               : 191256
> -  external_ids        : {logical-switch="2579f4", name="neutron-d1ac28",
> "name2"=public}
> -  tunnel_key          : 3
> -
> -  _uuid               : b87bec
> -  external_ids        : {logical-router="c59ad2", name="neutron-9b057f",
> "name2"="router1"}
> -  tunnel_key          : 2
> -
> -This output lists the NB DB UUIDs in external_ids:logical-switch and
> -Neutron UUIDs in externals_ids:uuid.  We can dive in deeper by viewing
> -the OVN logical flows that implement a logical switch.  Our new
> -logical switch is a simple and almost pathological example given that
> -it doesn't yet have any ports attached to it.  We'll look at the
> -details a bit later::
> -
> -  $ ovn-sbctl lflow-list n1 | abbrev
> -  Datapath: "neutron-5b6baf" aka "n1" (a8a758)  Pipeline: ingress
> -    table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]),
> action=(drop;)
> -    table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present),
> action=(drop;)
> -  ...
> -  Datapath: "neutron-5b6baf" aka "n1" (a8a758)  Pipeline: egress
> -    table=0 (ls_out_pre_lb      ), priority=0    , match=(1),
> action=(next;)
> -    table=1 (ls_out_pre_acl     ), priority=0    , match=(1),
> action=(next;)
> -  ...
> -
> -We have one hypervisor (aka "compute node", in OpenStack parlance),
> -which is the one where we're running all these commands.  On this
> -hypervisor, ovn-controller is translating OVN logical flows into
> -OpenFlow flows ("physical flows").  It makes sense to go deeper, to
> -see the OpenFlow flows that get generated from this datapath.  By
> -adding ``--ovs`` to the ``ovn-sbctl`` command, we can see OpenFlow
> -flows listed just below their logical flows.  We also need to use
> -``sudo`` because connecting to Open vSwitch is privileged.  Go ahead
> -and try it::
> -
> -  $ sudo ovn-sbctl --ovs lflow-list n1 | abbrev
> -  Datapath: "neutron-5b6baf" aka "n1" (a8a758)  Pipeline: ingress
> -    table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]),
> action=(drop;)
> -    table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present),
> action=(drop;)
> -  ...
> -  Datapath: "neutron-5b6baf" aka "n1" (a8a758)  Pipeline: egress
> -    table=0 (ls_out_pre_lb      ), priority=0    , match=(1),
> action=(next;)
> -    table=1 (ls_out_pre_acl     ), priority=0    , match=(1),
> action=(next;)
> -  ...
> -
> -You were probably disappointed: the output didn't change, and no
> -OpenFlow flows were printed.  That's because no OpenFlow flows are
> -installed for this logical datapath, which in turn is because there
> -are no VIFs for this logical datapath on the local hypervisor.  For a
> -better example, you can try ``ovn-sbctl --ovs`` on one of the other
> -logical datapaths.
> -
> -Attaching VMs
> -~~~~~~~~~~~~~
> -
> -A switch without any ports is not very interesting.  Let's create a
> -couple of VMs and attach them to the switch.  Run the following
> -commands, which create VMs named ``a`` and ``b`` and attaches them to
> -our network ``n1`` with IP addresses 10.1.1.5 and 10.1.1.6,
> -respectively.  It is not actually necessary to manually assign IP
> -address assignments, since OpenStack is perfectly happy to assign them
> -itself from the subnet's IP address range, but predictable addresses
> -are useful for our discussion::
> -
> -  $ openstack server create --nic net-id=n1,v4-fixed-ip=10.1.1.5 --flavor
> m1.nano --image $IMAGE_ID --key-name demo a
> -  $ openstack server create --nic net-id=n1,v4-fixed-ip=10.1.1.6 --flavor
> m1.nano --image $IMAGE_ID --key-name demo b
> -
> -These commands return before the VMs are really finished being built.
> -You can run ``openstack server list`` a few times until each of them
> -is shown in the state ACTIVE, which means that they're not just built
> -but already running on the local hypervisor.
> -
> -These operations had the side effect of creating separate "port"
> -objects, but without giving those ports any easy-to-read names.  It'll
> -be easier to deal with them later if we can refer to them by name, so
> -let's name ``a``'s port ``ap`` and ``b``'s port ``bp``::
> -
> -  $ openstack port set --name ap $(openstack port list --server a -f
> value -c ID)
> -  $ openstack port set --name bp $(openstack port list --server b -f
> value -c ID)
> -
> -We'll need to refer to these ports' MAC addresses a few times, so
> -let's put them in variables::
> -
> -  $ AP_MAC=$(openstack port show -f value -c mac_address ap)
> -  $ BP_MAC=$(openstack port show -f value -c mac_address bp)
> -
> -At this point you can log into the consoles of the VMs if you like.
> -You can do that from the OpenStack web interface or get a direct URL
> -to paste into a web browser using a command like::
> -
> -  $ openstack console url show -f yaml a
> -
> -(The option ``-f yaml`` keeps the URL in the output from being broken
> -into noncontiguous pieces on a 80-column console.)
> -
> -The VMs don't have many tools in them but ``ping`` and ``ssh`` from
> -one to the other should work fine.  The VMs do not have any external
> -network access or DNS configuration.
> -
> -Let's chase down what's changed in OVN.  Start with the NB DB at the
> -top of the system.  It's clear that our logical switch now has the two
> -logical ports attached to it::
> -
> -  $ ovn-nbctl show | abbrev
> -  ...
> -  switch 3eb263 (neutron-5b6baf) (aka n1)
> -      port c29d41 (aka bp)
> -          addresses: ["fa:16:3e:99:7a:17 10.1.1.6"]
> -      port 820c08 (aka ap)
> -          addresses: ["fa:16:3e:a9:4c:c7 10.1.1.5"]
> -  ...
> -
> -We can get some more details on each of these by looking at their NB
> -DB records in the Logical_Switch_Port table.  Each port has addressing
> -information, port security enabled, and a pointer to DHCP
> -configuration (which we'll look at much later in `DHCP`_)::
> -
> -  $ ovn-nbctl list logical_switch_port ap bp | abbrev
> -  _uuid               : ef17e5
> -  addresses           : ["fa:16:3e:a9:4c:c7 10.1.1.5"]
> -  dhcpv4_options      : 165974
> -  dhcpv6_options      : []
> -  dynamic_addresses   : []
> -  enabled             : true
> -  external_ids        : {"neutron:port_name"=ap}
> -  name                : "820c08"
> -  options             : {}
> -  parent_name         : []
> -  port_security       : ["fa:16:3e:a9:4c:c7 10.1.1.5"]
> -  tag                 : []
> -  tag_request         : []
> -  type                : ""
> -  up                  : true
> -
> -  _uuid               : e8af12
> -  addresses           : ["fa:16:3e:99:7a:17 10.1.1.6"]
> -  dhcpv4_options      : 165974
> -  dhcpv6_options      : []
> -  dynamic_addresses   : []
> -  enabled             : true
> -  external_ids        : {"neutron:port_name"=bp}
> -  name                : "c29d41"
> -  options             : {}
> -  parent_name         : []
> -  port_security       : ["fa:16:3e:99:7a:17 10.1.1.6"]
> -  tag                 : []
> -  tag_request         : []
> -  type                : ""
> -  up                  : true
> -
> -Now that the logical switch is less pathological, it's worth taking
> -another look at the SB DB logical flow table.  Try a command like
> -this::
> -
> -  $ ovn-sbctl lflow-list n1 | abbrev | less -S
> -
> -and then glance through the flows.  Packets that egress a VM into the
> -logical switch travel through the flow table's ingress pipeline
> -starting from table 0.  At each table, the switch finds the
> -highest-priority logical flow that matches and executes its actions,
> -or if there's no matching flow then the packet is dropped.  The
> -``ovn-sb``\(5) manpage gives all the details, but with a little
> -thought it's possible to guess a lot without reading the manpage.  For
> -example, consider the flows in ingress pipeline table 0, which are the
> -first flows encountered by a packet traversing the switch::
> -
> -  table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]),
> action=(drop;)
> -  table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present),
> action=(drop;)
> -  table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport ==
> "820c08" && eth.src == {fa:16:3e:a9:4c:c7}), action=(next;)
> -  table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport ==
> "c29d41" && eth.src == {fa:16:3e:99:7a:17}), action=(next;)
> -
> -The first two flows, with priority 100, immediately drop two kinds of
> -invalid packets: those with a multicast or broadcast Ethernet source
> -address (since multicast is only for packet destinations) and those
> -with a VLAN tag (because OVN doesn't yet support VLAN tags inside
> -logical networks).  The next two flows implement L2 port security:
> -they advance to the next table for packets with the correct Ethernet
> -source addresses for their ingress ports.  A packet that does not
> -match any flow is implicitly dropped, so there's no need for flows to
> -deal with mismatches.
> -
> -The logical flow table includes many other flows, some of which we
> -will look at later.  For now, it's most worth looking at ingress table
> -13::
> -
> -  table=13(ls_in_l2_lkup      ), priority=100  , match=(eth.mcast),
> action=(outport = "_MC_flood"; output;)
> -  table=13(ls_in_l2_lkup      ), priority=50   , match=(eth.dst ==
> fa:16:3e:99:7a:17), action=(outport = "c29d41"; output;)
> -  table=13(ls_in_l2_lkup      ), priority=50   , match=(eth.dst ==
> fa:16:3e:a9:4c:c7), action=(outport = "820c08"; output;)
> -
> -The first flow in table 13 checks whether the packet is an Ethernet
> -multicast or broadcast and, if so, outputs it to a special port that
> -egresses to every logical port (other than the ingress port).
> -Otherwise the packet is output to the port corresponding to its
> -Ethernet destination address.  Packets addressed to any other Ethernet
> -destination are implicitly dropped.
> -
> -(It's common for an OVN logical switch to know all the MAC addresses
> -supported by its logical ports, like this one.  That's why there's no
> -logic here for MAC learning or flooding packets to unknown MAC
> -addresses.  OVN does support unknown MAC handling but that's not in
> -play in our example.)
> -
> -.. note::
> -
> -  If you're interested in the details for the multicast group, you can
> -  run a command like the following and then look at the row for the
> -  correct datapath::
> -
> -    $ ovn-sbctl find multicast_group name=_MC_flood | abbrev
> -
> -Now if you want to look at the OpenFlow flows, you can actually see
> -them.  For example, here's the beginning of the output that lists the
> -first four logical flows, which we already looked at above, and their
> -corresponding OpenFlow flows.  If you want to know more about the
> -syntax, the ``ovs-fields``\(7) manpage explains OpenFlow matches and
> -``ovs-ofctl``\(8) explains OpenFlow actions::
> -
> -  $ sudo ovn-sbctl --ovs lflow-list n1 | abbrev
> -  Datapath: "neutron-5b6baf" aka "n1" (a8a758)  Pipeline: ingress
> -    table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]),
> action=(drop;)
> -      table=8 metadata=0x4,dl_src=01:00:00:00:00:00/01:00:00:00:00:00
> actions=drop
> -    table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present),
> action=(drop;)
> -      table=8 metadata=0x4,vlan_tci=0x1000/0x1000 actions=drop
> -    table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport ==
> "820c08" && eth.src == {fa:16:3e:a9:4c:c7}), action=(next;)
> -      table=8 reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7
> actions=resubmit(,9)
> -    table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport ==
> "c29d41" && eth.src == {fa:16:3e:99:7a:17}), action=(next;)
> -      table=8 reg14=0x2,metadata=0x4,dl_src=fa:16:3e:99:7a:17
> actions=resubmit(,9)
> -  ...
> -
> -Logical Tracing
> -+++++++++++++++
> -
> -Let's go a level deeper.  So far, everything we've done has been
> -fairly general.  We can also look at something more specific: the path
> -that a particular packet would take through OVN, logically, and Open
> -vSwitch, physically.
> -
> -Let's use OVN's ovn-trace utility to see what happens to packets from
> -a logical point of view.  The ``ovn-trace``\(8) manpage has a lot of
> -detail on how to do that, but let's just start by building up from a
> -simple example.  You can start with a command that just specifies the
> -logical datapath, an input port, and nothing else; unspecified fields
> -default to all-zeros.  This doesn't do much::
> -
> -  $ ovn-trace n1 'inport == "ap"'
> -  ...
> -  ingress(dp="n1", inport="ap")
> -  -----------------------------
> -   0. ls_in_port_sec_l2: no match (implicit drop)
> -
> -We see that the packet was dropped in logical table 0,
> -"ls_in_port_sec_l2", the L2 port security stage (as we discussed
> -earlier).  That's because we didn't use the right Ethernet source
> -address for ``a``.  Let's see what happens if we do::
> -
> -  $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC
> -  ...
> -  ingress(dp="n1", inport="ap")
> -  -----------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src ==
> {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
> -      next;
> -  13. ls_in_l2_lkup: no match (implicit drop)
> -
> -Now the packet passes through L2 port security and skips through
> -several other tables until it gets dropped in the L2 lookup stage
> -(because the destination is unknown).  Let's add the Ethernet
> -destination for ``b``::
> -
> -  $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst ==
> '$BP_MAC
> -  ...
> -  ingress(dp="n1", inport="ap")
> -  -----------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src ==
> {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:99:7a:17,
> priority 50, uuid 57a4c46f
> -      outport = "bp";
> -      output;
> -
> -  egress(dp="n1", inport="ap", outport="bp")
> -  ------------------------------------------
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "bp" && eth.dst
> == {fa:16:3e:99:7a:17}, priority 50, uuid 8aa6426d
> -      output;
> -      /* output to "bp", type "" */
> -
> -You can see that in this case the packet gets properly switched from
> -``a`` to ``b``.
> -
> -Physical Tracing for Hypothetical Packets
> -+++++++++++++++++++++++++++++++++++++++++
> -
> -ovn-trace showed us how a hypothetical packet would travel through the
> -system in a logical fashion, that is, without regard to how VMs are
> -distributed across the physical network.  This is a convenient
> -representation for understanding how OVN is **supposed** to work
> -abstractly, but sometimes we might want to know more about how it
> -actually works in the real systems where it is running.  For this, we
> -can use the tracing tool that Open vSwitch provides, which traces
> -a hypothetical packet through the OpenFlow tables.
> -
> -We can actually get two levels of detail.  Let's start with the
> -version that's easier to interpret, by physically tracing a packet
> -that looks like the one we logically traced before.  One obstacle is
> -that we need to know the OpenFlow port number of the input port.  One
> -way to do that is to look for a port whose "attached-mac" is the one
> -we expect and print its ofport number::
> -
> -  $ AP_PORT=$(ovs-vsctl --bare --columns=ofport find  interface
> external-ids:attached-mac=\"$AP_MAC\")
> -  $ echo $AP_PORT
> -  3
> -
> -(You could also just do a plain ``ovs-vsctl list interface`` and then
> -look through for the right row and pick its ``ofport`` value.)
> -
> -Now we can feed this input port number into ``ovs-appctl
> -ofproto/trace`` along with the correct Ethernet source and
> -destination addresses and get a physical trace::
> -
> -  $ sudo ovs-appctl ofproto/trace br-int
> in_port=$AP_PORT,dl_src=$AP_MAC,dl_dst=$BP_MAC
> -  Flow:
> in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=fa:16:3e:99:7a:17,dl_type=0x0000
> -
> -  bridge("br-int")
> -  ----------------
> -   0. in_port=3, priority 100
> -      set_field:0x8->reg13
> -      set_field:0x9->reg11
> -      set_field:0xa->reg12
> -      set_field:0x4->metadata
> -      set_field:0x1->reg14
> -      resubmit(,8)
> -   8. reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7, priority 50,
> cookie 0x6dcc418a
> -      resubmit(,9)
> -   9. metadata=0x4, priority 0, cookie 0x8fe8689e
> -      resubmit(,10)
> -  10. metadata=0x4, priority 0, cookie 0x719549d1
> -      resubmit(,11)
> -  11. metadata=0x4, priority 0, cookie 0x39c99e6f
> -      resubmit(,12)
> -  12. metadata=0x4, priority 0, cookie 0x838152a3
> -      resubmit(,13)
> -  13. metadata=0x4, priority 0, cookie 0x918259e3
> -      resubmit(,14)
> -  14. metadata=0x4, priority 0, cookie 0xcad14db2
> -      resubmit(,15)
> -  15. metadata=0x4, priority 0, cookie 0x7834d912
> -      resubmit(,16)
> -  16. metadata=0x4, priority 0, cookie 0x87745210
> -      resubmit(,17)
> -  17. metadata=0x4, priority 0, cookie 0x34951929
> -      resubmit(,18)
> -  18. metadata=0x4, priority 0, cookie 0xd7a8c9fb
> -      resubmit(,19)
> -  19. metadata=0x4, priority 0, cookie 0xd02e9578
> -      resubmit(,20)
> -  20. metadata=0x4, priority 0, cookie 0x42d35507
> -      resubmit(,21)
> -  21. metadata=0x4,dl_dst=fa:16:3e:99:7a:17, priority 50, cookie
> 0x57a4c46f
> -      set_field:0x2->reg15
> -      resubmit(,32)
> -  32. priority 0
> -      resubmit(,33)
> -  33. reg15=0x2,metadata=0x4, priority 100
> -      set_field:0xb->reg13
> -      set_field:0x9->reg11
> -      set_field:0xa->reg12
> -      resubmit(,34)
> -  34. priority 0
> -      set_field:0->reg0
> -      set_field:0->reg1
> -      set_field:0->reg2
> -      set_field:0->reg3
> -      set_field:0->reg4
> -      set_field:0->reg5
> -      set_field:0->reg6
> -      set_field:0->reg7
> -      set_field:0->reg8
> -      set_field:0->reg9
> -      resubmit(,40)
> -  40. metadata=0x4, priority 0, cookie 0xde9f3899
> -      resubmit(,41)
> -  41. metadata=0x4, priority 0, cookie 0x74074eff
> -      resubmit(,42)
> -  42. metadata=0x4, priority 0, cookie 0x7789c8b1
> -      resubmit(,43)
> -  43. metadata=0x4, priority 0, cookie 0xa6b002c0
> -      resubmit(,44)
> -  44. metadata=0x4, priority 0, cookie 0xaeab2b45
> -      resubmit(,45)
> -  45. metadata=0x4, priority 0, cookie 0x290cc4d4
> -      resubmit(,46)
> -  46. metadata=0x4, priority 0, cookie 0xa3223b88
> -      resubmit(,47)
> -  47. metadata=0x4, priority 0, cookie 0x7ac2132e
> -      resubmit(,48)
> -  48. reg15=0x2,metadata=0x4,dl_dst=fa:16:3e:99:7a:17, priority 50,
> cookie 0x8aa6426d
> -      resubmit(,64)
> -  64. priority 0
> -      resubmit(,65)
> -  65. reg15=0x2,metadata=0x4, priority 100
> -      output:4
> -
> -  Final flow:
> reg11=0x9,reg12=0xa,reg13=0xb,reg14=0x1,reg15=0x2,metadata=0x4,in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=fa:16:3e:99:7a:17,dl_type=0x0000
> -  Megaflow:
> recirc_id=0,ct_state=-new-est-rel-rpl-inv-trk,ct_label=0/0x1,in_port=3,vlan_tci=0x0000/0x1000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=fa:16:3e:99:7a:17,dl_type=0x0000
> -  Datapath actions: 4
> -
> -There's a lot there, which you can read through if you like, but the
> -important part is::
> -
> -  65. reg15=0x2,metadata=0x4, priority 100
> -      output:4
> -
> -which means that the packet is ultimately being output to OpenFlow
> -port 4.  That's port ``b``, which you can confirm with::
> -
> -  $ sudo ovs-vsctl find interface ofport=4
> -  _uuid               : 840a5aca-ea8d-4c16-a11b-a94e0f408091
> -  admin_state         : up
> -  bfd                 : {}
> -  bfd_status          : {}
> -  cfm_fault           : []
> -  cfm_fault_status    : []
> -  cfm_flap_count      : []
> -  cfm_health          : []
> -  cfm_mpid            : []
> -  cfm_remote_mpids    : []
> -  cfm_remote_opstate  : []
> -  duplex              : full
> -  error               : []
> -  external_ids        : {attached-mac="fa:16:3e:99:7a:17",
> iface-id="c29d4120-20a4-4c44-bd83-8d91f5f447fd", iface-status=active,
> vm-id="2db969ca-ca2a-4d9a-b49e-f287d39c5645"}
> -  ifindex             : 9
> -  ingress_policing_burst: 0
> -  ingress_policing_rate: 0
> -  lacp_current        : []
> -  link_resets         : 1
> -  link_speed          : 10000000
> -  link_state          : up
> -  lldp                : {}
> -  mac                 : []
> -  mac_in_use          : "fe:16:3e:99:7a:17"
> -  mtu                 : 1500
> -  mtu_request         : []
> -  name                : "tapc29d4120-20"
> -  ofport              : 4
> -  ofport_request      : []
> -  options             : {}
> -  other_config        : {}
> -  statistics          : {collisions=0, rx_bytes=4254, rx_crc_err=0,
> rx_dropped=0, rx_errors=0, rx_frame_err=0, rx_over_err=0, rx_packets=39,
> tx_bytes=4188, tx_dropped=0, tx_errors=0, tx_packets=39}
> -  status              : {driver_name=tun, driver_version="1.6",
> firmware_version=""}
> -  type                : ""
> -
> -or::
> -
> -  $ BP_PORT=$(ovs-vsctl --bare --columns=ofport find  interface
> external-ids:attached-mac=\"$BP_MAC\")
> -  $ echo $BP_PORT
> -  4
> -
> -Physical Tracing for Real Packets
> -+++++++++++++++++++++++++++++++++
> -
> -In the previous sections we traced a hypothetical L2 packet, one
> -that's honestly not very realistic: we didn't even supply an Ethernet
> -type, so it defaulted to zero, which isn't anything one would see on a
> -real network.  We could refine our packet so that it becomes a more
> -realistic TCP or UDP or ICMP, etc. packet, but let's try a different
> -approach: working from a real packet.
> -
> -Pull up a console for VM ``a`` and start ``ping 10.1.1.6``, then leave
> -it running for the rest of our experiment.
> -
> -Now go back to your DevStack session and run::
> -
> -  $ sudo watch ovs-dpctl dump-flows
> -
> -We're working with a new program.  ovn-dpctl is an interface to Open
> -vSwitch datapaths, in this case to the Linux kernel datapath.  Its
> -``dump-flows`` command displays the contents of the in-kernel flow
> -cache, and by running it under the ``watch`` program we see a new
> -snapshot of the flow table every 2 seconds.
> -
> -Look through the output for a flow that begins with ``recirc_id(0)``
> -and matches the Ethernet source address for ``a``.  There is one flow
> -per line, but the lines are very long, so it's easier to read if you
> -make the window very wide.  This flow's packet counter should be
> -increasing at a rate of 1 packet per second.  It looks something like
> -this::
> -
> -
> recirc_id(0),in_port(3),eth(src=fa:16:3e:f5:2a:90),eth_type(0x0800),ipv4(src=10.1.1.5,frag=no),
> packets:388, bytes:38024, used:0.977s, actions:ct(zone=8),recirc(0x18)
> -
> -We can hand the first part of this (everything up to the first space)
> -to ``ofproto/trace``, and it will tell us what happens::
> -
> -  $ sudo ovs-appctl ofproto/trace
> 'recirc_id(0),in_port(3),eth(src=fa:16:3e:a9:4c:c7),eth_type(0x0800),ipv4(src=10.1.1.5,dst=
> 10.1.0.0/255.255.0.0,frag=no)'
> -  Flow:
> ip,in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=00:00:00:00:00:00,nw_src=10.1.1.5,nw_dst=10.1.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
> -
> -  bridge("br-int")
> -  ----------------
> -   0. in_port=3, priority 100
> -      set_field:0x8->reg13
> -      set_field:0x9->reg11
> -      set_field:0xa->reg12
> -      set_field:0x4->metadata
> -      set_field:0x1->reg14
> -      resubmit(,8)
> -   8. reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7, priority 50,
> cookie 0x6dcc418a
> -      resubmit(,9)
> -   9. ip,reg14=0x1,metadata=0x4,dl_src=fa:16:3e:a9:4c:c7,nw_src=10.1.1.5,
> priority 90, cookie 0x343af48c
> -      resubmit(,10)
> -  10. metadata=0x4, priority 0, cookie 0x719549d1
> -      resubmit(,11)
> -  11. ip,metadata=0x4, priority 100, cookie 0x46c089e6
> -      load:0x1->NXM_NX_XXREG0[96]
> -      resubmit(,12)
> -  12. metadata=0x4, priority 0, cookie 0x838152a3
> -      resubmit(,13)
> -  13. ip,reg0=0x1/0x1,metadata=0x4, priority 100, cookie 0xd1941634
> -      ct(table=22,zone=NXM_NX_REG13[0..15])
> -      drop
> -
> -  Final flow:
> ip,reg0=0x1,reg11=0x9,reg12=0xa,reg13=0x8,reg14=0x1,metadata=0x4,in_port=3,vlan_tci=0x0000,dl_src=fa:16:3e:a9:4c:c7,dl_dst=00:00:00:00:00:00,nw_src=10.1.1.5,nw_dst=10.1.0.0,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=0
> -  Megaflow:
> recirc_id=0,ip,in_port=3,vlan_tci=0x0000/0x1000,dl_src=fa:16:3e:a9:4c:c7,nw_src=10.1.1.5,nw_dst=
> 10.1.0.0/16,nw_frag=no
> -  Datapath actions: ct(zone=8),recirc(0xb)
> -
> -.. note::
> -   Be careful cutting and pasting ``ovs-dpctl dump-flows`` output into
> -   ``ofproto/trace`` because the latter has terrible error reporting.
> -   If you add an extra line break, etc., it will likely give you a
> -   useless error message.
> -
> -There's no ``output`` action in the output, but there are ``ct`` and
> -``recirc`` actions (which you can see in the ``Datapath actions`` at
> -the end).  The ``ct`` action tells the kernel to pass the packet
> -through the kernel connection tracking for firewalling purposes and
> -the ``recirc`` says to go back to the flow cache for another pass
> -based on the firewall results.  The ``0xb`` value inside the
> -``recirc`` gives us a hint to look at the kernel flows for a cached
> -flow with ``recirc_id(0xb)``.  Indeed, there is one::
> -
> -
> recirc_id(0xb),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=
> 10.1.1.4/255.255.255.252,frag=no), packets:171, bytes:16758, used:0.271s,
> actions:ct(zone=11),recirc(0xc)
> -
> -We can then repeat our command with the match part of this kernel
> -flow::
> -
> -  $ sudo ovs-appctl ofproto/trace
> 'recirc_id(0xb),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=
> 10.1.1.4/255.255.255.252,frag=no)'
> -  ...
> -  Datapath actions: ct(zone=11),recirc(0xc)
> -
> -In other words, the flow passes through the connection tracker a
> -second time.  The first time was for ``a``'s outgoing firewall; this
> -second time is for ``b``'s incoming firewall.  Again, we continue
> -tracing with ``recirc_id(0xc)``::
> -
> -  $ sudo ovs-appctl ofproto/trace
> 'recirc_id(0xc),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=10.1.1.6,proto=1,frag=no)'
> -  ...
> -  Datapath actions: 4
> -
> -It was took multiple hops, but we finally came to the end of the line
> -where the packet was output to ``b`` after passing through both
> -firewalls.  The port number here is a datapath port number, which is
> -usually different from an OpenFlow port number.  To check that it is
> -``b``'s port, we first list the datapath ports to get the name
> -corresponding to the port number::
> -
> -  $ sudo ovs-dpctl show
> -  system at ovs-system:
> -          lookups: hit:1994 missed:56 lost:0
> -          flows: 6
> -          masks: hit:2340 total:4 hit/pkt:1.14
> -          port 0: ovs-system (internal)
> -          port 1: br-int (internal)
> -          port 2: br-ex (internal)
> -          port 3: tap820c0888-13
> -          port 4: tapc29d4120-20
> -
> -and then confirm that this is the port we think it is with a command
> -like this::
> -
> -  $ ovs-vsctl --columns=external-ids list interface tapc29d4120-20
> -  external_ids        : {attached-mac="fa:16:3e:99:7a:17",
> iface-id="c29d4120-20a4-4c44-bd83-8d91f5f447fd", iface-status=active,
> vm-id="2db969ca-ca2a-4d9a-b49e-f287d39c5645"}
> -
> -Finally, we can relate the OpenFlow flows from our traces back to OVN
> -logical flows.  For individual flows, cut and paste a "cookie" value
> -from ``ofproto/trace`` output into ``ovn-sbctl lflow-list``, e.g.::
> -
> -  $ ovn-sbctl lflow-list 0x6dcc418a|abbrev
> -  Datapath: "neutron-5b6baf" aka "n1" (a8a758)  Pipeline: ingress
> -    table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport ==
> "820c08" && eth.src == {fa:16:3e:a9:4c:c7}), action=(next;)
> -
> -Or, you can pipe ``ofproto/trace`` output through ``ovn-detrace`` to
> -annotate every flow::
> -
> -  $ sudo ovs-appctl ofproto/trace
> 'recirc_id(0xc),in_port(3),ct_state(-new+est-rel-rpl-inv+trk),ct_label(0/0x1),eth(src=fa:16:3e:a9:4c:c7,dst=fa:16:3e:99:7a:17),eth_type(0x0800),ipv4(dst=10.1.1.6,proto=1,frag=no)'
> | ovn-detrace
> -  ...
> -
> -Routing
> --------
> -
> -Previously we set up a pair of VMs ``a`` and ``b`` on a network ``n1``
> -and demonstrated how packets make their way between them.  In this
> -step, we'll set up a second network ``n2`` with a new VM ``c``,
> -connect a router ``r`` to both networks, and demonstrate how routing
> -works in OVN.
> -
> -There's nothing really new for the network and the VM so let's just go
> -ahead and create them::
> -
> -  $ openstack network create --project admin --provider-network-type
> geneve n2
> -  $ openstack subnet create --subnet-range 10.1.2.0/24 --network n2
> n2subnet
> -  $ openstack server create --nic net-id=n2,v4-fixed-ip=10.1.2.7 --flavor
> m1.nano --image $IMAGE_ID --key-name demo c
> -  $ openstack port set --name cp $(openstack port list --server c -f
> value -c ID)
> -  $ CP_MAC=$(openstack port show -f value -c mac_address cp)
> -
> -The new network ``n2`` is not yet connected to ``n1`` in any way.  You
> -can try tracing a broadcast packet from ``a`` to see, for example,
> -that it doesn't make it to ``c``::
> -
> -  $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst ==
> '$CP_MAC
> -  ...
> -
> -Now create an OpenStack router and connect it to ``n1`` and ``n2``::
> -
> -  $ openstack router create r
> -  $ openstack router add subnet r n1subnet
> -  $ openstack router add subnet r n2subnet
> -
> -Now ``a``, ``b``, and ``c`` should all be able to reach other.  You
> -can get some verification that routing is taking place by running you
> -``ping`` between ``c`` and one of the other VMs: the reported TTL
> -should be one less than between ``a`` and ``b`` (63 instead of 64).
> -
> -Observe via ``ovn-nbctl`` the new OVN logical switch and router and
> -then ports that connect them together::
> -
> -  $ ovn-nbctl show|abbrev
> -  ...
> -  switch f51234 (neutron-332346) (aka n2)
> -      port 82b983
> -          type: router
> -          router-port: lrp-82b983
> -      port 2e585f (aka cp)
> -          addresses: ["fa:16:3e:89:f2:36 10.1.2.7"]
> -  switch 3eb263 (neutron-5b6baf) (aka n1)
> -      port c29d41 (aka bp)
> -          addresses: ["fa:16:3e:99:7a:17 10.1.1.6"]
> -      port 820c08 (aka ap)
> -          addresses: ["fa:16:3e:a9:4c:c7 10.1.1.5"]
> -      port 17d870
> -          type: router
> -          router-port: lrp-17d870
> -  ...
> -  router dde06c (neutron-f88ebc) (aka r)
> -      port lrp-82b983
> -          mac: "fa:16:3e:19:9f:46"
> -          networks: ["10.1.2.1/24"]
> -      port lrp-17d870
> -          mac: "fa:16:3e:f6:e2:8f"
> -          networks: ["10.1.1.1/24"]
> -
> -We have not yet looked at the logical flows for an OVN logical router.
> -You might find it of interest to look at them on your own::
> -
> -  $ ovn-sbctl lflow-list r | abbrev | less -S
> -  ...
> -
> -Let's grab the ``n1subnet`` router porter MAC address to simplify
> -later commands::
> -
> -  $ N1SUBNET_MAC=$(ovn-nbctl --bare --columns=mac find
> logical_router_port networks=10.1.1.1/24)
> -
> -Let's see what happens at the logical flow level for an ICMP packet
> -from ``a`` to ``c``.  This generates a long trace but an interesting
> -one, so we'll look at it bit by bit.  The first three stanzas in the
> -output show the packet's ingress into ``n1`` and processing through
> -the firewall on that side (via the "ct_next" connection-tracking
> -action), and then the selection of the port that leads to router ``r``
> -as the output port::
> -
> -  $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst ==
> '$N1SUBNET_MAC' && ip4.src == 10.1.1.5 && ip4.dst == 10.1.2.7 && ip.ttl ==
> 64 && icmp4.type == 8'
> -  ...
> -  ingress(dp="n1", inport="ap")
> -  -----------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src ==
> {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
> -      next;
> -   1. ls_in_port_sec_ip (ovn-northd.c:2364): inport == "ap" && eth.src ==
> fa:16:3e:a9:4c:c7 && ip4.src == {10.1.1.5}, priority 90, uuid 343af48c
> -      next;
> -   3. ls_in_pre_acl (ovn-northd.c:2646): ip, priority 100, uuid 46c089e6
> -      reg0[0] = 1;
> -      next;
> -   5. ls_in_pre_stateful (ovn-northd.c:2764): reg0[0] == 1, priority 100,
> uuid d1941634
> -      ct_next;
> -
> -  ct_next(ct_state=est|trk /* default (use --ct to customize) */)
> -  ---------------------------------------------------------------
> -   6. ls_in_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl &&
> ct_label.blocked == 0 && (inport == "ap" && ip4), priority 2002, uuid
> a12b39f0
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:f6:e2:8f,
> priority 50, uuid c43ead31
> -      outport = "17d870";
> -      output;
> -
> -  egress(dp="n1", inport="ap", outport="17d870")
> -  ----------------------------------------------
> -   1. ls_out_pre_acl (ovn-northd.c:2626): ip && outport == "17d870",
> priority 110, uuid 60395450
> -      next;
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "17d870",
> priority 50, uuid 91b5cab0
> -      output;
> -      /* output to "17d870", type "patch" */
> -
> -The next two stanzas represent processing through logical router
> -``r``.  The processing in table 5 is the core of the routing
> -implementation: it recognizes that the packet is destined for an
> -attached subnet, decrements the TTL and updates the Ethernet source
> -address.  Table 6 then selects the Ethernet destination address based
> -on the IP destination.  The packet then passes to switch ``n2`` via an
> -OVN "logical patch port"::
> -
> -  ingress(dp="r", inport="lrp-17d870")
> -  ------------------------------------
> -   0. lr_in_admission (ovn-northd.c:4071): eth.dst == fa:16:3e:f6:e2:8f
> && inport == "lrp-17d870", priority 50, uuid fa5270b0
> -      next;
> -   5. lr_in_ip_routing (ovn-northd.c:3782): ip4.dst == 10.1.2.0/24,
> priority 49, uuid 5f9d469f
> -      ip.ttl--;
> -      reg0 = ip4.dst;
> -      reg1 = 10.1.2.1;
> -      eth.src = fa:16:3e:19:9f:46;
> -      outport = "lrp-82b983";
> -      flags.loopback = 1;
> -      next;
> -   6. lr_in_arp_resolve (ovn-northd.c:5088): outport == "lrp-82b983" &&
> reg0 == 10.1.2.7, priority 100, uuid 03d506d3
> -      eth.dst = fa:16:3e:89:f2:36;
> -      next;
> -   8. lr_in_arp_request (ovn-northd.c:5260): 1, priority 0, uuid 6dacdd82
> -      output;
> -
> -  egress(dp="r", inport="lrp-17d870", outport="lrp-82b983")
> -  ---------------------------------------------------------
> -   3. lr_out_delivery (ovn-northd.c:5288): outport == "lrp-82b983",
> priority 100, uuid 00bea4f2
> -      output;
> -      /* output to "lrp-82b983", type "patch" */
> -
> -Finally the logical switch for ``n2`` runs through the same logic as
> -``n1`` and the packet is delivered to VM ``c``::
> -
> -  ingress(dp="n2", inport="82b983")
> -  ---------------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "82b983", priority
> 50, uuid 9a789e06
> -      next;
> -   3. ls_in_pre_acl (ovn-northd.c:2624): ip && inport == "82b983",
> priority 110, uuid ab52f21a
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:89:f2:36,
> priority 50, uuid dcafb3e9
> -      outport = "cp";
> -      output;
> -
> -  egress(dp="n2", inport="82b983", outport="cp")
> -  ----------------------------------------------
> -   1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid cd9cfa74
> -      reg0[0] = 1;
> -      next;
> -   2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority
> 100, uuid 9e8e22c5
> -      ct_next;
> -
> -  ct_next(ct_state=est|trk /* default (use --ct to customize) */)
> -  ---------------------------------------------------------------
> -   4. ls_out_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl &&
> ct_label.blocked == 0 && (outport == "cp" && ip4 && ip4.src ==
> $as_ip4_0fc1b6cf_f925_49e6_8f00_6dd13beca9dc), priority 2002, uuid a746fa0d
> -      next;
> -   7. ls_out_port_sec_ip (ovn-northd.c:2364): outport == "cp" && eth.dst
> == fa:16:3e:89:f2:36 && ip4.dst == {255.255.255.255, 224.0.0.0/4,
> 10.1.2.7}, priority 90, uuid 4d9862b5
> -      next;
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "cp" && eth.dst
> == {fa:16:3e:89:f2:36}, priority 50, uuid 0242cdc3
> -      output;
> -      /* output to "cp", type "" */
> -
> -Physical Tracing
> -~~~~~~~~~~~~~~~~
> -
> -It's possible to use ``ofproto/trace``, just as before, to trace a
> -packet through OpenFlow tables, either for a hypothetical packet or
> -one that you get from a real test case using ``ovs-dpctl``.  The
> -process is just the same as before and the output is almost the same,
> -too.  Using a router doesn't actually introduce any interesting new
> -wrinkles, so we'll skip over this for this case and for the remainder
> -of the tutorial, but you can follow the steps on your own if you like.
> -
> -Adding a Gateway
> -----------------
> -
> -The VMs that we've created can access each other but they are isolated
> -from the physical world.  In OpenStack, the dominant way to connect a
> -VM to external networks is by creating what is called a "floating IP
> -address", which uses network address translation to connect an
> -external address to an internal one.
> -
> -DevStack created a pair of networks named "private" and "public".  To
> -use a floating IP address from a VM, we first add a port to the VM
> -with an IP address from the "private" network, then we create a
> -floating IP address on the "public" network, then we associate the
> -port with the floating IP address.
> -
> -Let's add a new VM ``d`` with a floating IP::
> -
> -  $ openstack server create --nic net-id=private --flavor m1.nano --image
> $IMAGE_ID --key-name demo d
> -  $ openstack port set --name dp $(openstack port list --server d -f
> value -c ID)
> -  $ DP_MAC=$(openstack port show -f value -c mac_address dp)
> -  $ openstack floating ip create --floating-ip-address 172.24.4.8 public
> -  $ openstack server add floating ip d 172.24.4.8
> -
> -(We specified a particular floating IP address to make the examples
> -easier to follow, but without that OpenStack will automatically
> -allocate one.)
> -
> -It's also necessary to configure the "public" network because DevStack
> -does not do it automatically::
> -
> -  $ sudo ip link set br-ex up
> -  $ sudo ip route add 172.24.4.0/24 dev br-ex
> -  $ sudo ip addr add 172.24.4.1/24 dev br-ex
> -
> -Now you should be able to "ping" VM ``d`` from the OpenStack host::
> -
> -  $ ping 172.24.4.8
> -  PING 172.24.4.8 (172.24.4.8) 56(84) bytes of data.
> -  64 bytes from 172.24.4.8: icmp_seq=1 ttl=63 time=56.0 ms
> -  64 bytes from 172.24.4.8: icmp_seq=2 ttl=63 time=1.44 ms
> -  64 bytes from 172.24.4.8: icmp_seq=3 ttl=63 time=1.04 ms
> -  64 bytes from 172.24.4.8: icmp_seq=4 ttl=63 time=0.403 ms
> -  ^C
> -  --- 172.24.4.8 ping statistics ---
> -  4 packets transmitted, 4 received, 0% packet loss, time 3003ms
> -  rtt min/avg/max/mdev = 0.403/14.731/56.028/23.845 ms
> -
> -You can also SSH in with the key that we created during setup::
> -
> -  $ ssh -i ~/id_rsa_demo cirros at 172.24.4.8
> -
> -Let's dive in and see how this gets implemented in OVN.  First, the
> -relevant parts of the NB DB for the "public" and "private" networks
> -and the router between them::
> -
> -  $ ovn-nbctl show | abbrev
> -  switch 2579f4 (neutron-d1ac28) (aka public)
> -      port provnet-d1ac28
> -          type: localnet
> -          addresses: ["unknown"]
> -      port ae9b52
> -          type: router
> -          router-port: lrp-ae9b52
> -  switch 5b3d5f (neutron-c02c4d) (aka private)
> -      port b256dd
> -          type: router
> -          router-port: lrp-b256dd
> -      port f264e7
> -          type: router
> -          router-port: lrp-f264e7
> -      port cae25b (aka dp)
> -          addresses: ["fa:16:3e:c1:f5:a2 10.0.0.6
> fdb0:5860:4ba8:0:f816:3eff:fec1:f5a2"]
> -  ...
> -  router c59ad2 (neutron-9b057f) (aka router1)
> -      port lrp-ae9b52
> -          mac: "fa:16:3e:b2:d2:67"
> -          networks: ["172.24.4.9/24", "2001:db8::b/64"]
> -      port lrp-b256dd
> -          mac: "fa:16:3e:35:33:db"
> -          networks: ["fdb0:5860:4ba8::1/64"]
> -      port lrp-f264e7
> -          mac: "fa:16:3e:fc:c8:da"
> -          networks: ["10.0.0.1/26"]
> -      nat 788c6d
> -          external ip: "172.24.4.8"
> -          logical ip: "10.0.0.6"
> -          type: "dnat_and_snat"
> -      nat 80914c
> -          external ip: "172.24.4.9"
> -          logical ip: "10.0.0.0/26"
> -          type: "snat"
> -  ...
> -
> -What we see is:
> -
> -* VM ``d`` is on the "private" switch under its private IP address
> -  10.0.0.8.  The "private" switch is connected to "router1" via two
> -  router ports (one for IPv4, one for IPv6).
> -
> -* The "public" switch is connected to "router1" and to the physical
> -  network via a "localnet" port.
> -
> -* "router1" is in the middle between "private" and "public".  In
> -  addition to the router ports that connect to these switches, it has
> -  "nat" entries that direct network address translation.  The
> -  translation between floating IP address 172.24.4.8 and private
> -  address 10.0.0.8 makes perfect sense.
> -
> -When the NB DB gets translated into logical flows at the southbound
> -layer, the "nat" entries get translated into IP matches that then
> -invoke "ct_snat" and "ct_dnat" actions.  The details are intricate,
> -but you can get some of the idea by just looking for relevant flows::
> -
> -  $ ovn-sbctl lflow-list router1 | abbrev | grep nat | grep -E
> '172.24.4.8|10.0.0.8'
> -    table=3 (lr_in_unsnat       ), priority=100  , match=(ip && ip4.dst
> == 172.24.4.8 && inport == "lrp-ae9b52" &&
> is_chassis_resident("cr-lrp-ae9b52")), action=(ct_snat;)
> -    table=3 (lr_in_unsnat       ), priority=50   , match=(ip && ip4.dst
> == 172.24.4.8), action=(reg9[0] = 1; next;)
> -    table=4 (lr_in_dnat         ), priority=100  , match=(ip && ip4.dst
> == 172.24.4.8 && inport == "lrp-ae9b52" &&
> is_chassis_resident("cr-lrp-ae9b52")), action=(ct_dnat(10.0.0.6);)
> -    table=4 (lr_in_dnat         ), priority=50   , match=(ip && ip4.dst
> == 172.24.4.8), action=(reg9[0] = 1; next;)
> -    table=1 (lr_out_snat        ), priority=33   , match=(ip && ip4.src
> == 10.0.0.6 && outport == "lrp-ae9b52" &&
> is_chassis_resident("cr-lrp-ae9b52")), action=(ct_snat(172.24.4.8);)
> -
> -Let's take a look at how a packet passes through this whole gauntlet.
> -The first two stanzas just show the packet traveling through the
> -"public" network and being forwarded to the "router1" network::
> -
> -  $ ovn-trace public 'inport ==
> "provnet-d1ac2896-18a7-4bca-8f46-b21e2370e5b1" && eth.src ==
> 00:01:02:03:04:05 && eth.dst == fa:16:3e:b2:d2:67 && ip4.src == 172.24.4.1
> && ip4.dst == 172.24.4.8 && ip.ttl == 64 && icmp4.type==8'
> -  ...
> -  ingress(dp="public", inport="provnet-d1ac28")
> -  ---------------------------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "provnet-d1ac28",
> priority 50, uuid 8d86fb06
> -      next;
> -  10. ls_in_arp_rsp (ovn-northd.c:3266): inport == "provnet-d1ac28",
> priority 100, uuid 21313eff
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3571): eth.dst == fa:16:3e:b2:d2:67 &&
> is_chassis_resident("cr-lrp-ae9b52"), priority 50, uuid 7f28f51f
> -      outport = "ae9b52";
> -      output;
> -
> -  egress(dp="public", inport="provnet-d1ac28", outport="ae9b52")
> -  --------------------------------------------------------------
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "ae9b52",
> priority 50, uuid 72fea396
> -      output;
> -      /* output to "ae9b52", type "patch" */
> -
> -In "router1", first the ``ct_snat`` action without an argument
> -attempts to "un-SNAT" the packet.  ovn-trace treats this as a no-op,
> -because it doesn't have any state for tracking connections.  As an
> -alternative, it invokes ``ct_dnat(10.0.0.8)`` to NAT the destination
> -IP::
> -
> -  ingress(dp="router1", inport="lrp-ae9b52")
> -  ------------------------------------------
> -   0. lr_in_admission (ovn-northd.c:4071): eth.dst == fa:16:3e:b2:d2:67
> && inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52"), priority
> 50, uuid 8c6945c2
> -      next;
> -   3. lr_in_unsnat (ovn-northd.c:4591): ip && ip4.dst == 172.24.4.8 &&
> inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52"), priority
> 100, uuid e922f541
> -      ct_snat;
> -
> -  ct_snat /* assuming no un-snat entry, so no change */
> -  -----------------------------------------------------
> -   4. lr_in_dnat (ovn-northd.c:4649): ip && ip4.dst == 172.24.4.8 &&
> inport == "lrp-ae9b52" && is_chassis_resident("cr-lrp-ae9b52"), priority
> 100, uuid 02f41b79
> -      ct_dnat(10.0.0.6);
> -
> -Still in "router1", the routing and output steps transmit the packet
> -to the "private" network::
> -
> -  ct_dnat(ip4.dst=10.0.0.6)
> -  -------------------------
> -   5. lr_in_ip_routing (ovn-northd.c:3782): ip4.dst == 10.0.0.0/26,
> priority 53, uuid 86e005b0
> -      ip.ttl--;
> -      reg0 = ip4.dst;
> -      reg1 = 10.0.0.1;
> -      eth.src = fa:16:3e:fc:c8:da;
> -      outport = "lrp-f264e7";
> -      flags.loopback = 1;
> -      next;
> -   6. lr_in_arp_resolve (ovn-northd.c:5088): outport == "lrp-f264e7" &&
> reg0 == 10.0.0.6, priority 100, uuid 2963d67c
> -      eth.dst = fa:16:3e:c1:f5:a2;
> -      next;
> -   8. lr_in_arp_request (ovn-northd.c:5260): 1, priority 0, uuid eea419b7
> -      output;
> -
> -  egress(dp="router1", inport="lrp-ae9b52", outport="lrp-f264e7")
> -  ---------------------------------------------------------------
> -   3. lr_out_delivery (ovn-northd.c:5288): outport == "lrp-f264e7",
> priority 100, uuid 42dadc23
> -      output;
> -      /* output to "lrp-f264e7", type "patch" */
> -
> -In the "private" network, the packet passes through VM ``d``'s
> -firewall and is output to ``d``::
> -
> -  ingress(dp="private", inport="f264e7")
> -  --------------------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "f264e7", priority
> 50, uuid 5b721214
> -      next;
> -   3. ls_in_pre_acl (ovn-northd.c:2624): ip && inport == "f264e7",
> priority 110, uuid 5bdc3209
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:c1:f5:a2,
> priority 50, uuid 7957f80f
> -      outport = "dp";
> -      output;
> -
> -  egress(dp="private", inport="f264e7", outport="dp")
> -  ---------------------------------------------------
> -   1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid 4981c79d
> -      reg0[0] = 1;
> -      next;
> -   2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority
> 100, uuid 247e02eb
> -      ct_next;
> -
> -  ct_next(ct_state=est|trk /* default (use --ct to customize) */)
> -  ---------------------------------------------------------------
> -   4. ls_out_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl &&
> ct_label.blocked == 0 && (outport == "dp" && ip4 && ip4.src == 0.0.0.0/0
> && icmp4), priority 2002, uuid b860fc9f
> -      next;
> -   7. ls_out_port_sec_ip (ovn-northd.c:2364): outport == "dp" && eth.dst
> == fa:16:3e:c1:f5:a2 && ip4.dst == {255.255.255.255, 224.0.0.0/4,
> 10.0.0.6}, priority 90, uuid 15655a98
> -      next;
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "dp" && eth.dst
> == {fa:16:3e:c1:f5:a2}, priority 50, uuid 5916f94b
> -      output;
> -      /* output to "dp", type "" */
> -
> -IPv6
> -----
> -
> -OVN supports IPv6 logical routing.  Let's try it out.
> -
> -The first step is to add an IPv6 subnet to networks ``n1`` and ``n2``,
> -then attach those subnets to our router ``r``.  As usual, though
> -OpenStack can assign addresses itself, we use fixed ones to make the
> -discussion easier::
> -
> -  $ openstack subnet create --ip-version 6 --subnet-range fc11::/64
> --network n1 n1subnet6
> -  $ openstack subnet create --ip-version 6 --subnet-range fc22::/64
> --network n2 n2subnet6
> -  $ openstack router add subnet r n1subnet6
> -  $ openstack router add subnet r n2subnet6
> -
> -Then we add an IPv6 address to each of our VMs::
> -
> -  $ A_PORT_ID=$(openstack port list --server a -f value -c ID)
> -  $ openstack port set --fixed-ip subnet=n1subnet6,ip-address=fc11::5
> $A_PORT_ID
> -  $ B_PORT_ID=$(openstack port list --server b -f value -c ID)
> -  $ openstack port set --fixed-ip subnet=n1subnet6,ip-address=fc11::6
> $B_PORT_ID
> -  $ C_PORT_ID=$(openstack port list --server c -f value -c ID)
> -  $ openstack port set --fixed-ip subnet=n2subnet6,ip-address=fc22::7
> $C_PORT_ID
> -
> -At least for me, the new IPv6 addresses didn't automatically get
> -propagated into the VMs.  To do it by hand, pull up the console for
> -``a`` and run::
> -
> -  $ sudo ip addr add fc11::5/64 dev eth0
> -  $ sudo ip route add via fc11::1
> -
> -Then in ``b``::
> -
> -  $ sudo ip addr add fc11::6/64 dev eth0
> -  $ sudo ip route add via fc11::1
> -
> -Finally in ``c``::
> -
> -  $ sudo ip addr add fc22::7/64 dev eth0
> -  $ sudo ip route add via fc22::1
> -
> -Now you should have working IPv6 routing through router ``r``.  The
> -relevant parts of the NB DB look like the following.  The interesting
> -parts are the new ``fc11::`` and ``fc22::`` addresses on the ports in
> -``n1`` and ``n2`` and the new IPv6 router ports in ``r``::
> -
> -  $ ovn-nbctl show | abbrev
> -  ...
> -  switch f51234 (neutron-332346) (aka n2)
> -      port 1a8162
> -          type: router
> -          router-port: lrp-1a8162
> -      port 82b983
> -          type: router
> -          router-port: lrp-82b983
> -      port 2e585f (aka cp)
> -          addresses: ["fa:16:3e:89:f2:36 10.1.2.7 fc22::7"]
> -  switch 3eb263 (neutron-5b6baf) (aka n1)
> -      port ad952e
> -          type: router
> -          router-port: lrp-ad952e
> -      port c29d41 (aka bp)
> -          addresses: ["fa:16:3e:99:7a:17 10.1.1.6 fc11::6"]
> -      port 820c08 (aka ap)
> -          addresses: ["fa:16:3e:a9:4c:c7 10.1.1.5 fc11::5"]
> -      port 17d870
> -          type: router
> -          router-port: lrp-17d870
> -  ...
> -  router dde06c (neutron-f88ebc) (aka r)
> -      port lrp-1a8162
> -          mac: "fa:16:3e:06:de:ad"
> -          networks: ["fc22::1/64"]
> -      port lrp-82b983
> -          mac: "fa:16:3e:19:9f:46"
> -          networks: ["10.1.2.1/24"]
> -      port lrp-ad952e
> -          mac: "fa:16:3e:ef:2f:8b"
> -          networks: ["fc11::1/64"]
> -      port lrp-17d870
> -          mac: "fa:16:3e:f6:e2:8f"
> -          networks: ["10.1.1.1/24"]
> -
> -Try tracing a packet from ``a`` to ``c``.  The results correspond
> -closely to those for IPv4 which we already discussed back under
> -`Routing`_::
> -
> -  $ N1SUBNET6_MAC=$(ovn-nbctl --bare --columns=mac find
> logical_router_port networks=\"fc11::1/64\")
> -  $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst ==
> '$N1SUBNET6_MAC' && ip6.src == fc11::5 && ip6.dst == fc22::7 && ip.ttl ==
> 64 && icmp6.type == 8'
> -  ...
> -  ingress(dp="n1", inport="ap")
> -  -----------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src ==
> {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
> -      next;
> -   1. ls_in_port_sec_ip (ovn-northd.c:2390): inport == "ap" && eth.src ==
> fa:16:3e:a9:4c:c7 && ip6.src == {fe80::f816:3eff:fea9:4cc7, fc11::5},
> priority 90, uuid 604810ea
> -      next;
> -   3. ls_in_pre_acl (ovn-northd.c:2646): ip, priority 100, uuid 46c089e6
> -      reg0[0] = 1;
> -      next;
> -   5. ls_in_pre_stateful (ovn-northd.c:2764): reg0[0] == 1, priority 100,
> uuid d1941634
> -      ct_next;
> -
> -  ct_next(ct_state=est|trk /* default (use --ct to customize) */)
> -  ---------------------------------------------------------------
> -   6. ls_in_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl &&
> ct_label.blocked == 0 && (inport == "ap" && ip6), priority 2002, uuid
> 7fdd607e
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:ef:2f:8b,
> priority 50, uuid e1d87fc5
> -      outport = "ad952e";
> -      output;
> -
> -  egress(dp="n1", inport="ap", outport="ad952e")
> -  ----------------------------------------------
> -   1. ls_out_pre_acl (ovn-northd.c:2626): ip && outport == "ad952e",
> priority 110, uuid 88f68988
> -      next;
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "ad952e",
> priority 50, uuid 5935755e
> -      output;
> -      /* output to "ad952e", type "patch" */
> -
> -  ingress(dp="r", inport="lrp-ad952e")
> -  ------------------------------------
> -   0. lr_in_admission (ovn-northd.c:4071): eth.dst == fa:16:3e:ef:2f:8b
> && inport == "lrp-ad952e", priority 50, uuid ddfeb712
> -      next;
> -   5. lr_in_ip_routing (ovn-northd.c:3782): ip6.dst == fc22::/64,
> priority 129, uuid cc2130ec
> -      ip.ttl--;
> -      xxreg0 = ip6.dst;
> -      xxreg1 = fc22::1;
> -      eth.src = fa:16:3e:06:de:ad;
> -      outport = "lrp-1a8162";
> -      flags.loopback = 1;
> -      next;
> -   6. lr_in_arp_resolve (ovn-northd.c:5122): outport == "lrp-1a8162" &&
> xxreg0 == fc22::7, priority 100, uuid bcf75288
> -      eth.dst = fa:16:3e:89:f2:36;
> -      next;
> -   8. lr_in_arp_request (ovn-northd.c:5260): 1, priority 0, uuid 6dacdd82
> -      output;
> -
> -  egress(dp="r", inport="lrp-ad952e", outport="lrp-1a8162")
> -  ---------------------------------------------------------
> -   3. lr_out_delivery (ovn-northd.c:5288): outport == "lrp-1a8162",
> priority 100, uuid 5260dfc5
> -      output;
> -      /* output to "lrp-1a8162", type "patch" */
> -
> -  ingress(dp="n2", inport="1a8162")
> -  ---------------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "1a8162", priority
> 50, uuid 10957d1b
> -      next;
> -   3. ls_in_pre_acl (ovn-northd.c:2624): ip && inport == "1a8162",
> priority 110, uuid a27ebd00
> -      next;
> -  13. ls_in_l2_lkup (ovn-northd.c:3529): eth.dst == fa:16:3e:89:f2:36,
> priority 50, uuid dcafb3e9
> -      outport = "cp";
> -      output;
> -
> -  egress(dp="n2", inport="1a8162", outport="cp")
> -  ----------------------------------------------
> -   1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid cd9cfa74
> -      reg0[0] = 1;
> -      next;
> -   2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority
> 100, uuid 9e8e22c5
> -      ct_next;
> -
> -  ct_next(ct_state=est|trk /* default (use --ct to customize) */)
> -  ---------------------------------------------------------------
> -   4. ls_out_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl &&
> ct_label.blocked == 0 && (outport == "cp" && ip6 && ip6.src ==
> $as_ip6_0fc1b6cf_f925_49e6_8f00_6dd13beca9dc), priority 2002, uuid 12fc96f9
> -      next;
> -   7. ls_out_port_sec_ip (ovn-northd.c:2390): outport == "cp" && eth.dst
> == fa:16:3e:89:f2:36 && ip6.dst == {fe80::f816:3eff:fe89:f236, ff00::/8,
> fc22::7}, priority 90, uuid c622596a
> -      next;
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "cp" && eth.dst
> == {fa:16:3e:89:f2:36}, priority 50, uuid 0242cdc3
> -      output;
> -      /* output to "cp", type "" */
> -
> -ACLs
> -----
> -
> -Let's explore how ACLs work in OpenStack and OVN.  In OpenStack, ACL
> -rules are part of "security groups", which are "default deny", that
> -is, packets are not allowed by default and the rules added to security
> -groups serve to allow different classes of packets.  The default group
> -(named "default") that is assigned to each of our VMs so far allows
> -all traffic from our other VMs, which isn't very interesting for
> -testing.  So, let's create a new security group, which we'll name
> -"custom", add rules to it that allow incoming SSH and ICMP traffic,
> -and apply this security group to VM ``c``::
> -
> -  $ openstack security group create custom
> -  $ openstack security group rule create --dst-port 22 custom
> -  $ openstack security group rule create --protocol icmp custom
> -  $ openstack server remove security group c default
> -  $ openstack server add security group c custom
> -
> -Now we can do some experiments to test security groups.  From the
> -console on ``a`` or ``b``, it should now be possible to "ping" ``c``
> -or to SSH to it, but attempts to initiate connections on other ports
> -should be blocked.  (You can try to connect on another port with
> -``ssh -p PORT IP`` or ``nc PORT IP``.)  Connection attempts should
> -time out rather than receive the "connection refused" or "connection
> -reset" error that you would see between ``a`` and ``b``.
> -
> -It's also possible to test ACLs via ovn-trace, with one new wrinkle.
> -ovn-trace can't simulate connection tracking state in the network, so
> -by default it assumes that every packet represents an established
> -connection.  That's good enough for what we've been doing so far, but
> -for checking properties of security groups we want to look at more
> -detail.
> -
> -If you look back at the VM-to-VM traces we've done until now, you can
> -see that they execute two ``ct_next`` actions:
> -
> -* The first of these is for the packet passing outward through the
> -  source VM's firewall.  We can tell ovn-trace to treat the packet as
> -  starting a new connection or adding to an established connection by
> -  adding a ``--ct`` option: ``--ct new`` or ``--ct est``,
> -  respectively.  The latter is the default and therefore what we've
> -  been using so far.  We can also use ``--ct est,rpl``, which in
> -  addition to ``--ct est`` means that the connection was initiated by
> -  the destination VM rather than by the VM sending this packet.
> -
> -* The second is for the packet passing inward through the destination
> -  VM's firewall.  For this one, it makes sense to tell ovn-trace that
> -  the packet is starting a new connection, with ``--ct new``, or that
> -  it is a packet sent in reply to a connection established by the
> -  destination VM, with ``--ct est,rpl``.
> -
> -ovn-trace uses the ``--ct`` options in order, so if we want to
> -override the second ``ct_next`` behavior we have to specify two
> -options.
> -
> -Another useful ovn-trace option for this testing is ``--minimal``,
> -which reduces the amount of output.  In this case we're really just
> -interested in finding out whether the packet reaches the destination
> -VM, that is, whether there's an eventual ``output`` action to ``c``,
> -so ``--minimal`` works fine and the output is easier to read.
> -
> -Try a few traces.  For example:
> -
> -* VM ``a`` initiates a new SSH connection to ``c``::
> -
> -    $ ovn-trace --ct new --ct new --minimal n1 'inport == "ap" && eth.src
> == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip4.src == 10.1.1.5 &&
> ip4.dst == 10.1.2.7 && ip.ttl == 64 && tcp.dst == 22'
> -    ...
> -    ct_next(ct_state=new|trk) {
> -        ip.ttl--;
> -        eth.src = fa:16:3e:19:9f:46;
> -        eth.dst = fa:16:3e:89:f2:36;
> -        ct_next(ct_state=new|trk) {
> -            output("cp");
> -        };
> -    };
> -
> -  This succeeds, as you can see since there is an ``output`` action.
> -
> -* VM ``a`` initiates a new Telnet connection to ``c``::
> -
> -    $ ovn-trace --ct new --ct new --minimal n1 'inport == "ap" && eth.src
> == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip4.src == 10.1.1.5 &&
> ip4.dst == 10.1.2.7 && ip.ttl == 64 && tcp.dst == 23'
> -    ct_next(ct_state=new|trk) {
> -        ip.ttl--;
> -        eth.src = fa:16:3e:19:9f:46;
> -        eth.dst = fa:16:3e:89:f2:36;
> -        ct_next(ct_state=new|trk);
> -    };
> -
> -  This fails, as you can see from the lack of an ``output`` action.
> -
> -* VM ``a`` replies to a packet that is part of a Telnet connection
> -  originally initiated by ``c``::
> -
> -    $ ovn-trace --ct est,rpl --ct est,rpl --minimal n1 'inport == "ap" &&
> eth.src == '$AP_MAC' && eth.dst == '$N1SUBNET6_MAC' && ip4.src == 10.1.1.5
> && ip4.dst == 10.1.2.7 && ip.ttl == 64 && tcp.dst == 23'
> -    ...
> -    ct_next(ct_state=est|rpl|trk) {
> -        ip.ttl--;
> -        eth.src = fa:16:3e:19:9f:46;
> -        eth.dst = fa:16:3e:89:f2:36;
> -        ct_next(ct_state=est|rpl|trk) {
> -            output("cp");
> -        };
> -    };
> -
> -  This succeeds, as you can see from the ``output`` action, since
> -  traffic received in reply to an outgoing connection is always
> -  allowed.
> -
> -DHCP
> -----
> -
> -As a final demonstration of the OVN architecture, let's examine the
> -DHCP implementation.  Like switching, routing, and NAT, the OVN
> -implementation of DHCP involves configuration in the NB DB and logical
> -flows in the SB DB.
> -
> -Let's look at the DHCP support for ``a``'s port ``ap``.  The port's
> -Logical_Switch_Port record shows that ``ap`` has DHCPv4 options::
> -
> -  $ ovn-nbctl list logical_switch_port ap | abbrev
> -  _uuid               : ef17e5
> -  addresses           : ["fa:16:3e:a9:4c:c7 10.1.1.5 fc11::5"]
> -  dhcpv4_options      : 165974
> -  dhcpv6_options      : 26f7cd
> -  dynamic_addresses   : []
> -  enabled             : true
> -  external_ids        : {"neutron:port_name"=ap}
> -  name                : "820c08"
> -  options             : {}
> -  parent_name         : []
> -  port_security       : ["fa:16:3e:a9:4c:c7 10.1.1.5 fc11::5"]
> -  tag                 : []
> -  tag_request         : []
> -  type                : ""
> -  up                  : true
> -
> -We can then list them either by UUID or, more easily, by port name::
> -
> -  $ ovn-nbctl list dhcp_options ap | abbrev
> -  _uuid               : 165974
> -  cidr                : "10.1.1.0/24"
> -  external_ids        : {subnet_id="5e67e7"}
> -  options             : {lease_time="43200", mtu="1442",
> router="10.1.1.1", server_id="10.1.1.1", server_mac="fa:16:3e:bb:94:72"}
> -
> -These options show the basic DHCP configuration for the subnet.  They
> -do not include the IP address itself, which comes from the
> -Logical_Switch_Port record.  This allows a whole Neutron subnet to
> -share a single DHCP_Options record.  You can see this sharing in
> -action, if you like, by listing the record for port ``bp``, which is
> -on the same subnet as ``ap``, and see that it is the same record as
> before::
> -
> -  $ ovn-nbctl list dhcp_options bp | abbrev
> -  _uuid               : 165974
> -  cidr                : "10.1.1.0/24"
> -  external_ids        : {subnet_id="5e67e7"}
> -  options             : {lease_time="43200", mtu="1442",
> router="10.1.1.1", server_id="10.1.1.1", server_mac="fa:16:3e:bb:94:72"}
> -
> -You can take another look at the southbound flow table if you like,
> -but the best demonstration is to trace a DHCP packet.  The following
> -is a trace of a DHCP request inbound from ``ap``.  The first part is
> -just the usual travel through the firewall::
> -
> -  $ ovn-trace n1 'inport == "ap" && eth.src == '$AP_MAC' && eth.dst ==
> ff:ff:ff:ff:ff:ff && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst
> == 67 && ip.ttl == 1'
> -  ...
> -  ingress(dp="n1", inport="ap")
> -  -----------------------------
> -   0. ls_in_port_sec_l2 (ovn-northd.c:3234): inport == "ap" && eth.src ==
> {fa:16:3e:a9:4c:c7}, priority 50, uuid 6dcc418a
> -      next;
> -   1. ls_in_port_sec_ip (ovn-northd.c:2325): inport == "ap" && eth.src ==
> fa:16:3e:a9:4c:c7 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 &&
> udp.src == 68 && udp.dst == 67, priority 90, uuid e46bed6f
> -      next;
> -   3. ls_in_pre_acl (ovn-northd.c:2646): ip, priority 100, uuid 46c089e6
> -      reg0[0] = 1;
> -      next;
> -   5. ls_in_pre_stateful (ovn-northd.c:2764): reg0[0] == 1, priority 100,
> uuid d1941634
> -      ct_next;
> -
> -The next part is the new part.  First, an ACL in table 6 allows a DHCP
> -request to pass through.  In table 11, the special ``put_dhcp_opts``
> -action replaces a DHCPDISCOVER or DHCPREQUEST packet by a
> -reply.  Table 12 flips the packet's source and destination and sends
> -it back the way it came in::
> -
> -   6. ls_in_acl (ovn-northd.c:2925): !ct.new && ct.est && !ct.rpl &&
> ct_label.blocked == 0 && (inport == "ap" && ip4 && ip4.dst ==
> {255.255.255.255, 10.1.1.0/24} && udp && udp.src == 68 && udp.dst == 67),
> priority 2002, uuid 9c90245d
> -      next;
> -  11. ls_in_dhcp_options (ovn-northd.c:3409): inport == "ap" && eth.src
> == fa:16:3e:a9:4c:c7 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 &&
> udp.src == 68 && udp.dst == 67, priority 100, uuid 8d63f29c
> -      reg0[3] = put_dhcp_opts(offerip = 10.1.1.5, lease_time = 43200, mtu
> = 1442, netmask = 255.255.255.0, router = 10.1.1.1, server_id = 10.1.1.1);
> -      /* We assume that this packet is DHCPDISCOVER or DHCPREQUEST. */
> -      next;
> -  12. ls_in_dhcp_response (ovn-northd.c:3438): inport == "ap" && eth.src
> == fa:16:3e:a9:4c:c7 && ip4 && udp.src == 68 && udp.dst == 67 && reg0[3],
> priority 100, uuid 995eeaa9
> -      eth.dst = eth.src;
> -      eth.src = fa:16:3e:bb:94:72;
> -      ip4.dst = 10.1.1.5;
> -      ip4.src = 10.1.1.1;
> -      udp.src = 67;
> -      udp.dst = 68;
> -      outport = inport;
> -      flags.loopback = 1;
> -      output;
> -
> -Then the last part is just traveling back through the firewall to VM
> -``a``::
> -
> -  egress(dp="n1", inport="ap", outport="ap")
> -  ------------------------------------------
> -   1. ls_out_pre_acl (ovn-northd.c:2648): ip, priority 100, uuid 3752b746
> -      reg0[0] = 1;
> -      next;
> -   2. ls_out_pre_stateful (ovn-northd.c:2766): reg0[0] == 1, priority
> 100, uuid 0c066ea1
> -      ct_next;
> -
> -  ct_next(ct_state=est|trk /* default (use --ct to customize) */)
> -  ---------------------------------------------------------------
> -   4. ls_out_acl (ovn-northd.c:3008): outport == "ap" && eth.src ==
> fa:16:3e:bb:94:72 && ip4.src == 10.1.1.1 && udp && udp.src == 67 && udp.dst
> == 68, priority 34000, uuid 0b383e77
> -      ct_commit;
> -      next;
> -   7. ls_out_port_sec_ip (ovn-northd.c:2364): outport == "ap" && eth.dst
> == fa:16:3e:a9:4c:c7 && ip4.dst == {255.255.255.255, 224.0.0.0/4,
> 10.1.1.5}, priority 90, uuid 7b8cbcd5
> -      next;
> -   8. ls_out_port_sec_l2 (ovn-northd.c:3654): outport == "ap" && eth.dst
> == {fa:16:3e:a9:4c:c7}, priority 50, uuid b874ece8
> -      output;
> -      /* output to "ap", type "" */
> -
> -Further Directions
> -------------------
> -
> -We've looked at a fair bit of how OVN works and how it interacts with
> -OpenStack.  If you still have some interest, then you might want to
> -explore some of these directions:
> -
> -* Adding more than one hypervisor ("compute node", in OpenStack
> -  parlance).  OVN connects compute nodes by tunneling packets with the
> -  STT or Geneve protocols.  OVN scales to 1000 compute nodes or more,
> -  but two compute nodes demonstrate the principle.  All of the tools
> -  and techniques we demonstrated also work with multiple compute
> -  nodes.
> -
> -* Container support.  OVN supports seamlessly connecting VMs to
> -  containers, whether the containers are hosted on "bare metal" or
> -  nested inside VMs.  OpenStack support for containers, however, is
> -  still evolving, and too difficult to incorporate into the tutorial
> -  at this point.
> -
> -* Other kinds of gateways.  In addition to floating IPs with NAT, OVN
> -  supports directly attaching VMs to a physical network and connecting
> -  logical switches to VTEP hardware.
> diff --git a/Documentation/tutorials/ovn-rbac.rst
> b/Documentation/tutorials/ovn-rbac.rst
> deleted file mode 100644
> index ec163e2df..000000000
> --- a/Documentation/tutorials/ovn-rbac.rst
> +++ /dev/null
> @@ -1,134 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -=============================================
> -OVN Role-Based Access Control (RBAC) Tutorial
> -=============================================
> -
> -This document provides a step-by-step guide for setting up role-based
> access
> -control (RBAC) in OVN. In OVN, hypervisors (chassis) read and write the
> -southbound database to do configuration. Without restricting hypervisor's
> -access to the southbound database, a compromised hypervisor might disrupt
> the
> -entire OVN deployment by corrupting the database. RBAC ensures that each
> -hypervisor can only modify its own data and thus improves the security of
> OVN.
> -More details about the RBAC design can be found in
> ``ovn-architecture``\(7)
> -manpage.
> -
> -This document assumes OVN is installed in your system and runs normally.
> -
> -.. _gen-certs-keys:
> -
> -Generating Certificates and Keys
> ---------------------------------
> -
> -In the OVN RBAC deployment, ovn-controller connects to the southbound
> database
> -via SSL connection. The southbound database uses CA-signed certificate to
> -authenticate ovn-controller.
> -
> -Suppose there are three machines in your deployment. `machine_1` runs
> -`chassis_1` and has IP address `machine_1-ip`. `machine_2` runs
> `chassis_2` and
> -has IP address `machine_2-ip`. `machine_3` hosts southbound database and
> has IP
> -address `machine_3-ip`. `machine_3` also hosts public key infrastructure
> (PKI).
> -
> -1. Initiate PKI.
> -
> -   In `machine_3`::
> -
> -      $ ovs-pki init
> -
> -2. Generate southbound database's certificate request. Sign the
> certificate
> -   request with the CA key.
> -
> -   In `machine_3`::
> -
> -      $ ovs-pki req -u sbdb
> -      $ ovs-pki sign sbdb switch
> -
> -3. Generate chassis certificate requests. Copy the certificate requests to
> -   `machine_3`.
> -
> -   In `machine_1`::
> -
> -      $ ovs-pki req -u chassis_1
> -      $ scp chassis_1-req.pem \
> -                    machine_3 at 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 at 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 at machine_1-ip:/path/to/chassis_1-cert.pem
> -      $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \
> -                    machine_1 at machine_1-ip:/path/to/cacert.pem
> -      $ scp chassis_2-cert.pem \
> -                    machine_2 at machine_2-ip:/path/to/chassis_2-cert.pem
> -      $ scp /var/lib/openvswitch/pki/switchca/cacert.pem \
> -                    machine_2 at machine_2-ip:/path/to/cacert.pem
> -
> -Configuring RBAC
> -----------------
> -
> -1. Set certificate, private key, and CA certificate for the southbound
> -   database. Configure the southbound database to listen on SSL
> connection and
> -   enforce role-based access control.
> -
> -   In `machine_3`::
> -
> -      $ ovn-sbctl set-ssl /path/to/sbdb-privkey.pem \
> -                          /path/to/sbdb-cert.pem /path/to/cacert.pem
> -      $ ovn-sbctl set-connection role=ovn-controller pssl:6642
> -
> -2. Set certificate, private key, and CA certificate for `chassis_1` and
> -   `chassis_2`. Configure `chassis_1` and `chassis_2` to connect
> southbound
> -   database via SSL.
> -
> -   In `machine_1`::
> -
> -      $ ovs-vsctl set-ssl /path/to/chassis_1-privkey.pem \
> -                    /path/to/chassis_1-cert.pem /path/to/cacert.pem
> -      $ ovs-vsctl set open_vswitch . \
> -                    external_ids:ovn-remote=ssl:machine_3-ip:6642
> -
> -   In `machine_2`::
> -
> -      $ ovs-vsctl set-ssl /path/to/chassis_2-privkey.pem \
> -                    /path/to/chassis_2-cert.pem /path/to/cacert.pem
> -      $ ovs-vsctl set open_vswitch . \
> -                    external_ids:ovn-remote=ssl:machine_3-ip:6642
> diff --git a/Documentation/tutorials/ovn-sandbox.rst
> b/Documentation/tutorials/ovn-sandbox.rst
> deleted file mode 100644
> index b906b799d..000000000
> --- a/Documentation/tutorials/ovn-sandbox.rst
> +++ /dev/null
> @@ -1,177 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -===========
> -OVN Sandbox
> -===========
> -
> -This tutorial shows you how to explore features using ``ovs-sandbox`` as a
> -simulated test environment.  It's assumed that you have an understanding
> of OVS
> -before going through this tutorial. Detail about OVN is covered in
> -ovn-architecture_, but this tutorial lets you quickly see it in action.
> -
> -Getting Started
> ----------------
> -
> -For some general information about ``ovs-sandbox``, see the "Getting
> Started"
> -section of :doc:`ovs-advanced`.
> -
> -``ovs-sandbox`` does not include OVN support by default.  To enable OVN,
> you
> -must pass the ``--ovn`` flag.  For example, if running it straight from
> the OVS
> -git tree you would run::
> -
> -    $ make sandbox SANDBOXFLAGS="--ovn"
> -
> -Running the sandbox with OVN enabled does the following additional steps
> to the
> -environment:
> -
> -1. Creates the ``OVN_Northbound`` and ``OVN_Southbound`` databases as
> described in
> -   `ovn-nb(5)`_ and `ovn-sb(5)`_.
> -
> -2. Creates a backup server for ``OVN_Southbond`` database. Sandbox launch
> -   screen provides the instructions on accessing the backup database.
> However
> -   access to the backup server is not required to go through the tutorial.
> -
> -3. Creates the ``hardware_vtep`` database as described in `vtep(5)`_.
> -
> -4. Runs the `ovn-northd(8)`_, `ovn-controller(8)`_, and
> -   `ovn-controller-vtep(8)`_ daemons.
> -
> -5. Makes OVN and VTEP utilities available for use in the environment,
> including
> -   `vtep-ctl(8)`_, `ovn-nbctl(8)`_, and `ovn-sbctl(8)`_.
> -
> -Using GDB
> ----------
> -
> -GDB support is not required to go through the tutorial. See the "Using
> GDB"
> -section of :doc:`ovs-advanced` for more info. Additional flags exist for
> -launching the debugger for the OVN programs::
> -
> -    --gdb-ovn-northd
> -    --gdb-ovn-controller
> -    --gdb-ovn-controller-vtep
> -
> -Creating OVN Resources
> -----------------------
> -
> -Once you have ``ovs-sandbox`` running with OVN enabled, you can start
> using OVN
> -utilities to create resources in OVN.  As an example, we will create an
> -environment that has two logical switches connected by a logical router.
> -
> -Create the first logical switch with one port::
> -
> -    $ ovn-nbctl ls-add sw0
> -    $ ovn-nbctl lsp-add sw0 sw0-port1
> -    $ ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01
> 192.168.0.2"
> -
> -Create the second logical switch with one port::
> -
> -    $ ovn-nbctl ls-add sw1
> -    $ ovn-nbctl lsp-add sw1 sw1-port1
> -    $ ovn-nbctl lsp-set-addresses sw1-port1 "50:54:00:00:00:03 11.0.0.2"
> -
> -Create the logical router and attach both logical switches::
> -
> -    $ ovn-nbctl lr-add lr0
> -    $ ovn-nbctl lrp-add lr0 lrp0 00:00:00:00:ff:01 192.168.0.1/24
> -    $ ovn-nbctl lsp-add sw0 lrp0-attachment
> -    $ ovn-nbctl lsp-set-type lrp0-attachment router
> -    $ ovn-nbctl lsp-set-addresses lrp0-attachment 00:00:00:00:ff:01
> -    $ ovn-nbctl lsp-set-options lrp0-attachment router-port=lrp0
> -    $ ovn-nbctl lrp-add lr0 lrp1 00:00:00:00:ff:02 11.0.0.1/24
> -    $ ovn-nbctl lsp-add sw1 lrp1-attachment
> -    $ ovn-nbctl lsp-set-type lrp1-attachment router
> -    $ ovn-nbctl lsp-set-addresses lrp1-attachment 00:00:00:00:ff:02
> -    $ ovn-nbctl lsp-set-options lrp1-attachment router-port=lrp1
> -
> -View a summary of OVN's current logical configuration::
> -
> -    $ ovn-nbctl show
> -        switch 1396cf55-d176-4082-9a55-1c06cef626e4 (sw1)
> -            port lrp1-attachment
> -                addresses: ["00:00:00:00:ff:02"]
> -            port sw1-port1
> -                addresses: ["50:54:00:00:00:03 11.0.0.2"]
> -        switch 2c9d6d03-09fc-4e32-8da6-305f129b0d53 (sw0)
> -            port lrp0-attachment
> -                addresses: ["00:00:00:00:ff:01"]
> -            port sw0-port1
> -                addresses: ["50:54:00:00:00:01 192.168.0.2"]
> -        router f8377e8c-f75e-4fc8-8751-f3ea03c6dd98 (lr0)
> -            port lrp0
> -                mac: "00:00:00:00:ff:01"
> -                networks: ["192.168.0.1/24"]
> -            port lrp1
> -                mac: "00:00:00:00:ff:02"
> -                networks: ["11.0.0.1/24"]
> -
> -The ``tutorial`` directory of the OVS source tree includes a script
> -that runs all of the commands for you::
> -
> -    $ ./ovn-setup.sh
> -
> -Using ovn-trace
> ----------------
> -
> -Once you have configured resources in OVN, try using ``ovn-trace`` to see
> -how OVN would process a sample packet through its logical pipeline.
> -
> -For example, we can trace an IP packet from ``sw0-port1`` to
> ``sw1-port1``.
> -The ``--minimal`` output shows each visible action performed on the
> packet,
> -which includes:
> -
> -#. The logical router will decrement the IP TTL field.
> -#. The logical router will change the source and destination
> -   MAC addresses to reflect the next hop.
> -#. The packet will be output to ``sw1-port1``.
> -
> -::
> -
> -    $ ovn-trace --minimal sw0 'inport == "sw0-port1" \
> -    > && eth.src == 50:54:00:00:00:01 && ip4.src == 192.168.0.2 \
> -    > && eth.dst == 00:00:00:00:ff:01 && ip4.dst == 11.0.0.2 \
> -    > && ip.ttl == 64'
> -
> -    #
> ip,reg14=0x1,vlan_tci=0x0000,dl_src=50:54:00:00:00:01,dl_dst=00:00:00:00:ff:01,nw_src=192.168.0.2,nw_dst=11.0.0.2,nw_proto=0,nw_tos=0,nw_ecn=0,nw_ttl=64
> -    ip.ttl--;
> -    eth.src = 00:00:00:00:ff:02;
> -    eth.dst = 50:54:00:00:00:03;
> -    output("sw1-port1");
> -
> -The ``ovn-trace`` utility can also provide much more detail on how the
> packet
> -would be processed through OVN's logical pipeline, as well as correlate
> that
> -to OpenFlow flows programmed by ``ovn-controller``.  See the
> `ovn-trace(8)`_
> -man page for more detail.
> -
> -
> -.. _ovn-architecture:
> http://openvswitch.org/support/dist-docs/ovn-architecture.7.html
> -.. _ovn-nb(5): http://openvswitch.org/support/dist-docs/ovn-nb.5.html
> -.. _ovn-sb(5): http://openvswitch.org/support/dist-docs/ovn-sb.5.html
> -.. _vtep(5): http://openvswitch.org/support/dist-docs/vtep.5.html
> -.. _ovn-northd(8):
> http://openvswitch.org/support/dist-docs/ovn-northd.8.html
> -.. _ovn-controller(8):
> http://openvswitch.org/support/dist-docs/ovn-controller.8.html
> -.. _ovn-controller-vtep(8):
> http://openvswitch.org/support/dist-docs/ovn-controller-vtep.8.html
> -.. _vtep-ctl(8): http://openvswitch.org/support/dist-docs/vtep-ctl.8.html
> -.. _ovn-nbctl(8):
> http://openvswitch.org/support/dist-docs/ovn-nbctl.8.html
> -.. _ovn-sbctl(8):
> http://openvswitch.org/support/dist-docs/ovn-sbctl.8.html
> -.. _ovn-trace(8):
> http://openvswitch.org/support/dist-docs/ovn-trace.8.html
> diff --git a/Makefile.am b/Makefile.am
> index ff1f94b48..a03a0351a 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -495,6 +495,5 @@ include vtep/automake.mk
>  include datapath-windows/automake.mk
>  include datapath-windows/include/automake.mk
>  include windows/automake.mk
> -include ovn/automake.mk
>  include selinux/automake.mk
>  include build-aux/automake.mk
> diff --git a/NEWS b/NEWS
> index c5caa13d6..289d1e4f9 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -1,6 +1,9 @@
>  Post-v2.12.0
>  ---------------------
> -
> +   - OVN:
> +     * OVN has been removed from this repository. It now exists as a
> +       separate project. You can find it at
> +       https://github.com/ovn-org/ovn.git
>
>  v2.12.0 - xx xxx xxxx
>  ---------------------
> diff --git a/configure.ac b/configure.ac
> index e3603926b..64ce4cef0 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -148,7 +148,6 @@ AC_CONFIG_FILES([
>      ofproto/libofproto.sym
>      lib/libsflow.sym
>      lib/libopenvswitch.sym
> -    ovn/lib/libovn.sym
>      vtep/libvtep.sym])
>
>  OVS_ENABLE_OPTION([-Wall])
> @@ -210,8 +209,6 @@ dnl This makes sure that include/openflow gets created
> in the build directory.
>  AC_CONFIG_COMMANDS([include/openflow/openflow.h.stamp])
>
>  AC_CONFIG_COMMANDS([utilities/bugtool/dummy], [:])
> -AC_CONFIG_COMMANDS([ovn/dummy], [:])
> -AC_CONFIG_COMMANDS([ovn/utilities/dummy], [:])
>  AC_CONFIG_COMMANDS([ipsec/dummy], [:])
>
>  m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
> diff --git a/debian/.gitignore b/debian/.gitignore
> index 9ec70eb9c..441a8ffb7 100644
> --- a/debian/.gitignore
> +++ b/debian/.gitignore
> @@ -22,10 +22,5 @@
>  /openvswitch-test
>  /openvswitch-testcontroller
>  /openvswitch-vtep
> -/ovn-common
> -/ovn-controller-vtep
> -/ovn-host
> -/ovn-central
> -/ovn-docker
>  /python-openvswitch
>  /tmp
> diff --git a/debian/automake.mk b/debian/automake.mk
> index 8a8d43c9f..03a1d68c2 100644
> --- a/debian/automake.mk
> +++ b/debian/automake.mk
> @@ -52,28 +52,6 @@ EXTRA_DIST += \
>         debian/openvswitch-vtep.init \
>         debian/openvswitch-vtep.install \
>         debian/openvswitch-vtep.manpages \
> -       debian/ovn-central.dirs \
> -       debian/ovn-central.init \
> -       debian/ovn-central.install \
> -       debian/ovn-central.manpages \
> -       debian/ovn-central.postinst \
> -       debian/ovn-central.postrm \
> -       debian/ovn-central.template \
> -       debian/ovn-controller-vtep.init \
> -       debian/ovn-controller-vtep.install \
> -       debian/ovn-controller-vtep.manpages \
> -       debian/ovn-common.install \
> -       debian/ovn-common.manpages \
> -       debian/ovn-common.postinst \
> -       debian/ovn-common.postrm \
> -       debian/ovn-docker.install \
> -       debian/ovn-host.dirs \
> -       debian/ovn-host.init \
> -       debian/ovn-host.install \
> -       debian/ovn-host.manpages \
> -       debian/ovn-host.postinst \
> -       debian/ovn-host.postrm \
> -       debian/ovn-host.template \
>         debian/python-openvswitch.dirs \
>         debian/python-openvswitch.install \
>         debian/rules \
> diff --git a/debian/control b/debian/control
> index b97e99b92..2ad35f2ce 100644
> --- a/debian/control
> +++ b/debian/control
> @@ -119,84 +119,6 @@ Description: Open vSwitch switch implementations
>   openvswitch-switch provides the userspace components and utilities for
>   the Open vSwitch kernel-based switch.
>
> -Package: ovn-common
> -Architecture: linux-any
> -Depends: openvswitch-common (= ${binary:Version}),
> -         ${misc:Depends},
> -         ${shlibs:Depends}
> -Description: OVN common components
> - OVN, the Open Virtual Network, is a system to support virtual network
> - abstraction.  OVN complements the existing capabilities of OVS to add
> - native support for virtual network abstractions, such as virtual L2 and
> L3
> - overlays and security groups.
> - .
> - ovn-common provides components required by other OVN packages.
> -
> -Package: ovn-controller-vtep
> -Architecture: linux-any
> -Depends: ovn-common (= ${binary:Version}),
> -         ${misc:Depends},
> -         ${shlibs:Depends}
> -Description: OVN vtep controller
> - ovn-controller-vtep is the local controller daemon in
> - OVN, the Open Virtual Network, for VTEP enabled physical switches.
> - It connects up to the OVN Southbound database over the OVSDB protocol,
> - and down to the VTEP database over the OVSDB protocol.
> - .
> - ovn-controller-vtep provides the ovn-controller-vtep binary for
> controlling
> - vtep gateways.
> -
> -
> -Package: ovn-host
> -Architecture: linux-any
> -Depends: openvswitch-switch (= ${binary:Version}),
> -         openvswitch-common (= ${binary:Version}),
> -         ovn-common (= ${binary:Version}),
> -         ${misc:Depends},
> -         ${shlibs:Depends}
> -Description: OVN host components
> - OVN, the Open Virtual Network, is a system to support virtual network
> - abstraction.  OVN complements the existing capabilities of OVS to add
> - native support for virtual network abstractions, such as virtual L2 and
> L3
> - overlays and security groups.
> - .
> - ovn-host provides the userspace components and utilities for
> - OVN that can be run on every host/hypervisor.
> -
> -Package: ovn-central
> -Architecture: linux-any
> -Depends: openvswitch-switch (= ${binary:Version}),
> -         openvswitch-common (= ${binary:Version}),
> -         ovn-common (= ${binary:Version}),
> -         ${misc:Depends},
> -         ${shlibs:Depends}
> -Description: OVN central components
> - OVN, the Open Virtual Network, is a system to support virtual network
> - abstraction.  OVN complements the existing capabilities of OVS to add
> - native support for virtual network abstractions, such as virtual L2 and
> L3
> - overlays and security groups.
> - .
> - ovn-central provides the userspace daemons, utilities and
> - databases for OVN that is run at a central location.
> -
> -Package: ovn-docker
> -Architecture: linux-any
> -Depends: openvswitch-switch (= ${binary:Version}),
> -         openvswitch-common (= ${binary:Version}),
> -         python (>= 2.7),
> -         python-openvswitch (= ${source:Version}),
> -         ovn-common (= ${binary:Version}),
> -         ${misc:Depends},
> -         ${python:Depends},
> -         ${shlibs:Depends}
> -Description: OVN Docker drivers
> - OVN, the Open Virtual Network, is a system to support virtual network
> - abstraction.  OVN complements the existing capabilities of OVS to add
> - native support for virtual network abstractions, such as virtual L2 and
> L3
> - overlays and security groups.
> - .
> - ovn-docker provides the docker drivers for OVN.
> -
>  Package: openvswitch-pki
>  Architecture: all
>  Depends: openvswitch-common (<< ${source:Version}.1~),
> diff --git a/debian/ovn-central.dirs b/debian/ovn-central.dirs
> deleted file mode 100644
> index 6394883ce..000000000
> --- a/debian/ovn-central.dirs
> +++ /dev/null
> @@ -1 +0,0 @@
> -/usr/share/ovn/central
> diff --git a/debian/ovn-central.init b/debian/ovn-central.init
> deleted file mode 100755
> index 60cee95a3..000000000
> --- a/debian/ovn-central.init
> +++ /dev/null
> @@ -1,60 +0,0 @@
> -#! /bin/sh
> -#
> -### BEGIN INIT INFO
> -# Provides:          ovn-central
> -# Required-Start:    openvswitch-switch $remote_fs $syslog
> -# Required-Stop:     $remote_fs
> -# Default-Start:     2 3 4 5
> -# Default-Stop:      0 1 6
> -# Short-Description: OVN central components
> -# Description:       ovn-central provides the userspace daemons,
> -#                    utilities and databases for OVN that is run at a
> central
> -#                    location.
> -### END INIT INFO
> -
> -test -x /usr/bin/ovn-northd  || exit 0
> -test -x /usr/share/openvswitch/scripts/ovn-ctl || exit 0
> -
> -_SYSTEMCTL_SKIP_REDIRECT=yes
> -SYSTEMCTL_SKIP_REDIRECT=yes
> -
> -. /usr/share/openvswitch/scripts/ovs-lib
> -if [ -e /etc/default/ovn-central ]; then
> -    . /etc/default/ovn-central
> -fi
> -
> -start () {
> -    set /usr/share/openvswitch/scripts/ovn-ctl ${1-start_northd}
> -    set "$@" $OVN_CTL_OPTS
> -    "$@" || exit $?
> -}
> -
> -stop_northd () {
> -    set /usr/share/openvswitch/scripts/ovn-ctl ${1-stop_northd}
> -    set "$@" $OVN_CTL_OPTS
> -    "$@" || exit $?
> -}
> -
> -case $1 in
> -    start)
> -        start
> -        ;;
> -    stop)
> -        stop_northd
> -        ;;
> -    restart)
> -        start restart_northd
> -        ;;
> -    reload | force-reload)
> -        ;;
> -    status)
> -        /usr/share/openvswitch/scripts/ovn-ctl status_northd
> -        exit $?
> -        ;;
> -    *)
> -        echo "Usage: $0 {start|stop|reload|force-reload|restart|status}"
> >&2
> -        exit 1
> -        ;;
> -esac
> -
> -exit 0
> diff --git a/debian/ovn-central.install b/debian/ovn-central.install
> deleted file mode 100644
> index 733d3fc5e..000000000
> --- a/debian/ovn-central.install
> +++ /dev/null
> @@ -1,3 +0,0 @@
> -usr/bin/ovn-northd
> -usr/share/openvswitch/ovn-nb.ovsschema
> -usr/share/openvswitch/ovn-sb.ovsschema
> diff --git a/debian/ovn-central.manpages b/debian/ovn-central.manpages
> deleted file mode 100644
> index 2ddb43784..000000000
> --- a/debian/ovn-central.manpages
> +++ /dev/null
> @@ -1 +0,0 @@
> -ovn/northd/ovn-northd.8
> diff --git a/debian/ovn-central.postinst b/debian/ovn-central.postinst
> deleted file mode 100755
> index 10e07ece4..000000000
> --- a/debian/ovn-central.postinst
> +++ /dev/null
> @@ -1,49 +0,0 @@
> -#!/bin/sh
> -# postinst script for ovn-central
> -#
> -# see: dh_installdeb(1)
> -
> -set -e
> -
> -# summary of how this script can be called:
> -#        * <postinst> `configure' <most-recently-configured-version>
> -#        * <old-postinst> `abort-upgrade' <new version>
> -#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
> -#          <new-version>
> -#        * <postinst> `abort-remove'
> -#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
> -#          <failed-install-package> <version> `removing'
> -#          <conflicting-package> <version>
> -# for details, see http://www.debian.org/doc/debian-policy/ or
> -# the debian-policy package
> -
> -
> -case "$1" in
> -    configure)
> -        DEFAULT=/etc/default/ovn-central
> -        TEMPLATE=/usr/share/ovn/central/default.template
> -        if ! test -e $DEFAULT; then
> -            cp $TEMPLATE $DEFAULT
> -        else
> -            for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}'
> $TEMPLATE)
> -            do
> -                if ! grep $var $DEFAULT >/dev/null 2>&1; then
> -                    echo >> $DEFAULT
> -                    sed -n "/$var:/,/$var=/p" $TEMPLATE >> $DEFAULT
> -                fi
> -            done
> -        fi
> -        ;;
> -
> -    abort-upgrade|abort-remove|abort-deconfigure)
> -        ;;
> -
> -    *)
> -        echo "postinst called with unknown argument \`$1'" >&2
> -        exit 1
> -        ;;
> -esac
> -
> -#DEBHELPER#
> -
> -exit 0
> diff --git a/debian/ovn-central.postrm b/debian/ovn-central.postrm
> deleted file mode 100755
> index 0e654a37a..000000000
> --- a/debian/ovn-central.postrm
> +++ /dev/null
> @@ -1,48 +0,0 @@
> -#!/bin/sh
> -# postrm script for ovn-central
> -#
> -# see: dh_installdeb(1)
> -
> -set -e
> -
> -# summary of how this script can be called:
> -#        * <postrm> `remove'
> -#        * <postrm> `purge'
> -#        * <old-postrm> `upgrade' <new-version>
> -#        * <new-postrm> `failed-upgrade' <old-version>
> -#        * <new-postrm> `abort-install'
> -#        * <new-postrm> `abort-install' <old-version>
> -#        * <new-postrm> `abort-upgrade' <old-version>
> -#        * <disappearer's-postrm> `disappear' <overwriter>
> -#          <overwriter-version>
> -# for details, see http://www.debian.org/doc/debian-policy/ or
> -# the debian-policy package
> -
> -
> -case "$1" in
> -    purge)
> -        rm -f /etc/default/ovn-central
> -        rm -f /etc/openvswitch/ovnnb.db*
> -        rm -f /etc/openvswitch/.ovnnb.db.~lock~
> -        rm -f /etc/openvswitch/ovnsb.db*
> -        rm -f /etc/openvswitch/.ovnsb.db.~lock~
> -        rm -f /var/log/openvswitch/ovn-northd.log* || true
> -        ;;
> -
> -    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
> -        ;;
> -
> -    *)
> -        echo "postrm called with unknown argument \`$1'" >&2
> -        exit 1
> -        ;;
> -esac
> -
> -# dh_installdeb will replace this with shell code automatically
> -# generated by other debhelper scripts.
> -
> -#DEBHELPER#
> -
> -exit 0
> -
> -
> diff --git a/debian/ovn-central.template b/debian/ovn-central.template
> deleted file mode 100644
> index 7cea13e50..000000000
> --- a/debian/ovn-central.template
> +++ /dev/null
> @@ -1,5 +0,0 @@
> -# This is a POSIX shell fragment                -*- sh -*-
> -
> -# OVN_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
> -# a suitable place to specify --ovn-northd-wrapper=valgrind.
> -# OVN_CTL_OPTS=
> diff --git a/debian/ovn-common.install b/debian/ovn-common.install
> deleted file mode 100644
> index 90484d2fe..000000000
> --- a/debian/ovn-common.install
> +++ /dev/null
> @@ -1,7 +0,0 @@
> -usr/bin/ovn-nbctl
> -usr/bin/ovn-sbctl
> -usr/bin/ovn-trace
> -usr/bin/ovn-detrace
> -usr/share/openvswitch/scripts/ovn-ctl
> -usr/share/openvswitch/scripts/ovndb-servers.ocf
> -usr/lib/*/libovn*.so.*
> diff --git a/debian/ovn-common.manpages b/debian/ovn-common.manpages
> deleted file mode 100644
> index 249349ed8..000000000
> --- a/debian/ovn-common.manpages
> +++ /dev/null
> @@ -1,8 +0,0 @@
> -ovn/ovn-architecture.7
> -ovn/ovn-nb.5
> -ovn/ovn-sb.5
> -ovn/utilities/ovn-ctl.8
> -ovn/utilities/ovn-nbctl.8
> -ovn/utilities/ovn-sbctl.8
> -ovn/utilities/ovn-trace.8
> -ovn/utilities/ovn-detrace.1
> diff --git a/debian/ovn-common.postinst b/debian/ovn-common.postinst
> deleted file mode 100644
> index 588044fbc..000000000
> --- a/debian/ovn-common.postinst
> +++ /dev/null
> @@ -1,24 +0,0 @@
> -#!/bin/sh
> -# postinst script for ovn-common
> -#
> -# see: dh_installdeb(1)
> -
> -set -e
> -
> -case "$1" in
> -    configure)
> -        mkdir -p /usr/lib/ocf/resource.d/ovn
> -        ln -sf /usr/share/openvswitch/scripts/ovndb-servers.ocf
> /usr/lib/ocf/resource.d/ovn/ovndb-servers
> -        ;;
> -    abort-upgrade|abort-remove|abort-deconfigure)
> -        ;;
> -
> -    *)
> -        echo "postinst called with unknown argument \`$1'" >&2
> -        exit 1
> -        ;;
> -esac
> -
> -#DEBHELPER#
> -
> -exit 0
> diff --git a/debian/ovn-common.postrm b/debian/ovn-common.postrm
> deleted file mode 100644
> index 9face726b..000000000
> --- a/debian/ovn-common.postrm
> +++ /dev/null
> @@ -1,23 +0,0 @@
> -#!/bin/sh
> -# postrm script for openvswitch-testcontroller
> -#
> -# see: dh_installdeb(1)
> -
> -set -e
> -
> -case "$1" in
> -    purge|remove)
> -        rm -rf /usr/lib/ocf/resource.d/ovn
> -        ;;
> -    upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
> -        ;;
> -
> -    *)
> -        echo "postrm called with unknown argument \`$1'" >&2
> -        exit 1
> -        ;;
> -esac
> -
> -#DEBHELPER#
> -
> -exit 0
> diff --git a/debian/ovn-controller-vtep.init
> b/debian/ovn-controller-vtep.init
> deleted file mode 100755
> index be0a24358..000000000
> --- a/debian/ovn-controller-vtep.init
> +++ /dev/null
> @@ -1,54 +0,0 @@
> -#! /bin/sh
> -#
> -### BEGIN INIT INFO
> -# Provides:          ovn-controller-vtep
> -# Required-Start:    openvswitch-switch $remote_fs $syslog
> -# Required-Stop:     $remote_fs
> -# Default-Start:     2 3 4 5
> -# Default-Stop:      0 1 6
> -# Short-Description: OVN Controller for VTEP enabled devices
> -# Description:       ovn-controller-vtep provides the userspace
> -#                    components and utilities for OVN that can be run on
> -#                    hosts taht connect to VTEP enabled devices.
> -### END INIT INFO
> -
> -test -x /usr/bin/ovn-controller-vtep  || exit 0
> -test -x /usr/share/openvswitch/scripts/ovn-ctl || exit 0
> -
> -_SYSTEMCTL_SKIP_REDIRECT=yes
> -SYSTEMCTL_SKIP_REDIRECT=yes
> -
> -. /usr/share/openvswitch/scripts/ovs-lib
> -if [ -e /etc/default/ovn-controller-vtep ]; then
> -    . /etc/default/ovn-controller-vtep
> -fi
> -
> -start () {
> -    set /usr/share/openvswitch/scripts/ovn-ctl ${1-start_controller_vtep}
> -    set "$@" $OVN_CTL_OPTS
> -    "$@" || exit $?
> -}
> -
> -case $1 in
> -    start)
> -        start
> -        ;;
> -    stop | force-stop)
> -        /usr/share/openvswitch/scripts/ovn-ctl stop_controller_vtep
> -        ;;
> -    restart)
> -        start restart_controller_vtep
> -        ;;
> -    status)
> -        /usr/share/openvswitch/scripts/ovn-ctl status_controller_vtep
> -        exit $?
> -        ;;
> -    reload | force-reload)
> -        ;;
> -    *)
> -        echo "Usage: $0 {start|stop|reload|force-reload|restart|status}"
> >&2
> -        exit 1
> -        ;;
> -esac
> -
> -exit 0
> diff --git a/debian/ovn-controller-vtep.install
> b/debian/ovn-controller-vtep.install
> deleted file mode 100644
> index 1d208f37e..000000000
> --- a/debian/ovn-controller-vtep.install
> +++ /dev/null
> @@ -1 +0,0 @@
> -usr/bin/ovn-controller-vtep
> diff --git a/debian/ovn-controller-vtep.manpages
> b/debian/ovn-controller-vtep.manpages
> deleted file mode 100644
> index 787301704..000000000
> --- a/debian/ovn-controller-vtep.manpages
> +++ /dev/null
> @@ -1 +0,0 @@
> -ovn/controller-vtep/ovn-controller-vtep.8
> diff --git a/debian/ovn-docker.install b/debian/ovn-docker.install
> deleted file mode 100644
> index 583306732..000000000
> --- a/debian/ovn-docker.install
> +++ /dev/null
> @@ -1,2 +0,0 @@
> -usr/bin/ovn-docker-overlay-driver
> -usr/bin/ovn-docker-underlay-driver
> diff --git a/debian/ovn-host.dirs b/debian/ovn-host.dirs
> deleted file mode 100644
> index 7d3c761e1..000000000
> --- a/debian/ovn-host.dirs
> +++ /dev/null
> @@ -1 +0,0 @@
> -/usr/share/ovn/host
> diff --git a/debian/ovn-host.init b/debian/ovn-host.init
> deleted file mode 100755
> index 39c3bcf16..000000000
> --- a/debian/ovn-host.init
> +++ /dev/null
> @@ -1,54 +0,0 @@
> -#! /bin/sh
> -#
> -### BEGIN INIT INFO
> -# Provides:          ovn-host
> -# Required-Start:    openvswitch-switch $remote_fs $syslog
> -# Required-Stop:     $remote_fs
> -# Default-Start:     2 3 4 5
> -# Default-Stop:      0 1 6
> -# Short-Description: OVN host components
> -# Description:       ovn-host provides the userspace
> -#                    components and utilities for OVN that can be run on
> -#                    every host/hypervisor.
> -### END INIT INFO
> -
> -test -x /usr/bin/ovn-controller  || exit 0
> -test -x /usr/share/openvswitch/scripts/ovn-ctl || exit 0
> -
> -_SYSTEMCTL_SKIP_REDIRECT=yes
> -SYSTEMCTL_SKIP_REDIRECT=yes
> -
> -. /usr/share/openvswitch/scripts/ovs-lib
> -if [ -e /etc/default/ovn-host ]; then
> -    . /etc/default/ovn-host
> -fi
> -
> -start () {
> -    set /usr/share/openvswitch/scripts/ovn-ctl ${1-start_controller}
> -    set "$@" $OVN_CTL_OPTS
> -    "$@" || exit $?
> -}
> -
> -case $1 in
> -    start)
> -        start
> -        ;;
> -    stop | force-stop)
> -        /usr/share/openvswitch/scripts/ovn-ctl stop_controller
> -        ;;
> -    restart)
> -        start restart_controller
> -        ;;
> -    status)
> -        /usr/share/openvswitch/scripts/ovn-ctl status_controller
> -        exit $?
> -        ;;
> -    reload | force-reload)
> -        ;;
> -    *)
> -        echo "Usage: $0 {start|stop|reload|force-reload|restart|status}"
> >&2
> -        exit 1
> -        ;;
> -esac
> -
> -exit 0
> diff --git a/debian/ovn-host.install b/debian/ovn-host.install
> deleted file mode 100644
> index d2de82fd9..000000000
> --- a/debian/ovn-host.install
> +++ /dev/null
> @@ -1 +0,0 @@
> -usr/bin/ovn-controller
> diff --git a/debian/ovn-host.manpages b/debian/ovn-host.manpages
> deleted file mode 100644
> index 4f9e7bc90..000000000
> --- a/debian/ovn-host.manpages
> +++ /dev/null
> @@ -1 +0,0 @@
> -ovn/controller/ovn-controller.8
> diff --git a/debian/ovn-host.postinst b/debian/ovn-host.postinst
> deleted file mode 100755
> index 4b3edeb75..000000000
> --- a/debian/ovn-host.postinst
> +++ /dev/null
> @@ -1,49 +0,0 @@
> -#!/bin/sh
> -# postinst script for ovn-host
> -#
> -# see: dh_installdeb(1)
> -
> -set -e
> -
> -# summary of how this script can be called:
> -#        * <postinst> `configure' <most-recently-configured-version>
> -#        * <old-postinst> `abort-upgrade' <new version>
> -#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
> -#          <new-version>
> -#        * <postinst> `abort-remove'
> -#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
> -#          <failed-install-package> <version> `removing'
> -#          <conflicting-package> <version>
> -# for details, see http://www.debian.org/doc/debian-policy/ or
> -# the debian-policy package
> -
> -
> -case "$1" in
> -    configure)
> -        DEFAULT=/etc/default/ovn-host
> -        TEMPLATE=/usr/share/ovn/host/default.template
> -        if ! test -e $DEFAULT; then
> -            cp $TEMPLATE $DEFAULT
> -        else
> -            for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}'
> $TEMPLATE)
> -            do
> -                if ! grep $var $DEFAULT >/dev/null 2>&1; then
> -                    echo >> $DEFAULT
> -                    sed -n "/$var:/,/$var=/p" $TEMPLATE >> $DEFAULT
> -                fi
> -            done
> -        fi
> -        ;;
> -
> -    abort-upgrade|abort-remove|abort-deconfigure)
> -        ;;
> -
> -    *)
> -        echo "postinst called with unknown argument \`$1'" >&2
> -        exit 1
> -        ;;
> -esac
> -
> -#DEBHELPER#
> -
> -exit 0
> diff --git a/debian/ovn-host.postrm b/debian/ovn-host.postrm
> deleted file mode 100755
> index 4cceb9087..000000000
> --- a/debian/ovn-host.postrm
> +++ /dev/null
> @@ -1,44 +0,0 @@
> -#!/bin/sh
> -# postrm script for ovn-host
> -#
> -# see: dh_installdeb(1)
> -
> -set -e
> -
> -# summary of how this script can be called:
> -#        * <postrm> `remove'
> -#        * <postrm> `purge'
> -#        * <old-postrm> `upgrade' <new-version>
> -#        * <new-postrm> `failed-upgrade' <old-version>
> -#        * <new-postrm> `abort-install'
> -#        * <new-postrm> `abort-install' <old-version>
> -#        * <new-postrm> `abort-upgrade' <old-version>
> -#        * <disappearer's-postrm> `disappear' <overwriter>
> -#          <overwriter-version>
> -# for details, see http://www.debian.org/doc/debian-policy/ or
> -# the debian-policy package
> -
> -
> -case "$1" in
> -    purge)
> -        rm -f /etc/default/ovn-host
> -        rm -f /var/log/openvswitch/ovn-controller.log* || true
> -        ;;
> -
> -    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
> -        ;;
> -
> -    *)
> -        echo "postrm called with unknown argument \`$1'" >&2
> -        exit 1
> -        ;;
> -esac
> -
> -# dh_installdeb will replace this with shell code automatically
> -# generated by other debhelper scripts.
> -
> -#DEBHELPER#
> -
> -exit 0
> -
> -
> diff --git a/debian/ovn-host.template b/debian/ovn-host.template
> deleted file mode 100644
> index 7fd54efda..000000000
> --- a/debian/ovn-host.template
> +++ /dev/null
> @@ -1,5 +0,0 @@
> -# This is a POSIX shell fragment                -*- sh -*-
> -
> -# OVN_CTL_OPTS: Extra options to pass to ovs-ctl.  This is, for example,
> -# a suitable place to specify --ovn-controller-wrapper=valgrind.
> -# OVN_CTL_OPTS=
> diff --git a/debian/rules b/debian/rules
> index 9d0a81f1a..77f3a1984 100755
> --- a/debian/rules
> +++ b/debian/rules
> @@ -53,12 +53,6 @@ override_dh_install-arch:
>         # openvswitch-switch
>         cp debian/openvswitch-switch.template
> debian/openvswitch-switch/usr/share/openvswitch/switch/default.template
>
> -       # ovn-host
> -       cp debian/ovn-host.template
> debian/ovn-host/usr/share/ovn/host/default.template
> -
> -       # ovn-central
> -       cp debian/ovn-central.template
> debian/ovn-central/usr/share/ovn/central/default.template
> -
>  override_dh_install-indep:
>         dh_install
>
> diff --git a/include/automake.mk b/include/automake.mk
> index 01031e88d..e982da87d 100644
> --- a/include/automake.mk
> +++ b/include/automake.mk
> @@ -11,7 +11,6 @@ include/odp-netlink-macros.h: include/odp-netlink.h \
>  EXTRA_DIST += build-aux/extract-odp-netlink-h
> build-aux/extract-odp-netlink-macros-h
>  CLEANFILES += include/odp-netlink.h include/odp-netlink-macros.h
>
> -include include/ovn/automake.mk
>  include include/openflow/automake.mk
>  include include/openvswitch/automake.mk
>  include include/sparse/automake.mk
> diff --git a/include/ovn/actions.h b/include/ovn/actions.h
> deleted file mode 100644
> index 63d3907d8..000000000
> --- a/include/ovn/actions.h
> +++ /dev/null
> @@ -1,622 +0,0 @@
> -/*
> - * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_ACTIONS_H
> -#define OVN_ACTIONS_H 1
> -
> -#include <stdbool.h>
> -#include <stdint.h>
> -#include "compiler.h"
> -#include "expr.h"
> -#include "openvswitch/dynamic-string.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/uuid.h"
> -#include "util.h"
> -
> -struct expr;
> -struct lexer;
> -struct ofpbuf;
> -struct shash;
> -struct simap;
> -struct ovn_extend_table;
> -
> -/* List of OVN logical actions.
> - *
> - * This macro is used directly only internally by this header and its
> - * corresponding .c file, but the list is still of interest to developers.
> - *
> - * Each OVNACT invocation has the following parameters:
> - *
> - * 1. <ENUM>, used below in the enum definition of OVNACT_<ENUM>, and
> - *    elsewhere.
> - *
> - * 2. <STRUCT> corresponding to a structure "struct <STRUCT>", that must
> be a
> - *    defined below.  This structure must be an abstract definition of the
> - *    action.  Its first member must have type "struct ovnact" and name
> - *    "ovnact".  The structure must have a fixed length, that is, it may
> not
> - *    end with a flexible array member.
> - */
> -#define OVNACTS                                       \
> -    OVNACT(OUTPUT,            ovnact_null)            \
> -    OVNACT(NEXT,              ovnact_next)            \
> -    OVNACT(LOAD,              ovnact_load)            \
> -    OVNACT(MOVE,              ovnact_move)            \
> -    OVNACT(EXCHANGE,          ovnact_move)            \
> -    OVNACT(DEC_TTL,           ovnact_null)            \
> -    OVNACT(CT_NEXT,           ovnact_ct_next)         \
> -    OVNACT(CT_COMMIT,         ovnact_ct_commit)       \
> -    OVNACT(CT_DNAT,           ovnact_ct_nat)          \
> -    OVNACT(CT_SNAT,           ovnact_ct_nat)          \
> -    OVNACT(CT_LB,             ovnact_ct_lb)           \
> -    OVNACT(CT_CLEAR,          ovnact_null)            \
> -    OVNACT(CLONE,             ovnact_nest)            \
> -    OVNACT(ARP,               ovnact_nest)            \
> -    OVNACT(ICMP4,             ovnact_nest)            \
> -    OVNACT(ICMP4_ERROR,       ovnact_nest)            \
> -    OVNACT(ICMP6,             ovnact_nest)            \
> -    OVNACT(IGMP,              ovnact_null)            \
> -    OVNACT(TCP_RESET,         ovnact_nest)            \
> -    OVNACT(ND_NA,             ovnact_nest)            \
> -    OVNACT(ND_NA_ROUTER,      ovnact_nest)            \
> -    OVNACT(GET_ARP,           ovnact_get_mac_bind)    \
> -    OVNACT(PUT_ARP,           ovnact_put_mac_bind)    \
> -    OVNACT(GET_ND,            ovnact_get_mac_bind)    \
> -    OVNACT(PUT_ND,            ovnact_put_mac_bind)    \
> -    OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
> -    OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
> -    OVNACT(SET_QUEUE,         ovnact_set_queue)       \
> -    OVNACT(DNS_LOOKUP,        ovnact_dns_lookup)      \
> -    OVNACT(LOG,               ovnact_log)             \
> -    OVNACT(PUT_ND_RA_OPTS,    ovnact_put_opts)        \
> -    OVNACT(ND_NS,             ovnact_nest)            \
> -    OVNACT(SET_METER,         ovnact_set_meter)       \
> -    OVNACT(OVNFIELD_LOAD,     ovnact_load)            \
> -    OVNACT(CHECK_PKT_LARGER,  ovnact_check_pkt_larger) \
> -    OVNACT(TRIGGER_EVENT,     ovnact_controller_event)
> -
> -/* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
> -enum OVS_PACKED_ENUM ovnact_type {
> -#define OVNACT(ENUM, STRUCT) OVNACT_##ENUM,
> -    OVNACTS
> -#undef OVNACT
> -};
> -
> -/* Define N_OVNACTS to the number of types of ovnacts. */
> -enum {
> -#define OVNACT(ENUM, STRUCT) + 1
> -    N_OVNACTS = OVNACTS
> -#undef OVNACT
> -};
> -
> -/* Header for an action.
> - *
> - * Each action is a structure "struct ovnact_*" that begins with "struct
> - * ovnact", usually followed by other data that describes the action.
> Actions
> - * are padded out to a multiple of OVNACT_ALIGNTO bytes in length.
> - */
> -struct ovnact {
> -    /* We want the space advantage of an 8-bit type here on every
> -     * implementation, without giving up the advantage of having a useful
> type
> -     * on implementations that support packed enums. */
> -#ifdef HAVE_PACKED_ENUM
> -    enum ovnact_type type;      /* OVNACT_*. */
> -#else
> -    uint8_t type;               /* OVNACT_* */
> -#endif
> -    uint8_t pad;                /* Pad to multiple of 16 bits. */
> -
> -    uint16_t len;               /* Length of the action, in bytes,
> including
> -                                 * struct ovnact, excluding padding. */
> -};
> -BUILD_ASSERT_DECL(sizeof(struct ovnact) == 4);
> -
> -/* Alignment. */
> -#define OVNACT_ALIGNTO 8
> -#define OVNACT_ALIGN(SIZE) ROUND_UP(SIZE, OVNACT_ALIGNTO)
> -
> -/* Returns the ovnact following 'ovnact'. */
> -static inline struct ovnact *
> -ovnact_next(const struct ovnact *ovnact)
> -{
> -    return (void *) ((uint8_t *) ovnact + OVNACT_ALIGN(ovnact->len));
> -}
> -
> -struct ovnact *ovnact_next_flattened(const struct ovnact *);
> -
> -static inline struct ovnact *
> -ovnact_end(const struct ovnact *ovnacts, size_t ovnacts_len)
> -{
> -    return (void *) ((uint8_t *) ovnacts + ovnacts_len);
> -}
> -
> -/* Assigns POS to each ovnact, in turn, in the OVNACTS_LEN bytes of
> ovnacts
> - * starting at OVNACTS. */
> -#define OVNACT_FOR_EACH(POS, OVNACTS, OVNACTS_LEN)                      \
> -    for ((POS) = (OVNACTS); (POS) < ovnact_end(OVNACTS, OVNACTS_LEN);  \
> -         (POS) = ovnact_next(POS))
> -
> -static inline int
> -ovnacts_count(const struct ovnact *ovnacts, size_t ovnacts_len)
> -{
> -    uint8_t n_ovnacts = 0;
> -    if (ovnacts) {
> -        const struct ovnact *a;
> -
> -        OVNACT_FOR_EACH (a, ovnacts, ovnacts_len) {
> -            n_ovnacts++;
> -        }
> -    }
> -    return n_ovnacts;
> -}
> -
> -/* Action structure for each OVNACT_*. */
> -
> -/* Action structure for actions that do not have any extra data beyond the
> - * action type. */
> -struct ovnact_null {
> -    struct ovnact ovnact;
> -};
> -
> -/* Logical pipeline in which a set of actions is executed. */
> -enum ovnact_pipeline {
> -    OVNACT_P_INGRESS,
> -    OVNACT_P_EGRESS,
> -};
> -
> -/* OVNACT_NEXT. */
> -struct ovnact_next {
> -    struct ovnact ovnact;
> -
> -    /* Arguments. */
> -    uint8_t ltable;                /* Logical table ID of next table. */
> -    enum ovnact_pipeline pipeline; /* Pipeline of next table. */
> -
> -    /* Information about the flow that the action is in.  This does not
> affect
> -     * behavior, since the implementation of "next" doesn't depend on the
> -     * source table or pipeline.  It does affect how ovnacts_format()
> prints
> -     * the action. */
> -    uint8_t src_ltable;                /* Logical table ID of source
> table. */
> -    enum ovnact_pipeline src_pipeline; /* Pipeline of source table. */
> -};
> -
> -/* OVNACT_LOAD. */
> -struct ovnact_load {
> -    struct ovnact ovnact;
> -    struct expr_field dst;
> -    union expr_constant imm;
> -};
> -
> -/* OVNACT_MOVE, OVNACT_EXCHANGE. */
> -struct ovnact_move {
> -    struct ovnact ovnact;
> -    struct expr_field lhs;
> -    struct expr_field rhs;
> -};
> -
> -/* OVNACT_CT_NEXT. */
> -struct ovnact_ct_next {
> -    struct ovnact ovnact;
> -    uint8_t ltable;                /* Logical table ID of next table. */
> -};
> -
> -/* OVNACT_CT_COMMIT. */
> -struct ovnact_ct_commit {
> -    struct ovnact ovnact;
> -    uint32_t ct_mark, ct_mark_mask;
> -    ovs_be128 ct_label, ct_label_mask;
> -};
> -
> -/* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */
> -struct ovnact_ct_nat {
> -    struct ovnact ovnact;
> -    ovs_be32 ip;
> -    uint8_t ltable;             /* Logical table ID of next table. */
> -};
> -
> -struct ovnact_ct_lb_dst {
> -    int family;
> -    union {
> -        struct in6_addr ipv6;
> -        ovs_be32 ipv4;
> -    };
> -    uint16_t port;
> -};
> -
> -/* OVNACT_CT_LB. */
> -struct ovnact_ct_lb {
> -    struct ovnact ovnact;
> -    struct ovnact_ct_lb_dst *dsts;
> -    size_t n_dsts;
> -    uint8_t ltable;             /* Logical table ID of next table. */
> -};
> -
> -/* OVNACT_ARP, OVNACT_ND_NA, OVNACT_CLONE. */
> -struct ovnact_nest {
> -    struct ovnact ovnact;
> -    struct ovnact *nested;
> -    size_t nested_len;
> -};
> -
> -/* OVNACT_GET_ARP, OVNACT_GET_ND. */
> -struct ovnact_get_mac_bind {
> -    struct ovnact ovnact;
> -    struct expr_field port;     /* Logical port name. */
> -    struct expr_field ip;       /* 32-bit or 128-bit IP address. */
> -};
> -
> -/* OVNACT_PUT_ARP, ONVACT_PUT_ND. */
> -struct ovnact_put_mac_bind {
> -    struct ovnact ovnact;
> -    struct expr_field port;     /* Logical port name. */
> -    struct expr_field ip;       /* 32-bit or 128-bit IP address. */
> -    struct expr_field mac;      /* 48-bit Ethernet address. */
> -};
> -
> -struct ovnact_gen_option {
> -    const struct gen_opts_map *option;
> -    struct expr_constant_set value;
> -};
> -
> -/* OVNACT_PUT_DHCPV4_OPTS, OVNACT_PUT_DHCPV6_OPTS. */
> -struct ovnact_put_opts {
> -    struct ovnact ovnact;
> -    struct expr_field dst;      /* 1-bit destination field. */
> -    struct ovnact_gen_option *options;
> -    size_t n_options;
> -};
> -
> -/* Valid arguments to SET_QUEUE action.
> - *
> - * QDISC_MIN_QUEUE_ID is the default queue, so user-defined queues should
> - * start at QDISC_MIN_QUEUE_ID+1. */
> -#define QDISC_MIN_QUEUE_ID  0
> -#define QDISC_MAX_QUEUE_ID  0xf000
> -
> -/* OVNACT_SET_QUEUE. */
> -struct ovnact_set_queue {
> -    struct ovnact ovnact;
> -    uint16_t queue_id;
> -};
> -
> -/* OVNACT_DNS_LOOKUP. */
> -struct ovnact_dns_lookup {
> -    struct ovnact ovnact;
> -    struct expr_field dst;      /* 1-bit destination field. */
> -};
> -
> -/* OVNACT_LOG. */
> -struct ovnact_log {
> -    struct ovnact ovnact;
> -    uint8_t verdict;            /* One of LOG_VERDICT_*. */
> -    uint8_t severity;           /* One of LOG_SEVERITY_*. */
> -    char *name;
> -    char *meter;
> -};
> -
> -/* OVNACT_SET_METER. */
> -struct ovnact_set_meter {
> -    struct ovnact ovnact;
> -    uint64_t rate;                   /* rate field, in kbps. */
> -    uint64_t burst;                  /* burst rate field, in kbps. */
> -};
> -
> -/* OVNACT_CHECK_IP_PKT_LARGER. */
> -struct ovnact_check_pkt_larger {
> -    struct ovnact ovnact;
> -    uint16_t pkt_len;
> -    struct expr_field dst;      /* 1-bit destination field. */
> -};
> -
> -/* OVNACT_EVENT. */
> -struct ovnact_controller_event {
> -    struct ovnact ovnact;
> -    int event_type;   /* controller event type */
> -    struct ovnact_gen_option *options;
> -    size_t n_options;
> -};
> -
> -/* Internal use by the helpers below. */
> -void ovnact_init(struct ovnact *, enum ovnact_type, size_t len);
> -void *ovnact_put(struct ofpbuf *, enum ovnact_type, size_t len);
> -
> -/* For each OVNACT_<ENUM> with a corresponding struct <STRUCT>, this
> defines
> - * the following commonly useful functions:
> - *
> - *   struct <STRUCT> *ovnact_put_<ENUM>(struct ofpbuf *ovnacts);
> - *
> - *     Appends a new 'ovnact', of length OVNACT_<ENUM>_SIZE, to 'ovnacts',
> - *     initializes it with ovnact_init_<ENUM>(), and returns it.  Also
> sets
> - *     'ovnacts->header' to the returned action.
> - *
> - *   struct <STRUCT> *ovnact_get_<ENUM>(const struct ovnact *ovnact);
> - *
> - *     Returns 'ovnact' cast to "struct <STRUCT> *".  'ovnact->type' must
> be
> - *     OVNACT_<ENUM>.
> - *
> - * as well as the following more rarely useful definitions:
> - *
> - *   void ovnact_init_<ENUM>(struct <STRUCT> *ovnact);
> - *
> - *     Initializes the parts of 'ovnact' that identify it as having type
> - *     OVNACT_<ENUM> and length OVNACT_<ENUM>_SIZE and zeros the rest.
> - *
> - *   <ENUM>_SIZE
> - *
> - *     The size of the action structure, that is, sizeof(struct <STRUCT>)
> - *     rounded up to a multiple of OVNACT_ALIGNTO.
> - */
> -#define OVNACT(ENUM, STRUCT)                                            \
> -    BUILD_ASSERT_DECL(offsetof(struct STRUCT, ovnact) == 0);            \
> -                                                                        \
> -    enum { OVNACT_##ENUM##_SIZE = OVNACT_ALIGN(sizeof(struct STRUCT)) }; \
> -                                                                        \
> -    static inline struct STRUCT *                                       \
> -    ovnact_get_##ENUM(const struct ovnact *ovnact)                      \
> -    {                                                                   \
> -        ovs_assert(ovnact->type == OVNACT_##ENUM);                      \
> -        return ALIGNED_CAST(struct STRUCT *, ovnact);                   \
> -    }                                                                   \
> -                                                                        \
> -    static inline struct STRUCT *                                       \
> -    ovnact_put_##ENUM(struct ofpbuf *ovnacts)                           \
> -    {                                                                   \
> -        return ovnact_put(ovnacts, OVNACT_##ENUM,                       \
> -                          OVNACT_##ENUM##_SIZE);                        \
> -    }                                                                   \
> -                                                                        \
> -    static inline void                                                  \
> -    ovnact_init_##ENUM(struct STRUCT *ovnact)                           \
> -    {                                                                   \
> -        ovnact_init(&ovnact->ovnact, OVNACT_##ENUM,                     \
> -                    OVNACT_##ENUM##_SIZE);                              \
> -    }
> -OVNACTS
> -#undef OVNACT
> -
> -enum action_opcode {
> -    /* "arp { ...actions... }".
> -     *
> -     * The actions, in OpenFlow 1.3 format, follow the action_header.
> -     */
> -    ACTION_OPCODE_ARP,
> -
> -    /* "put_arp(port, ip, mac)"
> -     *
> -     * Arguments are passed through the packet metadata and data, as
> follows:
> -     *
> -     *     MFF_REG0 = ip
> -     *     MFF_LOG_INPORT = port
> -     *     MFF_ETH_SRC = mac
> -     */
> -    ACTION_OPCODE_PUT_ARP,
> -
> -    /* "result = put_dhcp_opts(offer_ip, option, ...)".
> -     *
> -     * Arguments follow the action_header, in this format:
> -     *   - A 32-bit or 64-bit OXM header designating the result field.
> -     *   - A 32-bit integer specifying a bit offset within the result
> field.
> -     *   - The 32-bit DHCP offer IP.
> -     *   - Any number of DHCP options.
> -     */
> -    ACTION_OPCODE_PUT_DHCP_OPTS,
> -
> -    /* "nd_na { ...actions... }".
> -     *
> -     * The actions, in OpenFlow 1.3 format, follow the action_header.
> -     */
> -    ACTION_OPCODE_ND_NA,
> -
> -    /* "put_nd(port, ip6, mac)"
> -     *
> -     * Arguments are passed through the packet metadata and data, as
> follows:
> -     *
> -     *     MFF_XXREG0 = ip6
> -     *     MFF_LOG_INPORT = port
> -     *     MFF_ETH_SRC = mac
> -     */
> -    ACTION_OPCODE_PUT_ND,
> -
> -    /* "result = put_dhcpv6_opts(option, ...)".
> -     *
> -     * Arguments follow the action_header, in this format:
> -     *   - A 32-bit or 64-bit OXM header designating the result field.
> -     *   - A 32-bit integer specifying a bit offset within the result
> field.
> -     *   - Any number of DHCPv6 options.
> -     */
> -    ACTION_OPCODE_PUT_DHCPV6_OPTS,
> -
> -    /* "result = dns_lookup()".
> -     * Arguments follow the action_header, in this format:
> -     *   - A 32-bit or 64-bit OXM header designating the result field.
> -     *   - A 32-bit integer specifying a bit offset within the result
> field.
> -     *
> -     */
> -    ACTION_OPCODE_DNS_LOOKUP,
> -
> -    /* "log(arguments)".
> -     *
> -     * Arguments are as follows:
> -     *   - An 8-bit verdict.
> -     *   - An 8-bit severity.
> -     *   - A variable length string containing the name.
> -     */
> -    ACTION_OPCODE_LOG,
> -
> -    /* "result = put_nd_ra_opts(option, ...)".
> -     * Arguments follow the action_header, in this format:
> -     *   - A 32-bit or 64-bit OXM header designating the result field.
> -     *   - A 32-bit integer specifying a bit offset within the result
> field.
> -     *   - Any number of ICMPv6 options.
> -     */
> -    ACTION_OPCODE_PUT_ND_RA_OPTS,
> -
> -    /* "nd_ns { ...actions... }".
> -     *
> -     * The actions, in OpenFlow 1.3 format, follow the action_header.
> -     */
> -    ACTION_OPCODE_ND_NS,
> -
> -    /* "icmp4 { ...actions... } and icmp6 { ...actions... }".
> -     *
> -     * The actions, in OpenFlow 1.3 format, follow the action_header.
> -     */
> -    ACTION_OPCODE_ICMP,
> -
> -    /* "tcp_reset { ...actions... }".
> -     *
> -     * The actions, in OpenFlow 1.3 format, follow the action_header.
> -     */
> -    ACTION_OPCODE_TCP_RESET,
> -
> -    /* "nd_na_router { ...actions... }" with rso flag 'ND_RSO_ROUTER' set.
> -        *
> -        * The actions, in OpenFlow 1.3 format, follow the action_header.
> -        */
> -    ACTION_OPCODE_ND_NA_ROUTER,
> -
> -     /* MTU value (to put in the icmp4 header field - frag_mtu) follow the
> -     * action header. */
> -    ACTION_OPCODE_PUT_ICMP4_FRAG_MTU,
> -
> -    /* "icmp4_error { ...actions... }".
> -     *
> -     * The actions, in OpenFlow 1.3 format, follow the action_header.
> -     */
> -    ACTION_OPCODE_ICMP4_ERROR,
> -
> -    /* "trigger_event (event_type)" */
> -    ACTION_OPCODE_EVENT,
> -
> -    /* "igmp".
> -     *
> -     * Snoop IGMP, learn the multicast participants
> -     */
> -    ACTION_OPCODE_IGMP,
> -};
> -
> -/* Header. */
> -struct action_header {
> -    ovs_be32 opcode;            /* One of ACTION_OPCODE_* */
> -    uint8_t pad[4];
> -};
> -BUILD_ASSERT_DECL(sizeof(struct action_header) == 8);
> -
> -OVS_PACKED(
> -struct ovnfield_act_header {
> -    ovs_be16 id; /* one of enum ovnfield_id. */
> -    ovs_be16 len; /* Length of the ovnfield data. */
> -});
> -
> -struct ovnact_parse_params {
> -    /* A table of "struct expr_symbol"s to support (as one would provide
> to
> -     * expr_parse()). */
> -    const struct shash *symtab;
> -
> -    /* hmap of 'struct gen_opts_map' to support 'put_dhcp_opts' action */
> -    const struct hmap *dhcp_opts;
> -
> -    /* hmap of 'struct gen_opts_map'  to support 'put_dhcpv6_opts' action
> */
> -    const struct hmap *dhcpv6_opts;
> -
> -    /* hmap of 'struct gen_opts_map' to support 'put_nd_ra_opts' action */
> -    const struct hmap *nd_ra_opts;
> -
> -    /* Array of hmap of 'struct gen_opts_map' to support 'trigger_event'
> -     * action */
> -    const struct controller_event_options *controller_event_opts;
> -
> -    /* Each OVN flow exists in a logical table within a logical pipeline.
> -     * These parameters express this context for a set of OVN actions
> being
> -     * parsed:
> -     *
> -     *     - 'n_tables' is the number of tables in the logical ingress and
> -     *        egress pipelines, that is, "next" may specify a table less
> than
> -     *        or equal to 'n_tables'.  If 'n_tables' is 0 then "next" is
> -     *        disallowed entirely.
> -     *
> -     *     - 'cur_ltable' is the logical table of the current flow, within
> -     *       'pipeline'.  If cur_ltable + 1 < n_tables, then this defines
> the
> -     *       default table that "next" will jump to.
> -     *
> -     *     - 'pipeline' is the logical pipeline.  It is the default
> pipeline to
> -     *       which 'next' will jump.  If 'pipeline' is OVNACT_P_EGRESS,
> then
> -     *       'next' will also be able to jump into the ingress pipeline,
> but
> -     *       the reverse is not true. */
> -    enum ovnact_pipeline pipeline; /* Logical pipeline. */
> -    uint8_t n_tables;              /* Number of logical flow tables. */
> -    uint8_t cur_ltable;            /* 0 <= cur_ltable < n_tables. */
> -};
> -
> -bool ovnacts_parse(struct lexer *, const struct ovnact_parse_params *,
> -                    struct ofpbuf *ovnacts, struct expr **prereqsp);
> -char *ovnacts_parse_string(const char *s, const struct
> ovnact_parse_params *,
> -                           struct ofpbuf *ovnacts, struct expr **prereqsp)
> -    OVS_WARN_UNUSED_RESULT;
> -
> -void ovnacts_format(const struct ovnact[], size_t ovnacts_len, struct ds
> *);
> -
> -struct ovnact_encode_params {
> -    /* Looks up logical port 'port_name'.  If found, stores its port
> number in
> -     * '*portp' and returns true; otherwise, returns false. */
> -    bool (*lookup_port)(const void *aux, const char *port_name,
> -                        unsigned int *portp);
> -    const void *aux;
> -
> -    /* 'true' if the flow is for a switch. */
> -    bool is_switch;
> -
> -    /* A struct to figure out the group_id for group actions. */
> -    struct ovn_extend_table *group_table;
> -
> -    /* A struct to figure out the meter_id for meter actions. */
> -    struct ovn_extend_table *meter_table;
> -
> -    /* The logical flow uuid that drove this action. */
> -    struct uuid lflow_uuid;
> -
> -    /* OVN maps each logical flow table (ltable), one-to-one, onto a
> physical
> -     * OpenFlow flow table (ptable).  A number of parameters describe this
> -     * mapping and data related to flow tables:
> -     *
> -     *     - 'pipeline' is the logical pipeline in which the actions are
> -     *       executing.
> -     *
> -     *     - 'ingress_ptable' is the OpenFlow table that corresponds to
> OVN
> -     *       ingress table 0.
> -     *
> -     *     - 'egress_ptable' is the OpenFlow table that corresponds to OVN
> -     *       egress table 0.
> -     *
> -     *     - 'output_ptable' should be the OpenFlow table to which the
> logical
> -     *       "output" action will resubmit.
> -     *
> -     *     - 'mac_bind_ptable' should be the OpenFlow table used to track
> MAC
> -     *       bindings. */
> -    enum ovnact_pipeline pipeline; /* Logical pipeline. */
> -    uint8_t ingress_ptable;     /* First OpenFlow ingress table. */
> -    uint8_t egress_ptable;      /* First OpenFlow egress table. */
> -    uint8_t output_ptable;      /* OpenFlow table for 'output' to
> resubmit. */
> -    uint8_t mac_bind_ptable;    /* OpenFlow table for 'get_arp'/'get_nd'
> to
> -                                   resubmit. */
> -};
> -
> -void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
> -                    const struct ovnact_encode_params *,
> -                    struct ofpbuf *ofpacts);
> -
> -void ovnacts_free(struct ovnact[], size_t ovnacts_len);
> -
> -#endif /* ovn/actions.h */
> diff --git a/include/ovn/automake.mk b/include/ovn/automake.mk
> deleted file mode 100644
> index 54b0e2c0e..000000000
> --- a/include/ovn/automake.mk
> +++ /dev/null
> @@ -1,6 +0,0 @@
> -ovnincludedir = $(includedir)/ovn
> -ovninclude_HEADERS = \
> -       include/ovn/actions.h \
> -       include/ovn/expr.h \
> -       include/ovn/lex.h  \
> -       include/ovn/logical-fields.h
> diff --git a/include/ovn/expr.h b/include/ovn/expr.h
> deleted file mode 100644
> index 22f633e57..000000000
> --- a/include/ovn/expr.h
> +++ /dev/null
> @@ -1,518 +0,0 @@
> -/*
> - * Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_EXPR_H
> -#define OVN_EXPR_H 1
> -
> -/* OVN matching expression tree
> - * ============================
> - *
> - * The data structures here form an abstract expression tree for matching
> - * expressions in OVN.
> - *
> - * The abstract syntax tree representation of a matching expression is
> one of:
> - *
> - *    - A Boolean literal ("true" or "false").
> - *
> - *    - A comparison of a field (or part of a field) against a constant
> - *      with one of the operators == != < <= > >=.
> - *
> - *    - The logical AND or OR of two or more matching expressions.
> - *
> - * Literals and comparisons are called "terminal" nodes, logical AND and
> OR
> - * nodes are "nonterminal" nodes.
> - *
> - * The syntax for expressions includes a few other concepts that are not
> part
> - * of the abstract syntax tree.  In these examples, x is a field, a, b,
> and c
> - * are constants, and e1 and e2 are arbitrary expressions:
> - *
> - *    - Logical NOT.  The parser implements NOT by inverting the sense of
> the
> - *      operand: !(x == a) becomes x != a, !(e1 && e2) becomes !e1 ||
> !e2, and
> - *      so on.
> - *
> - *    - Set membership.  The parser translates x == {a, b, c} into
> - *      x == a || x == b || x == c.
> - *
> - *    - Reversed comparisons.  The parser translates a < x into x > a.
> - *
> - *    - Range expressions.  The parser translates a < x < b into
> - *      x > a && x < b.
> - */
> -
> -#include "classifier.h"
> -#include "lex.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/list.h"
> -#include "openvswitch/match.h"
> -#include "openvswitch/meta-flow.h"
> -#include "logical-fields.h"
> -
> -struct ds;
> -struct expr;
> -struct flow;
> -struct ofpbuf;
> -struct shash;
> -struct simap;
> -struct sset;
> -
> -/* "Measurement level" of a field.  See "Level of Measurement" in the
> large
> - * comment on struct expr_symbol below for more information. */
> -enum expr_level {
> -    EXPR_L_NOMINAL,
> -
> -    /* Boolean values are nominal, however because of their simple nature
> OVN
> -     * can allow both equality and inequality tests on them. */
> -    EXPR_L_BOOLEAN,
> -
> -    /* Ordinal values can at least be ordered on a scale.  OVN allows
> equality
> -     * and inequality and relational tests on ordinal values.  These are
> the
> -     * fields on which OVS allows bitwise matching. */
> -    EXPR_L_ORDINAL
> -};
> -
> -const char *expr_level_to_string(enum expr_level);
> -
> -/* A symbol.
> - *
> - *
> - * Name
> - * ====
> - *
> - * Every symbol must have a name.  To be useful, the name must satisfy the
> - * lexer's syntax for an identifier.
> - *
> - *
> - * Width
> - * =====
> - *
> - * Every symbol has a width.  For integer symbols, this is the number of
> bits
> - * in the value; for string symbols, this is 0.
> - *
> - *
> - * Types
> - * =====
> - *
> - * There are three kinds of symbols:
> - *
> - *   Fields:
> - *
> - *     One might, for example, define a field named "vlan.tci" to refer to
> - *     MFF_VLAN_TCI.  'field' specifies the field.
> - *
> - *     'parent' and 'predicate' are NULL, and 'parent_ofs' is 0.
> - *
> - *     Integer fields can be nominal or ordinal (see below).  String
> fields are
> - *     always nominal.
> - *
> - *   Subfields:
> - *
> - *     'parent' specifies the field (which may itself be a subfield,
> - *     recursively) in which the subfield is embedded, and 'parent_ofs' a
> - *     bitwise offset from the least-significant bit of the parent.  The
> - *     subfield can contain a subset of the bits of the parent or all of
> them
> - *     (in the latter case the subfield is really just a synonym for the
> - *     parent).
> - *
> - *     'field' and 'predicate' are NULL.
> - *
> - *     Only ordinal fields (see below) may have subfields, and subfields
> are
> - *     always ordinal.
> - *
> - *   Predicates:
> - *
> - *     A predicate is an arbitrary Boolean expression that can be used in
> an
> - *     expression much like a 1-bit field.  'predicate' specifies the
> Boolean
> - *     expression, e.g. "ip4" might expand to "eth.type == 0x800".  The
> - *     epxression might refer to other predicates, e.g. "icmp4" might
> expand to
> - *     "ip4 && ip4.proto == 1".
> - *
> - *     'field' and 'parent' are NULL, and 'parent_ofs' is 0.
> - *
> - *     A predicate that refers to any nominal field or predicate (see
> below) is
> - *     nominal; other predicates have Boolean level of measurement.
> - *
> - *
> - * Level of Measurement
> - * ====================
> - *
> - * See http://en.wikipedia.org/wiki/Level_of_measurement for the
> statistical
> - * concept on which this classification is based.  There are three levels:
> - *
> - *   Ordinal:
> - *
> - *     In statistics, ordinal values can be ordered on a scale.  Here, we
> - *     consider a field (or subfield) to be ordinal if its bits can be
> examined
> - *     individually.  This is true for the OpenFlow fields that OpenFlow
> or
> - *     Open vSwitch makes "maskable".
> - *
> - *     OVN supports all the usual arithmetic relations (== != < <= > >=)
> on
> - *     ordinal fields and their subfields, because all of these can be
> - *     implemented as collections of bitwise tests.
> - *
> - *   Nominal:
> - *
> - *     In statistics, nominal values cannot be usefully compared except
> for
> - *     equality.  This is true of OpenFlow port numbers, Ethernet types,
> and IP
> - *     protocols are examples: all of these are just identifiers assigned
> - *     arbitrarily with no deeper meaning.  In OpenFlow and Open vSwitch,
> bits
> - *     in these fields generally aren't individually addressable.
> - *
> - *     OVN only supports arithmetic tests for equality on nominal fields,
> - *     because OpenFlow and Open vSwitch provide no way for a flow to
> - *     efficiently implement other comparisons on them.  (A test for
> inequality
> - *     can be sort of built out of two flows with different priorities,
> but OVN
> - *     matching expressions always generate flows with a single priority.)
> - *
> - *     String fields are always nominal.
> - *
> - *   Boolean:
> - *
> - *     A nominal field that has only two values, 0 and 1, is somewhat
> - *     exceptional, since it is easy to support both equality and
> inequality
> - *     tests on such a field: either one can be implemented as a test for
> 0 or
> - *     1.
> - *
> - *     Only predicates (see above) have a Boolean level of measurement.
> - *
> - *     This isn't a standard level of measurement.
> - *
> - *
> - * Prerequisites
> - * =============
> - *
> - * Any symbol can have prerequisites, which are specified as a string
> giving an
> - * additional expression that must be true whenever the symbol is
> referenced.
> - * For example, the "icmp4.type" symbol might have prerequisite "icmp4",
> which
> - * would cause an expression "icmp4.type == 0" to be interpreted as
> "icmp4.type
> - * == 0 && icmp4", which would in turn expand to "icmp4.type == 0 &&
> eth.type
> - * == 0x800 && ip4.proto == 1" (assuming "icmp4" is a predicate defined as
> - * suggested under "Types" above).
> - *
> - *
> - * Crossproducting
> - * ===============
> - *
> - * Ordinarily OVN is willing to consider using any field as a dimension
> in the
> - * Open vSwitch "conjunctive match" extension (see ovs-ofctl(8)).
> However,
> - * some fields can't actually be used that way because they are necessary
> as
> - * prerequisites.  For example, from an expression like "tcp.src ==
> {1,2,3}
> - * && tcp.dst == {4,5,6}", OVN might naturally generate flows like this:
> - *
> - *     conj_id=1,actions=...
> - *     ip,actions=conjunction(1,1/3)
> - *     ip6,actions=conjunction(1,1/3)
> - *     tp_src=1,actions=conjunction(1,2/3)
> - *     tp_src=2,actions=conjunction(1,2/3)
> - *     tp_src=3,actions=conjunction(1,2/3)
> - *     tp_dst=4,actions=conjunction(1,3/3)
> - *     tp_dst=5,actions=conjunction(1,3/3)
> - *     tp_dst=6,actions=conjunction(1,3/3)
> - *
> - * but that's not valid because any flow that matches on tp_src or tp_dst
> must
> - * also match on either ip or ip6.  Thus, one would mark eth.type as "must
> - * crossproduct", to force generating flows like this:
> - *
> - *     conj_id=1,actions=...
> - *     ip,tp_src=1,actions=conjunction(1,1/2)
> - *     ip,tp_src=2,actions=conjunction(1,1/2)
> - *     ip,tp_src=3,actions=conjunction(1,1/2)
> - *     ip6,tp_src=1,actions=conjunction(1,1/2)
> - *     ip6,tp_src=2,actions=conjunction(1,1/2)
> - *     ip6,tp_src=3,actions=conjunction(1,1/2)
> - *     ip,tp_dst=4,actions=conjunction(1,2/2)
> - *     ip,tp_dst=5,actions=conjunction(1,2/2)
> - *     ip,tp_dst=6,actions=conjunction(1,2/2)
> - *     ip6,tp_dst=4,actions=conjunction(1,2/2)
> - *     ip6,tp_dst=5,actions=conjunction(1,2/2)
> - *     ip6,tp_dst=6,actions=conjunction(1,2/2)
> - *
> - * which are acceptable.
> - */
> -struct expr_symbol {
> -    char *name;
> -    int width;
> -
> -    const struct mf_field *field;     /* Fields only, otherwise NULL. */
> -    const struct ovn_field *ovn_field;  /* OVN Fields only, otherwise
> NULL. */
> -    const struct expr_symbol *parent; /* Subfields only, otherwise NULL.
> */
> -    int parent_ofs;                   /* Subfields only, otherwise 0. */
> -    char *predicate;                  /* Predicates only, otherwise NULL.
> */
> -
> -    enum expr_level level;
> -
> -    char *prereqs;
> -    bool must_crossproduct;
> -    bool rw;
> -};
> -
> -void expr_symbol_format(const struct expr_symbol *, struct ds *);
> -
> -/* A reference to a symbol or a subfield of a symbol.
> - *
> - * For string fields, ofs and n_bits are 0. */
> -struct expr_field {
> -    const struct expr_symbol *symbol; /* The symbol. */
> -    int ofs;                          /* Starting bit offset. */
> -    int n_bits;                       /* Number of bits. */
> -};
> -
> -bool expr_field_parse(struct lexer *, const struct shash *symtab,
> -                      struct expr_field *, struct expr **prereqsp);
> -void expr_field_format(const struct expr_field *, struct ds *);
> -
> -struct expr_symbol *expr_symtab_add_field(struct shash *symtab,
> -                                          const char *name, enum
> mf_field_id,
> -                                          const char *prereqs,
> -                                          bool must_crossproduct);
> -struct expr_symbol *expr_symtab_add_subfield(struct shash *symtab,
> -                                             const char *name,
> -                                             const char *prereqs,
> -                                             const char *subfield);
> -struct expr_symbol *expr_symtab_add_string(struct shash *symtab,
> -                                           const char *name, enum
> mf_field_id,
> -                                           const char *prereqs);
> -struct expr_symbol *expr_symtab_add_predicate(struct shash *symtab,
> -                                              const char *name,
> -                                              const char *expansion);
> -struct expr_symbol *expr_symtab_add_ovn_field(struct shash *symtab,
> -                                              const char *name,
> -                                              enum ovn_field_id id);
> -void expr_symtab_destroy(struct shash *symtab);
> -
> -/* Expression type. */
> -enum expr_type {
> -    EXPR_T_CMP,                 /* Compare symbol with constant. */
> -    EXPR_T_AND,                 /* Logical AND of 2 or more
> subexpressions. */
> -    EXPR_T_OR,                  /* Logical OR of 2 or more
> subexpressions. */
> -    EXPR_T_BOOLEAN,             /* True or false constant. */
> -    EXPR_T_CONDITION,           /* Conditional to be evaluated in the
> -                                 * controller during expr_simplify(),
> -                                 * prior to constructing OpenFlow
> matches. */
> -};
> -
> -/* Expression condition type. */
> -enum expr_cond_type {
> -    EXPR_COND_CHASSIS_RESIDENT, /* Check if specified logical port name is
> -                                 * resident on the controller chassis. */
> -};
> -
> -/* Relational operator. */
> -enum expr_relop {
> -    EXPR_R_EQ,                  /* == */
> -    EXPR_R_NE,                  /* != */
> -    EXPR_R_LT,                  /* < */
> -    EXPR_R_LE,                  /* <= */
> -    EXPR_R_GT,                  /* > */
> -    EXPR_R_GE,                  /* >= */
> -};
> -const char *expr_relop_to_string(enum expr_relop);
> -bool expr_relop_from_token(enum lex_type type, enum expr_relop *relop);
> -
> -/* An abstract syntax tree for a matching expression.
> - *
> - * The expression code maintains and relies on a few important invariants:
> - *
> - *     - An EXPR_T_AND or EXPR_T_OR node never has a child of the same
> type.
> - *       (Any such children could be merged into their parent.)  A node
> may
> - *       have grandchildren of its own type.
> - *
> - *       As a consequence, every nonterminal node at the same distance
> from the
> - *       root has the same type.
> - *
> - *     - EXPR_T_AND and EXPR_T_OR nodes must have at least two children.
> - *
> - *     - An EXPR_T_CMP node always has a nonzero mask, and never has a
> 1-bit
> - *       in its value in a position where the mask is a 0-bit.
> - *
> - * The expr_honors_invariants() function can check invariants. */
> -struct expr {
> -    struct ovs_list node;       /* In parent EXPR_T_AND or EXPR_T_OR if
> any. */
> -    enum expr_type type;        /* Expression type. */
> -
> -    union {
> -        /* EXPR_T_CMP.
> -         *
> -         * The symbol is on the left, e.g. "field < constant". */
> -        struct {
> -            const struct expr_symbol *symbol;
> -            enum expr_relop relop;
> -
> -            union {
> -                char *string;
> -                struct {
> -                    union mf_subvalue value;
> -                    union mf_subvalue mask;
> -                };
> -            };
> -        } cmp;
> -
> -        /* EXPR_T_AND, EXPR_T_OR. */
> -        struct ovs_list andor;
> -
> -        /* EXPR_T_BOOLEAN. */
> -        bool boolean;
> -
> -        /* EXPR_T_CONDITION. */
> -        struct {
> -            enum expr_cond_type type;
> -            bool not;
> -            /* XXX Should arguments for conditions be generic? */
> -            char *string;
> -        } cond;
> -    };
> -};
> -
> -struct expr *expr_create_boolean(bool b);
> -struct expr *expr_create_andor(enum expr_type);
> -struct expr *expr_combine(enum expr_type, struct expr *a, struct expr *b);
> -
> -static inline struct expr *
> -expr_from_node(const struct ovs_list *node)
> -{
> -    return CONTAINER_OF(node, struct expr, node);
> -}
> -
> -void expr_format(const struct expr *, struct ds *);
> -void expr_print(const struct expr *);
> -struct expr *expr_parse(struct lexer *, const struct shash *symtab,
> -                        const struct shash *addr_sets,
> -                        const struct shash *port_groups,
> -                        struct sset *addr_sets_ref);
> -struct expr *expr_parse_string(const char *, const struct shash *symtab,
> -                               const struct shash *addr_sets,
> -                               const struct shash *port_groups,
> -                               struct sset *addr_sets_ref,
> -                               char **errorp);
> -
> -struct expr *expr_clone(struct expr *);
> -void expr_destroy(struct expr *);
> -
> -struct expr *expr_annotate(struct expr *, const struct shash *symtab,
> -                           char **errorp);
> -struct expr *expr_simplify(struct expr *,
> -                           bool (*is_chassis_resident)(const void *c_aux,
> -                                                       const char
> *port_name),
> -                           const void *c_aux);
> -struct expr *expr_normalize(struct expr *);
> -
> -bool expr_honors_invariants(const struct expr *);
> -bool expr_is_simplified(const struct expr *);
> -bool expr_is_normalized(const struct expr *);
> -
> -char *expr_parse_microflow(const char *, const struct shash *symtab,
> -                           const struct shash *addr_sets,
> -                           const struct shash *port_groups,
> -                           bool (*lookup_port)(const void *aux,
> -                                               const char *port_name,
> -                                               unsigned int *portp),
> -                           const void *aux, struct flow *uflow)
> -    OVS_WARN_UNUSED_RESULT;
> -
> -bool expr_evaluate(const struct expr *, const struct flow *uflow,
> -                   bool (*lookup_port)(const void *aux, const char
> *port_name,
> -                                       unsigned int *portp),
> -                   const void *aux);
> -
> -/* Converting expressions to OpenFlow flows. */
> -
> -/* An OpenFlow match generated from a Boolean expression.  See
> - * expr_to_matches() for more information. */
> -struct expr_match {
> -    struct hmap_node hmap_node;
> -    struct match match;
> -    struct cls_conjunction *conjunctions;
> -    size_t n, allocated;
> -};
> -
> -uint32_t expr_to_matches(const struct expr *,
> -                         bool (*lookup_port)(const void *aux,
> -                                             const char *port_name,
> -                                             unsigned int *portp),
> -                         const void *aux,
> -                         struct hmap *matches);
> -void expr_matches_destroy(struct hmap *matches);
> -void expr_matches_print(const struct hmap *matches, FILE *);
> -
> -/* Action parsing helper. */
> -
> -char *expr_type_check(const struct expr_field *, int n_bits, bool rw)
> -    OVS_WARN_UNUSED_RESULT;
> -struct mf_subfield expr_resolve_field(const struct expr_field *);
> -
> -/* Type of a "union expr_constant" or "struct expr_constant_set". */
> -enum expr_constant_type {
> -    EXPR_C_INTEGER,
> -    EXPR_C_STRING
> -};
> -
> -/* A string or integer constant (one must know which from context). */
> -union expr_constant {
> -    /* Integer constant.
> -     *
> -     * The width of a constant isn't always clear, e.g. if you write "1",
> -     * there's no way to tell whether you mean for that to be a 1-bit
> constant
> -     * or a 128-bit constant or somewhere in between. */
> -    struct {
> -        union mf_subvalue value;
> -        union mf_subvalue mask; /* Only initialized if 'masked'. */
> -        bool masked;
> -
> -        enum lex_format format; /* From the constant's lex_token. */
> -    };
> -
> -    /* Null-terminated string constant. */
> -    char *string;
> -};
> -
> -bool expr_constant_parse(struct lexer *, const struct expr_field *,
> -                         union expr_constant *);
> -void expr_constant_format(const union expr_constant *,
> -                          enum expr_constant_type, struct ds *);
> -void expr_constant_destroy(const union expr_constant *,
> -                           enum expr_constant_type);
> -
> -/* A collection of "union expr_constant"s of the same type. */
> -struct expr_constant_set {
> -    union expr_constant *values;  /* Constants. */
> -    size_t n_values;              /* Number of constants. */
> -    enum expr_constant_type type; /* Type of the constants. */
> -    bool in_curlies;              /* Whether the constants were in {}. */
> -};
> -
> -bool expr_constant_set_parse(struct lexer *, struct expr_constant_set *);
> -void expr_constant_set_format(const struct expr_constant_set *, struct ds
> *);
> -void expr_constant_set_destroy(struct expr_constant_set *cs);
> -
> -
> -/* Constant sets.
> - *
> - * For example, instead of referring to a set of IP addresses as:
> - *    {addr1, addr2, ..., addrN}
> - * You can register a set of values and refer to them as:
> - *    $name
> - *
> - * If convert_to_integer is true, the set must contain
> - * integer/masked-integer values. The values that don't qualify
> - * are ignored.
> - */
> -
> -void expr_const_sets_add(struct shash *const_sets, const char *name,
> -                         const char * const *values, size_t n_values,
> -                         bool convert_to_integer);
> -void expr_const_sets_remove(struct shash *const_sets, const char *name);
> -void expr_const_sets_destroy(struct shash *const_sets);
> -
> -#endif /* ovn/expr.h */
> diff --git a/include/ovn/lex.h b/include/ovn/lex.h
> deleted file mode 100644
> index 8d5585766..000000000
> --- a/include/ovn/lex.h
> +++ /dev/null
> @@ -1,152 +0,0 @@
> -/*
> - * Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_LEX_H
> -#define OVN_LEX_H 1
> -
> -/* OVN lexical analyzer
> - * ====================
> - *
> - * This is a simple lexical analyzer (or tokenizer) for OVN match
> expressions
> - * and ACLs. */
> -
> -#include "openvswitch/meta-flow.h"
> -
> -struct ds;
> -
> -/* Token type. */
> -enum lex_type {
> -    LEX_T_END,                  /* end of input */
> -
> -    /* Tokens with auxiliary data. */
> -    LEX_T_ID,                   /* foo */
> -    LEX_T_STRING,               /* "foo" */
> -    LEX_T_INTEGER,              /* 12345 or 1.2.3.4 or ::1 or
> 01:02:03:04:05 */
> -    LEX_T_MASKED_INTEGER,       /* 12345/10 or 1.2.0.0/16 or ::2/127
> or... */
> -    LEX_T_MACRO,                /* $NAME */
> -    LEX_T_PORT_GROUP,            /* @NAME */
> -    LEX_T_ERROR,                /* invalid input */
> -
> -    /* Bare tokens. */
> -    LEX_T_LPAREN,               /* ( */
> -    LEX_T_RPAREN,               /* ) */
> -    LEX_T_LCURLY,               /* { */
> -    LEX_T_RCURLY,               /* } */
> -    LEX_T_LSQUARE,              /* [ */
> -    LEX_T_RSQUARE,              /* ] */
> -    LEX_T_EQ,                   /* == */
> -    LEX_T_NE,                   /* != */
> -    LEX_T_LT,                   /* < */
> -    LEX_T_LE,                   /* <= */
> -    LEX_T_GT,                   /* > */
> -    LEX_T_GE,                   /* >= */
> -    LEX_T_LOG_NOT,              /* ! */
> -    LEX_T_LOG_AND,              /* && */
> -    LEX_T_LOG_OR,               /* || */
> -    LEX_T_ELLIPSIS,             /* .. */
> -    LEX_T_COMMA,                /* , */
> -    LEX_T_SEMICOLON,            /* ; */
> -    LEX_T_EQUALS,               /* = */
> -    LEX_T_EXCHANGE,             /* <-> */
> -    LEX_T_DECREMENT,            /* -- */
> -    LEX_T_COLON,                /* : */
> -};
> -
> -/* Subtype for LEX_T_INTEGER and LEX_T_MASKED_INTEGER tokens.
> - *
> - * These do not change the semantics of a token; instead, they determine
> the
> - * format used when a token is serialized back to a text form.  That's
> - * important because 3232268289 is meaningless to a human whereas
> 192.168.128.1
> - * has some actual significance. */
> -enum lex_format {
> -    LEX_F_DECIMAL,
> -    LEX_F_HEXADECIMAL,
> -    LEX_F_IPV4,
> -    LEX_F_IPV6,
> -    LEX_F_ETHERNET,
> -};
> -const char *lex_format_to_string(enum lex_format);
> -
> -/* A token. */
> -struct lex_token {
> -    /* One of LEX_*. */
> -    enum lex_type type;
> -
> -    /* Meaningful for LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO
> only.
> -     * For these token types, 's' may point to 'buffer'; otherwise, it
> points
> -     * to malloc()ed memory owned by the token.
> -     *
> -     * Must be NULL for other token types.
> -     *
> -     * For LEX_T_MACRO, 's' does not include the leading $. */
> -    char *s;
> -
> -    /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
> -    enum lex_format format;
> -
> -    union {
> -        /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
> -        struct {
> -            union mf_subvalue value; /* LEX_T_INTEGER,
> LEX_T_MASKED_INTEGER. */
> -            union mf_subvalue mask;  /* LEX_T_MASKED_INTEGER only. */
> -        };
> -
> -        /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR, LEX_T_MACRO only. */
> -        char buffer[256];
> -    };
> -};
> -
> -void lex_token_init(struct lex_token *);
> -void lex_token_destroy(struct lex_token *);
> -void lex_token_swap(struct lex_token *, struct lex_token *);
> -void lex_token_strcpy(struct lex_token *, const char *s, size_t length);
> -void lex_token_strset(struct lex_token *, char *s);
> -void lex_token_vsprintf(struct lex_token *, const char *format, va_list
> args);
> -
> -void lex_token_format(const struct lex_token *, struct ds *);
> -const char *lex_token_parse(struct lex_token *, const char *input,
> -                            const char **startp);
> -
> -/* A lexical analyzer. */
> -struct lexer {
> -    const char *input;          /* Remaining input (not owned by lexer).
> */
> -    const char *start;          /* Start of current token in 'input'. */
> -    struct lex_token token;     /* Current token (owned by lexer). */
> -    char *error;                /* Error message, if any (owned by
> lexer). */
> -};
> -
> -void lexer_init(struct lexer *, const char *input);
> -void lexer_destroy(struct lexer *);
> -
> -enum lex_type lexer_get(struct lexer *);
> -enum lex_type lexer_lookahead(const struct lexer *);
> -bool lexer_match(struct lexer *, enum lex_type);
> -bool lexer_force_match(struct lexer *, enum lex_type);
> -bool lexer_match_id(struct lexer *, const char *id);
> -bool lexer_is_int(const struct lexer *);
> -bool lexer_get_int(struct lexer *, int *value);
> -bool lexer_force_int(struct lexer *, int *value);
> -
> -bool lexer_force_end(struct lexer *);
> -
> -void lexer_error(struct lexer *, const char *message, ...)
> -    OVS_PRINTF_FORMAT(2, 3);
> -void lexer_syntax_error(struct lexer *, const char *message, ...)
> -    OVS_PRINTF_FORMAT(2, 3);
> -
> -char *lexer_steal_error(struct lexer *);
> -
> -#endif /* ovn/lex.h */
> diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
> deleted file mode 100644
> index 9bac8e027..000000000
> --- a/include/ovn/logical-fields.h
> +++ /dev/null
> @@ -1,130 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_LOGICAL_FIELDS_H
> -#define OVN_LOGICAL_FIELDS_H 1
> -
> -#include "openvswitch/meta-flow.h"
> -
> -struct shash;
> -
> -enum ovn_controller_event {
> -    OVN_EVENT_EMPTY_LB_BACKENDS = 0,
> -    OVN_EVENT_MAX,
> -};
> -
> -/* Logical fields.
> - *
> - * These values are documented in ovn-architecture(7), please update the
> - * documentation if you change any of them. */
> -#define MFF_LOG_DATAPATH MFF_METADATA /* Logical datapath (64 bits). */
> -#define MFF_LOG_FLAGS      MFF_REG10  /* One of MLF_* (32 bits). */
> -#define MFF_LOG_DNAT_ZONE  MFF_REG11  /* conntrack dnat zone for gateway
> router
> -                                       * (32 bits). */
> -#define MFF_LOG_SNAT_ZONE  MFF_REG12  /* conntrack snat zone for gateway
> router
> -                                       * (32 bits). */
> -#define MFF_LOG_CT_ZONE    MFF_REG13  /* Logical conntrack zone for lports
> -                                       * (32 bits). */
> -#define MFF_LOG_INPORT     MFF_REG14  /* Logical input port (32 bits). */
> -#define MFF_LOG_OUTPORT    MFF_REG15  /* Logical output port (32 bits). */
> -
> -/* Logical registers.
> - *
> - * Make sure these don't overlap with the logical fields! */
> -#define MFF_LOG_REG0 MFF_REG0
> -#define MFF_N_LOG_REGS 10
> -
> -void ovn_init_symtab(struct shash *symtab);
> -
> -/* MFF_LOG_FLAGS_REG bit assignments */
> -enum mff_log_flags_bits {
> -    MLF_ALLOW_LOOPBACK_BIT = 0,
> -    MLF_RCV_FROM_VXLAN_BIT = 1,
> -    MLF_FORCE_SNAT_FOR_DNAT_BIT = 2,
> -    MLF_FORCE_SNAT_FOR_LB_BIT = 3,
> -    MLF_LOCAL_ONLY_BIT = 4,
> -    MLF_NESTED_CONTAINER_BIT = 5,
> -};
> -
> -/* MFF_LOG_FLAGS_REG flag assignments */
> -enum mff_log_flags {
> -    /* Allow outputting back to inport. */
> -    MLF_ALLOW_LOOPBACK = (1 << MLF_ALLOW_LOOPBACK_BIT),
> -
> -    /* Indicate that a packet was received from a VXLAN tunnel to
> -     * compensate for the lack of egress port information available in
> -     * VXLAN encapsulation.  Egress port information is available for
> -     * Geneve and STT tunnel types. */
> -    MLF_RCV_FROM_VXLAN = (1 << MLF_RCV_FROM_VXLAN_BIT),
> -
> -    /* Indicate that a packet needs a force SNAT in the gateway router
> when
> -     * DNAT has taken place. */
> -    MLF_FORCE_SNAT_FOR_DNAT = (1 << MLF_FORCE_SNAT_FOR_DNAT_BIT),
> -
> -    /* Indicate that a packet needs a force SNAT in the gateway router
> when
> -     * load-balancing has taken place. */
> -    MLF_FORCE_SNAT_FOR_LB = (1 << MLF_FORCE_SNAT_FOR_LB_BIT),
> -
> -    /* Indicate that a packet that should be distributed across multiple
> -     * hypervisors should instead only be output to local targets
> -     */
> -    MLF_LOCAL_ONLY = (1 << MLF_LOCAL_ONLY_BIT),
> -
> -    /* Indicate that a packet was received from a nested container. */
> -    MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT),
> -};
> -
> -/* OVN logical fields
> - * ===================
> - * These are the fields which OVN supports modifying which gets translated
> - * to OFFlow controller action.
> - *
> - * OpenvSwitch doesn't support modifying these fields yet. If a field is
> - * supported later by OpenvSwitch, it can be deleted from here.
> - */
> -
> -enum ovn_field_id {
> -    /*
> -     * Name: "icmp4.frag_mtu" -
> -     * Type: be16
> -     * Description: Sets the low-order 16 bits of the ICMP4 header field
> -     * (that is labelled "unused" in the ICMP specification) of the ICMP4
> -     * packet as per the RFC 1191.
> -     */
> -    OVN_ICMP4_FRAG_MTU,
> -
> -    OVN_FIELD_N_IDS
> -};
> -
> -struct ovn_field {
> -    enum ovn_field_id id;
> -    const char *name;
> -    unsigned int n_bytes;       /* Width of the field in bytes. */
> -    unsigned int n_bits;        /* Number of significant bits in field. */
> -};
> -
> -static inline const struct ovn_field *
> -ovn_field_from_id(enum ovn_field_id id)
> -{
> -    extern const struct ovn_field ovn_fields[OVN_FIELD_N_IDS];
> -    ovs_assert((unsigned int) id < OVN_FIELD_N_IDS);
> -    return &ovn_fields[id];
> -}
> -
> -const char *event_to_string(enum ovn_controller_event event);
> -int string_to_event(const char *s);
> -const struct ovn_field *ovn_field_from_name(const char *name);
> -void ovn_destroy_ovnfields(void);
> -#endif /* ovn/lib/logical-fields.h */
> diff --git a/lib/db-ctl-base.xml b/lib/db-ctl-base.xml
> index a5fcc901c..10124c3ad 100644
> --- a/lib/db-ctl-base.xml
> +++ b/lib/db-ctl-base.xml
> @@ -40,8 +40,8 @@
>      <dd>
>        Either a universally unique identifier in the style of RFC 4122,
>        e.g. <code>f81d4fae-7dec-11d0-a765-00a0c91e6bf6</code>, or an
> <code>@</code><var>name</var>
> -      defined by a <code>get</code> or <code>create</code> command within
> the same <code>ovn-nbctl</code>
> -      invocation.
> +      defined by a <code>get</code> or <code>create</code> command within
> the
> +      same <code>ovs-vsctl</code> invocation.
>      </dd>
>
>    </dl>
> @@ -177,7 +177,7 @@
>        </p>
>
>        <p>
> -        The UUIDs shown for rows created in the same
> <code>ovn-nbctl</code>
> +        The UUIDs shown for rows created in the same
> <code>ovs-vsctl</code>
>          invocation will be wrong.
>        </p>
>
> @@ -199,7 +199,7 @@
>        </p>
>        <p>
>          If <code>@</code><var>name</var> is specified, then the UUID for
> <var>record</var> may be
> -        referred to by that name later in the same <code>ovn-nbctl</code>
> +        referred to by that name later in the same <code>ovs-vsctl</code>
>          invocation in contexts where a UUID is expected.
>        </p>
>        <p>
> @@ -379,8 +379,8 @@
>        </dl>
>        <p>
>          Consider specifying <code>--timeout=0</code> along with
> -        <code>--wait-until</code>, to prevent <code>ovn-nbctl</code> from
> terminating
> -        after waiting only at most 5 seconds.
> +        <code>--wait-until</code>, to prevent <code>ovs-vsctl</code> from
> +        terminating after waiting only at most 5 seconds.
>        </p>
>      </dd>
>
> diff --git a/manpages.mk b/manpages.mk
> index 5f43aa387..5012977aa 100644
> --- a/manpages.mk
> +++ b/manpages.mk
> @@ -1,33 +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 \
> -       lib/db-ctl-base.man \
> -       lib/ovs.tmac \
> -       lib/ssl-bootstrap.man \
> -       lib/ssl.man \
> -       lib/table.man \
> -       lib/vlog.man
> -ovn/utilities/ovn-sbctl.8.in:
> -lib/common.man:
> -lib/db-ctl-base.man:
> -lib/ovs.tmac:
> -lib/ssl-bootstrap.man:
> -lib/ssl.man:
> -lib/table.man:
> -lib/vlog.man:
> -
>  ovsdb/ovsdb-client.1: \
>         ovsdb/ovsdb-client.1.in \
>         lib/common-syn.man \
> diff --git a/ovn/.gitignore b/ovn/.gitignore
> deleted file mode 100644
> index d971938aa..000000000
> --- a/ovn/.gitignore
> +++ /dev/null
> @@ -1,8 +0,0 @@
> -/ovn-architecture.7
> -/ovn-nb.5
> -/ovn-nb.gv
> -/ovn-nb.pic
> -/ovn-sb.5
> -/ovn-sb.gv
> -/ovn-sb.pic
> -/*.ovsschema.stamp
> diff --git a/ovn/TODO.rst b/ovn/TODO.rst
> deleted file mode 100644
> index 33489174f..000000000
> --- a/ovn/TODO.rst
> +++ /dev/null
> @@ -1,147 +0,0 @@
> -..
> -      Licensed under the Apache License, Version 2.0 (the "License"); you
> may
> -      not use this file except in compliance with the License. You may
> obtain
> -      a copy of the License at
> -
> -          http://www.apache.org/licenses/LICENSE-2.0
> -
> -      Unless required by applicable law or agreed to in writing, software
> -      distributed under the License is distributed on an "AS IS" BASIS,
> WITHOUT
> -      WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> See the
> -      License for the specific language governing permissions and
> limitations
> -      under the License.
> -
> -      Convention for heading levels in Open vSwitch documentation:
> -
> -      =======  Heading 0 (reserved for the title in a document)
> -      -------  Heading 1
> -      ~~~~~~~  Heading 2
> -      +++++++  Heading 3
> -      '''''''  Heading 4
> -
> -      Avoid deeper levels because they do not render well.
> -
> -==============
> -OVN To-do List
> -==============
> -
> -* Get incremental updates in ovn-controller and ovn-northd in some
> -  sensible way.
> -
> -* Live migration.
> -
> -  Russell Bryant: "When you're ready to have the destination take over,
> you
> -  have to remove the iface-id from the source and add it at the
> destination and
> -  I think it'd typically be configured on both ends, since it's a clone
> of the
> -  source VM (and it's config)."
> -
> -* VLAN trunk ports.
> -
> -  Russell Bryant: "Today that would require creating 4096 ports for the
> VM and
> -  attach to 4096 OVN networks, so doable, but not quite ideal."
> -
> -* Service function chaining.
> -
> -* MAC learning.
> -
> -  Han Zhou: "To support VMs that hosts workloads with their own macs, e.g.
> -  containers, if not using OVN native container support."
> -
> -* Finish up ARP/ND support: re-checking bindings, expiring bindings.
> -
> -* Hitless upgrade, especially for data plane.
> -
> -* Use OpenFlow "bundles" for transactional data plane updates.
> -
> -* Dynamic IP to MAC binding enhancements.
> -
> -  OVN has basic support for establishing IP to MAC bindings dynamically,
> using
> -  ARP.
> -
> -  * Ratelimiting.
> -
> -    From casual observation, Linux appears to generate at most one ARP per
> -    second per destination.
> -
> -    This might be supported by adding a new OVN logical action for
> -    rate-limiting.
> -
> -  * Tracking queries
> -
> -     It's probably best to only record in the database responses to
> queries
> -     actually issued by an L3 logical router, so somehow they have to be
> -     tracked, probably by putting a tentative binding without a MAC
> address
> -     into the database.
> -
> -  * Renewal and expiration.
> -
> -    Something needs to make sure that bindings remain valid and expire
> those
> -    that become stale.
> -
> -    One way to do this might be to add some support for time to the
> database
> -    server itself.
> -
> -  * Table size limiting.
> -
> -    The table of MAC bindings must not be allowed to grow unreasonably
> large.
> -
> -  * MTU handling (fragmentation on output)
> -
> -* ovsdb-server
> -
> -  ovsdb-server should have adequate features for OVN but it probably
> needs work
> -  for scale and possibly for availability as deployments grow.  Here are
> some
> -  thoughts.
> -
> -  * Multithreading.
> -
> -    If it turns out that other changes don't let ovsdb-server scale
> -    adequately, we can multithread ovsdb-server.  Initially one might
> -    only break protocol handling into separate threads, leaving the
> -    actual database work serialized through a lock.
> -
> -  * Reducing startup time.
> -
> -    As-is, if ovsdb-server restarts, every client will fetch a fresh copy
> of
> -    the part of the database that it cares about.  With hundreds of
> clients,
> -    this could cause heavy CPU load on ovsdb-server and use excessive
> network
> -    bandwidth.  It would be better to allow incremental updates even
> across
> -    connection loss.  One way might be to use "Difference Digests" as
> described
> -    in Epstein et al., "What's the Difference? Efficient Set
> Reconciliation
> -    Without Prior Context".  (I'm not yet aware of previous non-academic
> use of
> -    this technique.)
> -
> -* Support multiple tunnel encapsulations in Chassis.
> -
> -  So far, both ovn-controller and ovn-controller-vtep only allow chassis
> to
> -  have one tunnel encapsulation entry.  We should extend the
> implementation
> -  to support multiple tunnel encapsulations.
> -
> -* Update learned MAC addresses from VTEP to OVN
> -
> -  The VTEP gateway stores all MAC addresses learned from its physical
> -  interfaces in the 'Ucast_Macs_Local' and the 'Mcast_Macs_Local' tables.
> -  ovn-controller-vtep should be able to update that information back to
> -  ovn-sb database, so that other chassis know where to send packets
> destined
> -  to the extended external network instead of broadcasting.
> -
> -* Translate ovn-sb Multicast_Group table into VTEP config
> -
> -  The ovn-controller-vtep daemon should be able to translate the
> -  Multicast_Group table entry in ovn-sb database into Mcast_Macs_Remote
> table
> -  configuration in VTEP database.
> -
> -* OVN OCF pacemaker script to support Active / Passive HA for OVN dbs
> provides
> -  the option to configure the inactivity_probe value. The default 5
> seconds
> -  inactivity_probe value is not sufficient and ovsdb-server drops the
> client
> -  IDL connections for openstack deployments when the neutron server is
> heavily
> -  loaded.
> -
> -  We need to find a proper solution to solve this issue instead of
> increasing
> -  the inactivity_probe value.
> -
> -* ACL
> -
> -  * Support FTP ALGs.
> -
> -  * Support reject action.
> diff --git a/ovn/automake.mk b/ovn/automake.mk
> deleted file mode 100644
> index b33112ef1..000000000
> --- a/ovn/automake.mk
> +++ /dev/null
> @@ -1,92 +0,0 @@
> -# OVN southbound schema and IDL
> -EXTRA_DIST += ovn/ovn-sb.ovsschema
> -pkgdata_DATA += ovn/ovn-sb.ovsschema
> -
> -# OVN southbound E-R diagram
> -#
> -# If "python" or "dot" is not available, then we do not add graphical
> diagram
> -# to the documentation.
> -if HAVE_PYTHON
> -if HAVE_DOT
> -ovn/ovn-sb.gv: ovsdb/ovsdb-dot.in ovn/ovn-sb.ovsschema
> -       $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn/ovn-sb.ovsschema
> > $@
> -ovn/ovn-sb.pic: ovn/ovn-sb.gv ovsdb/dot2pic
> -       $(AM_V_GEN)(dot -T plain < ovn/ovn-sb.gv | $(PYTHON)
> $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \
> -       mv $@.tmp $@
> -OVN_SB_PIC = ovn/ovn-sb.pic
> -OVN_SB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_SB_PIC)
> -CLEANFILES += ovn/ovn-sb.gv ovn/ovn-sb.pic
> -endif
> -endif
> -
> -# OVN southbound schema documentation
> -EXTRA_DIST += ovn/ovn-sb.xml
> -CLEANFILES += ovn/ovn-sb.5
> -man_MANS += ovn/ovn-sb.5
> -ovn/ovn-sb.5: \
> -       ovsdb/ovsdb-doc ovn/ovn-sb.xml ovn/ovn-sb.ovsschema $(OVN_SB_PIC)
> -       $(AM_V_GEN)$(OVSDB_DOC) \
> -               $(OVN_SB_DOT_DIAGRAM_ARG) \
> -               --version=$(VERSION) \
> -               $(srcdir)/ovn/ovn-sb.ovsschema \
> -               $(srcdir)/ovn/ovn-sb.xml > $@.tmp && \
> -       mv $@.tmp $@
> -
> -# OVN northbound schema and IDL
> -EXTRA_DIST += ovn/ovn-nb.ovsschema
> -pkgdata_DATA += ovn/ovn-nb.ovsschema
> -
> -# OVN northbound E-R diagram
> -#
> -# If "python" or "dot" is not available, then we do not add graphical
> diagram
> -# to the documentation.
> -if HAVE_PYTHON
> -if HAVE_DOT
> -ovn/ovn-nb.gv: ovsdb/ovsdb-dot.in ovn/ovn-nb.ovsschema
> -       $(AM_V_GEN)$(OVSDB_DOT) --no-arrows $(srcdir)/ovn/ovn-nb.ovsschema
> > $@
> -ovn/ovn-nb.pic: ovn/ovn-nb.gv ovsdb/dot2pic
> -       $(AM_V_GEN)(dot -T plain < ovn/ovn-nb.gv | $(PYTHON)
> $(srcdir)/ovsdb/dot2pic -f 3) > $@.tmp && \
> -       mv $@.tmp $@
> -OVN_NB_PIC = ovn/ovn-nb.pic
> -OVN_NB_DOT_DIAGRAM_ARG = --er-diagram=$(OVN_NB_PIC)
> -CLEANFILES += ovn/ovn-nb.gv ovn/ovn-nb.pic
> -endif
> -endif
> -
> -# OVN northbound schema documentation
> -EXTRA_DIST += ovn/ovn-nb.xml
> -CLEANFILES += ovn/ovn-nb.5
> -man_MANS += ovn/ovn-nb.5
> -ovn/ovn-nb.5: \
> -       ovsdb/ovsdb-doc ovn/ovn-nb.xml ovn/ovn-nb.ovsschema $(OVN_NB_PIC)
> -       $(AM_V_GEN)$(OVSDB_DOC) \
> -               $(OVN_NB_DOT_DIAGRAM_ARG) \
> -               --version=$(VERSION) \
> -               $(srcdir)/ovn/ovn-nb.ovsschema \
> -               $(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
> -       $(srcdir)/build-aux/cksum-schema-check $? $@
> -CLEANFILES += ovn/ovn-nb.ovsschema.stamp
> -
> -# Version checking for ovn-sb.ovsschema.
> -ALL_LOCAL += ovn/ovn-sb.ovsschema.stamp
> -ovn/ovn-sb.ovsschema.stamp: ovn/ovn-sb.ovsschema
> -       $(srcdir)/build-aux/cksum-schema-check $? $@
> -CLEANFILES += ovn/ovn-sb.ovsschema.stamp
> -
> -include ovn/controller/automake.mk
> -include ovn/controller-vtep/automake.mk
> -include ovn/lib/automake.mk
> -include ovn/northd/automake.mk
> -include ovn/utilities/automake.mk
> diff --git a/ovn/controller-vtep/.gitignore
> b/ovn/controller-vtep/.gitignore
> deleted file mode 100644
> index 3ec8072c7..000000000
> --- a/ovn/controller-vtep/.gitignore
> +++ /dev/null
> @@ -1,2 +0,0 @@
> -/ovn-controller-vtep
> -/ovn-controller-vtep.8
> diff --git a/ovn/controller-vtep/automake.mk b/ovn/controller-vtep/
> automake.mk
> deleted file mode 100644
> index 0c83dc70a..000000000
> --- a/ovn/controller-vtep/automake.mk
> +++ /dev/null
> @@ -1,14 +0,0 @@
> -bin_PROGRAMS += ovn/controller-vtep/ovn-controller-vtep
> -ovn_controller_vtep_ovn_controller_vtep_SOURCES = \
> -       ovn/controller-vtep/binding.c \
> -       ovn/controller-vtep/binding.h \
> -       ovn/controller-vtep/gateway.c \
> -       ovn/controller-vtep/gateway.h \
> -       ovn/controller-vtep/ovn-controller-vtep.c \
> -       ovn/controller-vtep/ovn-controller-vtep.h \
> -       ovn/controller-vtep/vtep.c \
> -       ovn/controller-vtep/vtep.h
> -ovn_controller_vtep_ovn_controller_vtep_LDADD = ovn/lib/libovn.la lib/
> libopenvswitch.la vtep/libvtep.la
> -man_MANS += ovn/controller-vtep/ovn-controller-vtep.8
> -EXTRA_DIST += ovn/controller-vtep/ovn-controller-vtep.8.xml
> -CLEANFILES += ovn/controller-vtep/ovn-controller-vtep.8
> diff --git a/ovn/controller-vtep/binding.c b/ovn/controller-vtep/binding.c
> deleted file mode 100644
> index 9cbfadc71..000000000
> --- a/ovn/controller-vtep/binding.c
> +++ /dev/null
> @@ -1,274 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "binding.h"
> -
> -#include "openvswitch/shash.h"
> -#include "lib/smap.h"
> -#include "lib/util.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn-controller-vtep.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "vtep/vtep-idl.h"
> -
> -VLOG_DEFINE_THIS_MODULE(binding);
> -
> -/*
> - * This module scans through the Port_Binding table in ovnsb.  If there
> is a
> - * logical port binding entry for logical switch in vtep gateway chassis's
> - * 'vtep_logical_switches' column, sets the binding's chassis column to
> the
> - * corresponding vtep gateway chassis.
> - *
> - */
> -
> -
> -/* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec'
> - * has already been bound to another port binding entry, and resets
> - * 'port_binding_rec''s chassis column.  Otherwise, updates 'ls_to_pb'
> - * and returns false. */
> -static bool
> -check_pb_conflict(struct shash *ls_to_pb,
> -                  const struct sbrec_port_binding *port_binding_rec,
> -                  const char *chassis_name,
> -                  const char *vtep_lswitch)
> -{
> -    const struct sbrec_port_binding *pb_conflict =
> -        shash_find_data(ls_to_pb, vtep_lswitch);
> -
> -    if (pb_conflict) {
> -        VLOG_WARN("logical switch (%s), on vtep gateway chassis "
> -                  "(%s) has already been associated with logical "
> -                  "port (%s), ignore logical port (%s)",
> -                  vtep_lswitch, chassis_name,
> -                  pb_conflict->logical_port,
> -                  port_binding_rec->logical_port);
> -        sbrec_port_binding_set_chassis(port_binding_rec, NULL);
> -
> -        return true;
> -    }
> -
> -    shash_add(ls_to_pb, vtep_lswitch, port_binding_rec);
> -    return false;
> -}
> -
> -/* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec'
> - * has already been bound to a different datapath, and resets
> - * 'port_binding_rec''s chassis column.  Otherwise, updates 'ls_to_db' and
> - * returns false. */
> -static bool
> -check_db_conflict(struct shash *ls_to_db,
> -                  const struct sbrec_port_binding *port_binding_rec,
> -                  const char *chassis_name,
> -                  const char *vtep_lswitch)
> -{
> -    const struct sbrec_datapath_binding *db_conflict =
> -        shash_find_data(ls_to_db, vtep_lswitch);
> -
> -    if (db_conflict && db_conflict != port_binding_rec->datapath) {
> -        VLOG_WARN("logical switch (%s), on vtep gateway chassis "
> -                  "(%s) has already been associated with logical "
> -                  "datapath (with tunnel key %"PRId64"), ignore "
> -                  "logical port (%s) which belongs to logical "
> -                  "datapath (with tunnel key %"PRId64")",
> -                  vtep_lswitch, chassis_name,
> -                  db_conflict->tunnel_key,
> -                  port_binding_rec->logical_port,
> -                  port_binding_rec->datapath->tunnel_key);
> -        sbrec_port_binding_set_chassis(port_binding_rec, NULL);
> -
> -        return true;
> -    }
> -
> -    shash_replace(ls_to_db, vtep_lswitch, port_binding_rec->datapath);
> -    return false;
> -}
> -
> -/* Updates the 'port_binding_rec''s chassis column to 'chassis_rec'. */
> -static void
> -update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
> -                  const struct sbrec_chassis *chassis_rec)
> -{
> -    if (port_binding_rec->chassis != chassis_rec) {
> -        if (chassis_rec && port_binding_rec->chassis) {
> -            VLOG_DBG("Changing chassis association of logical "
> -                     "port (%s) from (%s) to (%s)",
> -                     port_binding_rec->logical_port,
> -                     port_binding_rec->chassis->name,
> -                     chassis_rec->name);
> -        }
> -        sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
> -    }
> -}
> -
> -
> -/* Checks and updates logical port to vtep logical switch bindings for
> each
> - * physical switch in VTEP. */
> -void
> -binding_run(struct controller_vtep_ctx *ctx)
> -{
> -    if (!ctx->ovnsb_idl_txn) {
> -        return;
> -    }
> -
> -    /* 'ls_to_db'
> -     *
> -     * Maps vtep logical switch name to the datapath binding entry.  This
> is
> -     * used to guarantee that each vtep logical switch is only included
> -     * in only one ovn datapath (ovn logical switch).  See
> check_db_conflict()
> -     * for details.
> -     *
> -     * 'ls_to_pb'
> -     *
> -     * Maps vtep logical switch name to the port binding entry.  This is
> used
> -     * to guarantee that each vtep logical switch on a vtep physical
> switch
> -     * is only bound to one logical port.  See check_pb_conflict() for
> -     * details.
> -     *
> -     */
> -    struct shash ls_to_db = SHASH_INITIALIZER(&ls_to_db);
> -
> -    /* Stores the 'chassis' and the 'ls_to_pb' map related to
> -     * a vtep physcial switch. */
> -    struct ps {
> -        const struct sbrec_chassis *chassis_rec;
> -        struct shash ls_to_pb;
> -    };
> -    struct shash ps_map = SHASH_INITIALIZER(&ps_map);
> -    const struct vteprec_physical_switch *pswitch;
> -    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
> -        const struct sbrec_chassis *chassis_rec
> -            = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
> -        struct ps *ps = xmalloc(sizeof *ps);
> -        size_t i;
> -
> -        /* 'chassis_rec' must exist. */
> -        ovs_assert(chassis_rec);
> -        ps->chassis_rec = chassis_rec;
> -        shash_init(&ps->ls_to_pb);
> -        for (i = 0; i < chassis_rec->n_vtep_logical_switches; i++) {
> -            shash_add(&ps->ls_to_pb,
> chassis_rec->vtep_logical_switches[i],
> -                      NULL);
> -        }
> -        shash_add(&ps_map, chassis_rec->name, ps);
> -    }
> -
> -    ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
> -                              "ovn-controller-vtep: updating bindings");
> -
> -    const struct sbrec_port_binding *port_binding_rec;
> -    /* Port binding for vtep gateway chassis must have type "vtep",
> -     * and matched physical switch name and logical switch name. */
> -    SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
> -        const char *type = port_binding_rec->type;
> -        const char *vtep_pswitch = smap_get(&port_binding_rec->options,
> -                                            "vtep-physical-switch");
> -        const char *vtep_lswitch = smap_get(&port_binding_rec->options,
> -                                            "vtep-logical-switch");
> -        struct ps *ps
> -            = vtep_pswitch ? shash_find_data(&ps_map, vtep_pswitch) :
> NULL;
> -        bool found_ls
> -            = ps && vtep_lswitch && shash_find(&ps->ls_to_pb,
> vtep_lswitch);
> -
> -        if (!strcmp(type, "vtep") && found_ls) {
> -            bool pb_conflict, db_conflict;
> -
> -            pb_conflict = check_pb_conflict(&ps->ls_to_pb,
> port_binding_rec,
> -                                            ps->chassis_rec->name,
> -                                            vtep_lswitch);
> -            db_conflict = check_db_conflict(&ls_to_db, port_binding_rec,
> -                                            ps->chassis_rec->name,
> -                                            vtep_lswitch);
> -            /* Updates port binding's chassis column when there
> -             * is no conflict. */
> -            if (!pb_conflict && !db_conflict) {
> -                update_pb_chassis(port_binding_rec, ps->chassis_rec);
> -            }
> -        } else if (port_binding_rec->chassis
> -                   && shash_find(&ps_map,
> port_binding_rec->chassis->name)) {
> -            /* Resets 'port_binding_rec' since it is no longer bound to
> -             * any vtep logical switch. */
> -            update_pb_chassis(port_binding_rec, NULL);
> -        }
> -    }
> -
> -    struct shash_node *iter, *next;
> -    SHASH_FOR_EACH_SAFE (iter, next, &ps_map) {
> -        struct ps *ps = iter->data;
> -        struct shash_node *node;
> -
> -        SHASH_FOR_EACH (node, &ps->ls_to_pb) {
> -            if (!node->data) {
> -                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 5);
> -                VLOG_DBG_RL(&rl, "No port binding entry for logical
> switch (%s)"
> -                            "on vtep gateway chassis (%s)", node->name,
> -                            ps->chassis_rec->name);
> -            }
> -        }
> -        shash_delete(&ps_map, iter);
> -        shash_destroy(&ps->ls_to_pb);
> -        free(ps);
> -    }
> -    shash_destroy(&ls_to_db);
> -    shash_destroy(&ps_map);
> -}
> -
> -/* Removes all port binding association with vtep gateway chassis.
> - * Returns true when done (i.e. there is no change made to
> 'ctx->ovnsb_idl'),
> - * otherwise returns false. */
> -bool
> -binding_cleanup(struct controller_vtep_ctx *ctx)
> -{
> -    if (!ctx->ovnsb_idl_txn) {
> -        return false;
> -    }
> -
> -    struct shash ch_to_pb = SHASH_INITIALIZER(&ch_to_pb);
> -    const struct sbrec_port_binding *port_binding_rec;
> -    bool all_done = true;
> -    /* Hashs all port binding entries using the associated chassis name.
> */
> -    SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
> -        if (port_binding_rec->chassis) {
> -            shash_add(&ch_to_pb, port_binding_rec->chassis->name,
> -                      port_binding_rec);
> -        }
> -    }
> -
> -    ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
> -                              "ovn-controller-vtep: removing bindings");
> -
> -    const struct vteprec_physical_switch *pswitch;
> -    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
> -        const struct sbrec_chassis *chassis_rec
> -            = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
> -
> -        if (!chassis_rec) {
> -            continue;
> -        }
> -
> -        for (;;) {
> -            port_binding_rec = shash_find_and_delete(&ch_to_pb,
> -                                                     chassis_rec->name);
> -            if (!port_binding_rec) {
> -                break;
> -            }
> -            all_done = false;
> -            update_pb_chassis(port_binding_rec, NULL);
> -        }
> -    }
> -    shash_destroy(&ch_to_pb);
> -
> -    return all_done;
> -}
> diff --git a/ovn/controller-vtep/binding.h b/ovn/controller-vtep/binding.h
> deleted file mode 100644
> index 374c1ccf8..000000000
> --- a/ovn/controller-vtep/binding.h
> +++ /dev/null
> @@ -1,27 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -
> -#ifndef OVN_BINDING_H
> -#define OVN_BINDING_H 1
> -
> -#include <stdbool.h>
> -
> -struct controller_vtep_ctx;
> -
> -void binding_run(struct controller_vtep_ctx *);
> -bool binding_cleanup(struct controller_vtep_ctx *);
> -
> -#endif /* ovn/controller-gw/binding.h */
> diff --git a/ovn/controller-vtep/gateway.c b/ovn/controller-vtep/gateway.c
> deleted file mode 100644
> index 619c3c49a..000000000
> --- a/ovn/controller-vtep/gateway.c
> +++ /dev/null
> @@ -1,230 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "gateway.h"
> -
> -#include "openvswitch/poll-loop.h"
> -#include "lib/simap.h"
> -#include "lib/sset.h"
> -#include "lib/util.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "vtep/vtep-idl.h"
> -#include "ovn-controller-vtep.h"
> -
> -VLOG_DEFINE_THIS_MODULE(gateway);
> -
> -/*
> - * Registers the physical switches in vtep to ovnsb as chassis.  For each
> - * physical switch in the vtep database, finds all vtep logical switches
> that
> - * are associated with the physical switch, and updates the corresponding
> - * chassis's 'vtep_logical_switches' column.
> - *
> - */
> -
> -/* Global revalidation sequence number, incremented at each call to
> - * 'revalidate_gateway()'. */
> -static unsigned int gw_reval_seq;
> -
> -/* Maps all chassis created by the gateway module to their own reval_seq.
> */
> -static struct simap gw_chassis_map = SIMAP_INITIALIZER(&gw_chassis_map);
> -
> -/* Creates and returns a new instance of 'struct sbrec_chassis'. */
> -static const struct sbrec_chassis *
> -create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name,
> -                   const char *encap_ip)
> -{
> -    const struct sbrec_chassis *chassis_rec;
> -    struct sbrec_encap *encap_rec;
> -
> -    VLOG_INFO("add Chassis row for VTEP physical switch (%s)", name);
> -
> -    chassis_rec = sbrec_chassis_insert(txn);
> -    sbrec_chassis_set_name(chassis_rec, name);
> -    encap_rec = sbrec_encap_insert(txn);
> -    sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE);
> -    sbrec_encap_set_ip(encap_rec, encap_ip);
> -    const struct smap options = SMAP_CONST1(&options, "csum", "false");
> -    sbrec_encap_set_options(encap_rec, &options);
> -    sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
> -
> -    return chassis_rec;
> -}
> -
> -/* Revalidates chassis in ovnsb against vtep database.  Creates chassis
> for
> - * new vtep physical switch.  And removes chassis which no longer have
> - * physical switch in vtep.
> - *
> - * xxx: Support multiple tunnel encaps.
> - *
> - * */
> -static void
> -revalidate_gateway(struct controller_vtep_ctx *ctx)
> -{
> -    const struct vteprec_physical_switch *pswitch;
> -
> -    /* Increments the global revalidation sequence number. */
> -    gw_reval_seq++;
> -
> -    ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
> -                              "ovn-controller-vtep: updating vtep
> chassis");
> -
> -    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
> -        const struct sbrec_chassis *chassis_rec;
> -        struct simap_node *gw_node;
> -        const char *encap_ip;
> -
> -        encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : "";
> -        gw_node = simap_find(&gw_chassis_map, pswitch->name);
> -        chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
> -        if (chassis_rec) {
> -            if (!gw_node &&
> -                (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)
> -                 || strcmp(chassis_rec->encaps[0]->ip, encap_ip))) {
> -                VLOG_WARN("Chassis config changing on startup, make sure "
> -                          "multiple chassis are not configured :
> %s/%s->%s/%s",
> -                          chassis_rec->encaps[0]->type,
> -                          chassis_rec->encaps[0]->ip,
> -                          OVN_SB_ENCAP_TYPE, encap_ip);
> -            }
> -            /* Updates chassis's encap if anything changed. */
> -            if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) {
> -                VLOG_WARN("Chassis for VTEP physical switch (%s) can only
> have "
> -                          "encap type \"%s\"", pswitch->name,
> OVN_SB_ENCAP_TYPE);
> -                sbrec_encap_set_type(chassis_rec->encaps[0],
> OVN_SB_ENCAP_TYPE);
> -            }
> -            if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) {
> -                sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip);
> -            }
> -            if (smap_get_bool(&chassis_rec->encaps[0]->options, "csum",
> true)) {
> -                const struct smap options = SMAP_CONST1(&options, "csum",
> -
> "false");
> -                sbrec_encap_set_options(chassis_rec->encaps[0], &options);
> -            }
> -        } else {
> -            if (gw_node) {
> -                VLOG_WARN("Chassis for VTEP physical switch (%s)
> disappears, "
> -                          "maybe deleted by ovn-sbctl, adding it back",
> -                          pswitch->name);
> -            }
> -            /* Creates a new chassis for the VTEP physical switch. */
> -            create_chassis_rec(ctx->ovnsb_idl_txn, pswitch->name,
> encap_ip);
> -        }
> -        /* Updates or creates the simap node for 'pswitch->name'. */
> -        simap_put(&gw_chassis_map, pswitch->name, gw_reval_seq);
> -    }
> -
> -    struct simap_node *iter, *next;
> -    /* For 'gw_node' in 'gw_chassis_map' whose data is not
> -     * 'gw_reval_seq', it means the corresponding physical switch no
> -     * longer exist.  So, garbage collects them. */
> -    SIMAP_FOR_EACH_SAFE (iter, next, &gw_chassis_map) {
> -        if (iter->data != gw_reval_seq) {
> -            const struct sbrec_chassis *chassis_rec;
> -
> -            chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name);
> -            if (chassis_rec) {
> -                sbrec_chassis_delete(chassis_rec);
> -            }
> -            simap_delete(&gw_chassis_map, iter);
> -        }
> -    }
> -}
> -
> -/* Updates the 'vtep_logical_switches' column in the Chassis table based
> - * on vtep database configuration. */
> -static void
> -update_vtep_logical_switches(struct controller_vtep_ctx *ctx)
> -{
> -    const struct vteprec_physical_switch *pswitch;
> -
> -    ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
> -                              "updating chassis's vtep_logical_switches");
> -
> -    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
> -        const struct sbrec_chassis *chassis_rec =
> -            get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
> -        struct sset lswitches = SSET_INITIALIZER(&lswitches);
> -        size_t i;
> -
> -        for (i = 0; i < pswitch->n_ports; i++) {
> -            const struct vteprec_physical_port *port = pswitch->ports[i];
> -            size_t j;
> -
> -            for (j = 0; j < port->n_vlan_bindings; j++) {
> -                const struct vteprec_logical_switch *vtep_lswitch;
> -
> -                vtep_lswitch = port->value_vlan_bindings[j];
> -                /* If not already in 'lswitches', records it. */
> -                if (!sset_find(&lswitches, vtep_lswitch->name)) {
> -                    sset_add(&lswitches, vtep_lswitch->name);
> -                }
> -            }
> -        }
> -
> -        const char **ls_arr = sset_array(&lswitches);
> -        sbrec_chassis_set_vtep_logical_switches(chassis_rec, ls_arr,
> -                                                sset_count(&lswitches));
> -        free(ls_arr);
> -        sset_destroy(&lswitches);
> -    }
> -}
> -
> -
> -void
> -gateway_run(struct controller_vtep_ctx *ctx)
> -{
> -    if (!ctx->ovnsb_idl_txn) {
> -        return;
> -    }
> -
> -    revalidate_gateway(ctx);
> -    update_vtep_logical_switches(ctx);
> -}
> -
> -/* Destroys the chassis table entries for vtep physical switches.
> - * Returns true when done (i.e. there is no change made to
> 'ctx->ovnsb_idl'),
> - * otherwise returns false. */
> -bool
> -gateway_cleanup(struct controller_vtep_ctx *ctx)
> -{
> -    static bool simap_destroyed = false;
> -    const struct vteprec_physical_switch *pswitch;
> -
> -    if (!ctx->ovnsb_idl_txn) {
> -        return false;
> -    }
> -
> -    bool all_done = true;
> -    ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn, "ovn-controller-vtep: "
> -                              "unregistering vtep chassis");
> -    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
> -        const struct sbrec_chassis *chassis_rec;
> -
> -        chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
> -        if (!chassis_rec) {
> -            continue;
> -        }
> -        all_done = false;
> -        sbrec_chassis_delete(chassis_rec);
> -    }
> -    if (!simap_destroyed) {
> -        simap_destroy(&gw_chassis_map);
> -        simap_destroyed = true;
> -    }
> -
> -    return all_done;
> -}
> diff --git a/ovn/controller-vtep/gateway.h b/ovn/controller-vtep/gateway.h
> deleted file mode 100644
> index 0086191d9..000000000
> --- a/ovn/controller-vtep/gateway.h
> +++ /dev/null
> @@ -1,26 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_GATEWAY_H
> -#define OVN_GATEWAY_H 1
> -
> -#include <stdbool.h>
> -
> -struct controller_vtep_ctx;
> -
> -void gateway_run(struct controller_vtep_ctx *);
> -bool gateway_cleanup(struct controller_vtep_ctx *);
> -
> -#endif /* ovn/controller-gw/gateway.h */
> diff --git a/ovn/controller-vtep/ovn-controller-vtep.8.xml
> b/ovn/controller-vtep/ovn-controller-vtep.8.xml
> deleted file mode 100644
> index 2c706e46e..000000000
> --- a/ovn/controller-vtep/ovn-controller-vtep.8.xml
> +++ /dev/null
> @@ -1,80 +0,0 @@
> -<?xml version="1.0" encoding="utf-8"?>
> -<manpage program="ovn-controller-vtep" section="8"
> title="ovn-controller-vtep">
> -    <h1>Name</h1>
> -    <p>ovn-controller-vtep -- Open Virtual Network local controller for
> -       vtep enabled physical switches.
> -    </p>
> -
> -    <h1>Synopsis</h1>
> -    <p><code>ovn-controller-vtep</code> [<var>options</var>]
> -    [<var>--vtep-db=vtep-database</var>]
> [<var>--ovnsb-db=ovnsb-database</var>]
> -    </p>
> -
> -    <h1>Description</h1>
> -    <p>
> -      <code>ovn-controller-vtep</code> is the local controller daemon in
> -      OVN, the Open Virtual Network, for VTEP enabled physical switches.
> -      It connects up to the OVN Southbound database (see
> -      <code>ovn-sb</code>(5)) over the OVSDB protocol, and down to the
> VTEP
> -      database (see <code>vtep</code>(5)) over the OVSDB protocol.
> -    </p>
> -
> -    <h2>PKI Options</h2>
> -    <p>
> -      PKI configuration is required in order to use SSL for the
> connections to
> -      the VTEP and Southbound databases.
> -    </p>
> -    <xi:include href="lib/ssl.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -    <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -    <xi:include href="lib/ssl-peer-ca-cert.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -
> -    <h1>Configuration</h1>
> -    <p>
> -      <code>ovn-controller-vtep</code> retrieves its configuration
> -      information from both the ovnsb and the vtep database.  If the
> -      database locations are not given from command line, the default
> -      is the <code>db.sock</code> in local OVSDB's 'run' directory.
> -      The datapath location must take one of the following forms:
> -    </p>
> -    <ul>
> -      <li>
> -        <p>
> -          <code>ssl:<var>host</var>:<var>port</var></code>
> -        </p>
> -        <p>
> -          The specified SSL <var>port</var> on the give <var>host</var>,
> which
> -          can either be a DNS name (if built with unbound library) or an
> IP
> -          address (IPv4 or IPv6).  If <var>host</var> is an IPv6 address,
> then
> -          wrap <var>host</var> with square brackets, e.g.:
> <code>ssl:[::1]:6640</code>.
> -          The <code>--private-key</code>, <code>--certificate</code> and
> either
> -          of <code>--ca-cert</code> or <code>--bootstrap-ca-cert</code>
> options
> -          are mandatory when this form is used.
> -        </p>
> -      </li>
> -      <li>
> -        <p>
> -          <code>tcp:<var>host</var>:<var>port</var></code>
> -        </p>
> -        <p>
> -          Connect to the given TCP <var>port</var> on <var>host</var>,
> where
> -          <var>host</var> can be a DNS name (if built with unbound
> library) or
> -          IP address (IPv4 or IPv6). If <var>host</var> is an IPv6
> address,
> -          then wrap <var>host</var> with square brackets,
> -          e.g.: <code>tcp:[::1]:6640</code>.
> -        </p>
> -      </li>
> -      <li>
> -        <p>
> -          <code>unix:<var>file</var></code>
> -        </p>
> -        <p>
> -          On POSIX, connect to the Unix domain server socket named
> -          <var>file</var>.
> -        </p>
> -        <p>
> -          On Windows, connect to a localhost TCP port whose value is
> written
> -          in <var>file</var>.
> -        </p>
> -      </li>
> -    </ul>
> -</manpage>
> diff --git a/ovn/controller-vtep/ovn-controller-vtep.c
> b/ovn/controller-vtep/ovn-controller-vtep.c
> deleted file mode 100644
> index 292a3f464..000000000
> --- a/ovn/controller-vtep/ovn-controller-vtep.c
> +++ /dev/null
> @@ -1,272 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include <errno.h>
> -#include <getopt.h>
> -#include <signal.h>
> -#include <stdlib.h>
> -#include <string.h>
> -
> -#include "command-line.h"
> -#include "compiler.h"
> -#include "daemon.h"
> -#include "dirs.h"
> -#include "openvswitch/dynamic-string.h"
> -#include "fatal-signal.h"
> -#include "openvswitch/poll-loop.h"
> -#include "stream.h"
> -#include "stream-ssl.h"
> -#include "unixctl.h"
> -#include "util.h"
> -#include "openvswitch/vconn.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn/lib/ovn-util.h"
> -#include "vtep/vtep-idl.h"
> -
> -#include "binding.h"
> -#include "gateway.h"
> -#include "vtep.h"
> -#include "ovn-controller-vtep.h"
> -
> -static unixctl_cb_func ovn_controller_vtep_exit;
> -
> -static void parse_options(int argc, char *argv[]);
> -OVS_NO_RETURN static void usage(void);
> -
> -static char *vtep_remote;
> -static char *ovnsb_remote;
> -static char *default_db_;
> -
> -int
> -main(int argc, char *argv[])
> -{
> -    struct unixctl_server *unixctl;
> -    bool exiting;
> -    int retval;
> -
> -    ovs_cmdl_proctitle_init(argc, argv);
> -    set_program_name(argv[0]);
> -    service_start(&argc, &argv);
> -    parse_options(argc, argv);
> -    fatal_ignore_sigpipe();
> -
> -    daemonize_start(false);
> -
> -    retval = unixctl_server_create(NULL, &unixctl);
> -    if (retval) {
> -        exit(EXIT_FAILURE);
> -    }
> -    unixctl_command_register("exit", "", 0, 0, ovn_controller_vtep_exit,
> -                             &exiting);
> -
> -    daemonize_complete();
> -
> -    /* Connect to VTEP database. */
> -    struct ovsdb_idl_loop vtep_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
> -        ovsdb_idl_create(vtep_remote, &vteprec_idl_class, true, true));
> -    ovsdb_idl_get_initial_snapshot(vtep_idl_loop.idl);
> -
> -    /* Connect to OVN SB database. */
> -    struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
> -        ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));
> -    ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl);
> -
> -    /* Main loop. */
> -    exiting = false;
> -    while (!exiting) {
> -        struct controller_vtep_ctx ctx = {
> -            .vtep_idl = vtep_idl_loop.idl,
> -            .vtep_idl_txn = ovsdb_idl_loop_run(&vtep_idl_loop),
> -            .ovnsb_idl = ovnsb_idl_loop.idl,
> -            .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
> -        };
> -
> -        gateway_run(&ctx);
> -        binding_run(&ctx);
> -        vtep_run(&ctx);
> -        unixctl_server_run(unixctl);
> -
> -        unixctl_server_wait(unixctl);
> -        if (exiting) {
> -            poll_immediate_wake();
> -        }
> -        ovsdb_idl_loop_commit_and_wait(&vtep_idl_loop);
> -        ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
> -        poll_block();
> -        if (should_service_stop()) {
> -            exiting = true;
> -        }
> -    }
> -
> -    /* It's time to exit.  Clean up the databases. */
> -    bool done = false;
> -    while (!done) {
> -        struct controller_vtep_ctx ctx = {
> -            .vtep_idl = vtep_idl_loop.idl,
> -            .vtep_idl_txn = ovsdb_idl_loop_run(&vtep_idl_loop),
> -            .ovnsb_idl = ovnsb_idl_loop.idl,
> -            .ovnsb_idl_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
> -        };
> -
> -        /* Run all of the cleanup functions, even if one of them returns
> false.
> -         * We're done if all of them return true. */
> -        done = binding_cleanup(&ctx);
> -        done = gateway_cleanup(&ctx) && done;
> -        done = vtep_cleanup(&ctx) && done;
> -        if (done) {
> -            poll_immediate_wake();
> -        }
> -
> -        ovsdb_idl_loop_commit_and_wait(&vtep_idl_loop);
> -        ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
> -        poll_block();
> -    }
> -
> -    unixctl_server_destroy(unixctl);
> -
> -    ovsdb_idl_loop_destroy(&vtep_idl_loop);
> -    ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
> -
> -    free(ovnsb_remote);
> -    free(vtep_remote);
> -    free(default_db_);
> -    service_stop();
> -
> -    exit(retval);
> -}
> -
> -static const char *
> -default_db(void)
> -{
> -    if (!default_db_) {
> -        default_db_ = xasprintf("unix:%s/db.sock", ovs_rundir());
> -    }
> -    return default_db_;
> -}
> -
> -static void
> -parse_options(int argc, char *argv[])
> -{
> -    enum {
> -        OPT_PEER_CA_CERT = UCHAR_MAX + 1,
> -        OPT_BOOTSTRAP_CA_CERT,
> -        VLOG_OPTION_ENUMS,
> -        DAEMON_OPTION_ENUMS,
> -        SSL_OPTION_ENUMS,
> -    };
> -
> -    static struct option long_options[] = {
> -        {"ovnsb-db", required_argument, NULL, 'd'},
> -        {"vtep-db", required_argument, NULL, 'D'},
> -        {"help", no_argument, NULL, 'h'},
> -        {"version", no_argument, NULL, 'V'},
> -        VLOG_LONG_OPTIONS,
> -        DAEMON_LONG_OPTIONS,
> -        STREAM_SSL_LONG_OPTIONS,
> -        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
> -        {"bootstrap-ca-cert", required_argument, NULL,
> OPT_BOOTSTRAP_CA_CERT},
> -        {NULL, 0, NULL, 0}
> -    };
> -    char *short_options =
> ovs_cmdl_long_options_to_short_options(long_options);
> -
> -    for (;;) {
> -        int c;
> -
> -        c = getopt_long(argc, argv, short_options, long_options, NULL);
> -        if (c == -1) {
> -            break;
> -        }
> -
> -        switch (c) {
> -        case 'd':
> -            ovnsb_remote = xstrdup(optarg);
> -            break;
> -
> -        case 'D':
> -            vtep_remote = xstrdup(optarg);
> -            break;
> -
> -        case 'h':
> -            usage();
> -
> -        case 'V':
> -            ovs_print_version(OFP13_VERSION, OFP13_VERSION);
> -            exit(EXIT_SUCCESS);
> -
> -        VLOG_OPTION_HANDLERS
> -        DAEMON_OPTION_HANDLERS
> -        STREAM_SSL_OPTION_HANDLERS
> -
> -        case OPT_PEER_CA_CERT:
> -            stream_ssl_set_peer_ca_cert_file(optarg);
> -            break;
> -
> -        case OPT_BOOTSTRAP_CA_CERT:
> -            stream_ssl_set_ca_cert_file(optarg, true);
> -            break;
> -
> -        case '?':
> -            exit(EXIT_FAILURE);
> -
> -        default:
> -            abort();
> -        }
> -    }
> -    free(short_options);
> -
> -    if (!ovnsb_remote) {
> -        ovnsb_remote = xstrdup(default_sb_db());
> -    }
> -
> -    if (!vtep_remote) {
> -        vtep_remote = xstrdup(default_db());
> -    }
> -}
> -
> -static void
> -usage(void)
> -{
> -    printf("\
> -%s: OVN controller VTEP\n\
> -usage %s [OPTIONS]\n\
> -\n\
> -Options:\n\
> -  --vtep-db=DATABASE        connect to vtep database at DATABASE\n\
> -                            (default: %s)\n\
> -  --ovnsb-db=DATABASE       connect to ovn-sb database at DATABASE\n\
> -                            (default: %s)\n\
> -  -h, --help                display this help message\n\
> -  -o, --options             list available options\n\
> -  -V, --version             display version information\n\
> -", program_name, program_name, default_db(), default_sb_db());
> -    stream_usage("database", true, false, true);
> -    daemon_usage();
> -    vlog_usage();
> -    exit(EXIT_SUCCESS);
> -}
> -
> -
> -static void
> -ovn_controller_vtep_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -                       const char *argv[] OVS_UNUSED, void *exiting_)
> -{
> -    bool *exiting = exiting_;
> -    *exiting = true;
> -
> -    unixctl_command_reply(conn, NULL);
> -}
> diff --git a/ovn/controller-vtep/ovn-controller-vtep.h
> b/ovn/controller-vtep/ovn-controller-vtep.h
> deleted file mode 100644
> index 435a730d9..000000000
> --- a/ovn/controller-vtep/ovn-controller-vtep.h
> +++ /dev/null
> @@ -1,51 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -
> -#ifndef OVN_CONTROLLER_VTEP_H
> -#define OVN_CONTROLLER_VTEP_H 1
> -
> -#include "ovn/lib/ovn-sb-idl.h"
> -
> -struct ovsdb_idl;
> -struct ovsdb_idl_txn;
> -
> -struct controller_vtep_ctx {
> -    struct ovsdb_idl *ovnsb_idl;
> -    struct ovsdb_idl_txn *ovnsb_idl_txn;
> -
> -    struct ovsdb_idl *vtep_idl;
> -    struct ovsdb_idl_txn *vtep_idl_txn;
> -};
> -
> -/* VTEP needs what VTEP needs. */
> -#define OVN_SB_ENCAP_TYPE "vxlan"
> -#define VTEP_ENCAP_TYPE "vxlan_over_ipv4"
> -
> -static inline const struct sbrec_chassis *
> -get_chassis_by_name(struct ovsdb_idl *ovnsb_idl, const char *chassis_id)
> -{
> -    const struct sbrec_chassis *chassis_rec;
> -
> -    SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) {
> -        if (!strcmp(chassis_rec->name, chassis_id)) {
> -            break;
> -        }
> -    }
> -
> -    return chassis_rec;
> -}
> -
> -#endif /* ovn/ovn-controller-vtep.h */
> diff --git a/ovn/controller-vtep/vtep.c b/ovn/controller-vtep/vtep.c
> deleted file mode 100644
> index a72b149eb..000000000
> --- a/ovn/controller-vtep/vtep.c
> +++ /dev/null
> @@ -1,600 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "vtep.h"
> -
> -#include "lib/hash.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/shash.h"
> -#include "lib/smap.h"
> -#include "lib/sset.h"
> -#include "lib/util.h"
> -#include "ovn-controller-vtep.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "vtep/vtep-idl.h"
> -
> -VLOG_DEFINE_THIS_MODULE(vtep);
> -
> -struct vtep_rec_physical_locator_list_entry {
> -    struct ovs_list locators_node;
> -    const struct vteprec_physical_locator *vteprec_ploc;
> -};
> -
> -struct mmr_hash_node_data {
> -    const struct vteprec_mcast_macs_remote *mmr;
> -    struct shash physical_locators;
> -};
> -
> -/*
> - * Scans through the Binding table in ovnsb, and updates the vtep logical
> - * switch tunnel keys and the 'Ucast_Macs_Remote' table in the VTEP
> - * database.
> - *
> - */
> -
> -/* Searches the 'chassis_rec->encaps' for the first vtep tunnel
> - * configuration, returns the 'ip'.  Unless duplicated, the returned
> - * pointer cannot live past current vtep_run() execution. */
> -static const char *
> -get_chassis_vtep_ip(const struct sbrec_chassis *chassis_rec)
> -{
> -    if (chassis_rec) {
> -        size_t i;
> -
> -        for (i = 0; i < chassis_rec->n_encaps; i++) {
> -            if (!strcmp(chassis_rec->encaps[i]->type, "vxlan")) {
> -                return chassis_rec->encaps[i]->ip;
> -            }
> -        }
> -    }
> -
> -    return NULL;
> -}
> -
> -/* Creates a new 'Ucast_Macs_Remote'. */
> -static struct vteprec_ucast_macs_remote *
> -create_umr(struct ovsdb_idl_txn *vtep_idl_txn, const char *mac,
> -           const struct vteprec_logical_switch *vtep_ls)
> -{
> -    struct vteprec_ucast_macs_remote *new_umr =
> -        vteprec_ucast_macs_remote_insert(vtep_idl_txn);
> -
> -    vteprec_ucast_macs_remote_set_MAC(new_umr, mac);
> -    vteprec_ucast_macs_remote_set_logical_switch(new_umr, vtep_ls);
> -
> -    return new_umr;
> -}
> -
> -/* Creates a new 'Physical_Locator'. */
> -static struct vteprec_physical_locator *
> -create_pl(struct ovsdb_idl_txn *vtep_idl_txn, const char *chassis_ip)
> -{
> -    struct vteprec_physical_locator *new_pl =
> -        vteprec_physical_locator_insert(vtep_idl_txn);
> -
> -    vteprec_physical_locator_set_dst_ip(new_pl, chassis_ip);
> -    vteprec_physical_locator_set_encapsulation_type(new_pl,
> VTEP_ENCAP_TYPE);
> -
> -    return new_pl;
> -}
> -
> -/* Creates a new 'Mcast_Macs_Remote'. */
> -static void
> -vtep_create_mmr(struct ovsdb_idl_txn *vtep_idl_txn, const char *mac,
> -                const struct vteprec_logical_switch *vtep_ls,
> -                const struct vteprec_physical_locator_set *ploc_set)
> -{
> -    struct vteprec_mcast_macs_remote *new_mmr =
> -       vteprec_mcast_macs_remote_insert(vtep_idl_txn);
> -
> -    vteprec_mcast_macs_remote_set_MAC(new_mmr, mac);
> -    vteprec_mcast_macs_remote_set_logical_switch(new_mmr, vtep_ls);
> -    vteprec_mcast_macs_remote_set_locator_set(new_mmr, ploc_set);
> -}
> -
> -/* Compares previous and new mmr locator sets and returns true if they
> - * differ and false otherwise. This function also preps a new locator
> - * set for database write.
> - *
> - * 'locators_list' is the new set of locators for the associated
> - * 'Mcast_Macs_Remote' entry passed in and is queried to generate the
> - * new set of locators in vtep database format. */
> -static bool
> -vtep_process_pls(const struct ovs_list *locators_list,
> -                 const struct mmr_hash_node_data *mmr_ext,
> -                 struct vteprec_physical_locator **locators)
> -{
> -    size_t n_locators_prev = 0;
> -    size_t n_locators_new = ovs_list_size(locators_list);
> -    bool locator_lists_differ = false;
> -
> -    if (mmr_ext) {
> -        n_locators_prev = mmr_ext->mmr->locator_set->n_locators;
> -    }
> -    if (n_locators_prev != n_locators_new) {
> -        locator_lists_differ = true;
> -    }
> -
> -    if (n_locators_new) {
> -        int i = 0;
> -        struct vtep_rec_physical_locator_list_entry *ploc_entry;
> -        LIST_FOR_EACH (ploc_entry, locators_node, locators_list) {
> -            locators[i] = (struct vteprec_physical_locator *)
> -                           ploc_entry->vteprec_ploc;
> -            if (mmr_ext && !shash_find_data(&mmr_ext->physical_locators,
> -                                            locators[i]->dst_ip)) {
> -                    locator_lists_differ = true;
> -            }
> -            i++;
> -        }
> -    }
> -
> -    return locator_lists_differ;
> -}
> -
> -/* Creates a new 'Mcast_Macs_Remote' entry if needed and also cleans up
> - * out-dated remote mcast mac entries as needed. */
> -static void
> -vtep_update_mmr(struct ovsdb_idl_txn *vtep_idl_txn,
> -                struct ovs_list *locators_list,
> -                const struct vteprec_logical_switch *vtep_ls,
> -                const struct mmr_hash_node_data *mmr_ext)
> -{
> -    struct vteprec_physical_locator **locators = NULL;
> -    size_t n_locators_new = ovs_list_size(locators_list);
> -    bool mmr_changed;
> -
> -    locators = xmalloc(n_locators_new * sizeof *locators);
> -
> -    mmr_changed = vtep_process_pls(locators_list, mmr_ext, locators);
> -
> -    if (mmr_ext && !n_locators_new) {
> -        vteprec_mcast_macs_remote_delete(mmr_ext->mmr);
> -    } else if ((mmr_ext && mmr_changed) ||
> -               (!mmr_ext && n_locators_new)) {
> -
> -        const struct vteprec_physical_locator_set *ploc_set =
> -            vteprec_physical_locator_set_insert(vtep_idl_txn);
> -
> -        vtep_create_mmr(vtep_idl_txn, "unknown-dst", vtep_ls, ploc_set);
> -
> -        vteprec_physical_locator_set_set_locators(ploc_set, locators,
> -                                                  n_locators_new);
> -    }
> -    free(locators);
> -}
> -
> -/* Updates the vtep Logical_Switch table entries' tunnel keys based
> - * on the port bindings. */
> -static void
> -vtep_lswitch_run(struct shash *vtep_pbs, struct sset *vtep_pswitches,
> -                 struct shash *vtep_lswitches)
> -{
> -    struct sset used_ls = SSET_INITIALIZER(&used_ls);
> -    struct shash_node *node;
> -
> -    /* Collects the logical switch bindings from port binding entries.
> -     * Since the binding module has already guaranteed that each vtep
> -     * logical switch is bound only to one ovn-sb logical datapath,
> -     * we can just iterate and assign tunnel key to vtep logical switch.
> */
> -    SHASH_FOR_EACH (node, vtep_pbs) {
> -        const struct sbrec_port_binding *port_binding_rec = node->data;
> -        const char *pswitch_name = smap_get(&port_binding_rec->options,
> -                                            "vtep-physical-switch");
> -        const char *lswitch_name = smap_get(&port_binding_rec->options,
> -                                            "vtep-logical-switch");
> -        const struct vteprec_logical_switch *vtep_ls;
> -
> -        /* If 'port_binding_rec->chassis' exists then 'pswitch_name'
> -         * and 'lswitch_name' must also exist. */
> -        if (!pswitch_name || !lswitch_name) {
> -            /* This could only happen when someone directly modifies the
> -             * database,  (e.g. using ovn-sbctl). */
> -            VLOG_ERR("logical port (%s) with no 'options:vtep-physical-"
> -                     "switch' or 'options:vtep-logical-switch' specified "
> -                     "is bound to chassis (%s).",
> -                     port_binding_rec->logical_port,
> -                     port_binding_rec->chassis->name);
> -            continue;
> -        }
> -        vtep_ls = shash_find_data(vtep_lswitches, lswitch_name);
> -        /* Also checks 'pswitch_name' since the same 'lswitch_name' could
> -         * exist in multiple vtep database instances and be bound to
> different
> -         * ovn logical networks. */
> -        if (vtep_ls && sset_find(vtep_pswitches, pswitch_name)) {
> -            int64_t tnl_key;
> -
> -            if (sset_find(&used_ls, lswitch_name)) {
> -                continue;
> -            }
> -
> -            tnl_key = port_binding_rec->datapath->tunnel_key;
> -            if (vtep_ls->n_tunnel_key
> -                && vtep_ls->tunnel_key[0] != tnl_key) {
> -                VLOG_DBG("set vtep logical switch (%s) tunnel key from "
> -                         "(%"PRId64") to (%"PRId64")", vtep_ls->name,
> -                         vtep_ls->tunnel_key[0], tnl_key);
> -            }
> -            vteprec_logical_switch_set_tunnel_key(vtep_ls, &tnl_key, 1);
> -
> -            /* OVN is expected to always use source node replication mode,
> -             * hence the replication mode is hard-coded for each logical
> -             * switch in the context of ovn-controller-vtep. */
> -            vteprec_logical_switch_set_replication_mode(vtep_ls,
> "source_node");
> -            sset_add(&used_ls, lswitch_name);
> -        }
> -    }
> -    /* Resets the tunnel keys for unused vtep logical switches. */
> -    SHASH_FOR_EACH (node, vtep_lswitches) {
> -        if (!sset_find(&used_ls, node->name)) {
> -            int64_t tnl_key = 0;
> -            vteprec_logical_switch_set_tunnel_key(node->data, &tnl_key,
> 1);
> -        }
> -    }
> -    sset_destroy(&used_ls);
> -}
> -
> -/* Updates the vtep 'Ucast_Macs_Remote' and 'Mcast_Macs_Remote' tables
> based
> - * on non-vtep port bindings. */
> -static void
> -vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct shash
> *ucast_macs_rmts,
> -              struct shash *mcast_macs_rmts, struct shash
> *physical_locators,
> -              struct shash *vtep_lswitches, struct shash *non_vtep_pbs)
> -{
> -    struct shash_node *node;
> -    struct hmap ls_map;
> -
> -    /* Maps from ovn logical datapath tunnel key (which is also the vtep
> -     * logical switch tunnel key) to the corresponding vtep logical switch
> -     * instance.  Also, the shash map 'added_macs' is used for checking
> -     * duplicated MAC addresses in the same ovn logical datapath.
> 'mmr_ext'
> -     * is used to track mmr info per LS that needs creation/update and
> -     * 'locators_list' collects the new physical locators to be bound for
> -     * an mmr_ext; 'physical_locators' is used to track existing locators
> and
> -     * filter duplicates per logical switch. */
> -    struct ls_hash_node {
> -        struct hmap_node hmap_node;
> -
> -        const struct vteprec_logical_switch *vtep_ls;
> -        struct shash added_macs;
> -
> -        struct ovs_list locators_list;
> -        struct shash physical_locators;
> -        struct mmr_hash_node_data *mmr_ext;
> -    };
> -
> -    hmap_init(&ls_map);
> -    SHASH_FOR_EACH (node, vtep_lswitches) {
> -        const struct vteprec_logical_switch *vtep_ls = node->data;
> -        struct ls_hash_node *ls_node;
> -
> -        if (!vtep_ls->n_tunnel_key) {
> -            continue;
> -        }
> -        ls_node = xmalloc(sizeof *ls_node);
> -        ls_node->vtep_ls = vtep_ls;
> -        shash_init(&ls_node->added_macs);
> -        shash_init(&ls_node->physical_locators);
> -        ovs_list_init(&ls_node->locators_list);
> -        ls_node->mmr_ext = NULL;
> -        hmap_insert(&ls_map, &ls_node->hmap_node,
> -                    hash_uint64((uint64_t) vtep_ls->tunnel_key[0]));
> -    }
> -
> -    SHASH_FOR_EACH (node, non_vtep_pbs) {
> -        const struct sbrec_port_binding *port_binding_rec = node->data;
> -        const struct sbrec_chassis *chassis_rec;
> -        struct ls_hash_node *ls_node;
> -        const char *chassis_ip;
> -        int64_t tnl_key;
> -        size_t i;
> -
> -        chassis_rec = port_binding_rec->chassis;
> -        if (!chassis_rec) {
> -            continue;
> -        }
> -
> -        tnl_key = port_binding_rec->datapath->tunnel_key;
> -        HMAP_FOR_EACH_WITH_HASH (ls_node, hmap_node,
> -                                 hash_uint64((uint64_t) tnl_key),
> -                                 &ls_map) {
> -            if (ls_node->vtep_ls->tunnel_key[0] == tnl_key) {
> -                break;
> -            }
> -        }
> -        /* If 'ls_node' is NULL, that means no vtep logical switch is
> -         * attached to the corresponding ovn logical datapath, so pass.
> -         */
> -        if (!ls_node) {
> -            continue;
> -        }
> -
> -        chassis_ip = get_chassis_vtep_ip(chassis_rec);
> -        /* Unreachable chassis, continue. */
> -        if (!chassis_ip) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
> -            VLOG_INFO_RL(&rl, "VTEP tunnel encap on chassis (%s) not
> found",
> -                         chassis_rec->name);
> -            continue;
> -        }
> -
> -        const struct vteprec_physical_locator *pl =
> -            shash_find_data(physical_locators, chassis_ip);
> -        if (!pl) {
> -            pl = create_pl(vtep_idl_txn, chassis_ip);
> -            shash_add(physical_locators, chassis_ip, pl);
> -        }
> -
> -        const struct vteprec_physical_locator *ls_pl =
> -            shash_find_data(&ls_node->physical_locators, chassis_ip);
> -        if (!ls_pl) {
> -            struct vtep_rec_physical_locator_list_entry *ploc_entry =
> -                xmalloc(sizeof *ploc_entry);
> -            ploc_entry->vteprec_ploc = pl;
> -            ovs_list_push_back(&ls_node->locators_list,
> -                               &ploc_entry->locators_node);
> -            shash_add(&ls_node->physical_locators, chassis_ip, pl);
> -        }
> -
> -        char *mac_tnlkey = xasprintf("%s_%"PRId64, "unknown-dst",
> tnl_key);
> -        ls_node->mmr_ext = shash_find_data(mcast_macs_rmts, mac_tnlkey);
> -
> -        if (ls_node->mmr_ext &&
> -            ls_node->mmr_ext->mmr->logical_switch == ls_node->vtep_ls) {
> -
> -            /* Delete the entry from the hash table so the mmr does not
> get
> -             * removed from the DB later on during stale checking. */
> -            shash_find_and_delete(mcast_macs_rmts, mac_tnlkey);
> -        }
> -        free(mac_tnlkey);
> -
> -        for (i = 0; i < port_binding_rec->n_mac; i++) {
> -            const struct vteprec_ucast_macs_remote *umr;
> -            const struct sbrec_port_binding *conflict;
> -            char *mac = port_binding_rec->mac[i];
> -
> -            /* Checks for duplicate MAC in the same vtep logical switch.
> */
> -            conflict = shash_find_data(&ls_node->added_macs, mac);
> -            if (conflict) {
> -                VLOG_WARN("MAC address (%s) has already been known to be "
> -                          "on logical port (%s) in the same logical "
> -                          "datapath, so just ignore this logical port
> (%s)",
> -                          mac, conflict->logical_port,
> -                          port_binding_rec->logical_port);
> -                continue;
> -            }
> -            shash_add(&ls_node->added_macs, mac, port_binding_rec);
> -
> -            char *mac_ip_tnlkey = xasprintf("%s_%s_%"PRId64, mac,
> chassis_ip,
> -                                            tnl_key);
> -            umr = shash_find_data(ucast_macs_rmts, mac_ip_tnlkey);
> -            /* If finds the 'umr' entry for the mac, ip, and tnl_key,
> deletes
> -             * the entry from shash so that it is not gargage collected.
> -             *
> -             * If not found, creates a new 'umr' entry. */
> -            if (umr && umr->logical_switch == ls_node->vtep_ls) {
> -                shash_find_and_delete(ucast_macs_rmts, mac_ip_tnlkey);
> -            } else {
> -                const struct vteprec_ucast_macs_remote *new_umr;
> -                new_umr = create_umr(vtep_idl_txn, mac, ls_node->vtep_ls);
> -                vteprec_ucast_macs_remote_set_locator(new_umr, pl);
> -            }
> -            free(mac_ip_tnlkey);
> -        }
> -    }
> -
> -    /* Removes all remaining 'umr's, since they do not exist anymore. */
> -    SHASH_FOR_EACH (node, ucast_macs_rmts) {
> -        vteprec_ucast_macs_remote_delete(node->data);
> -    }
> -    struct ls_hash_node *iter, *next;
> -    HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &ls_map) {
> -        struct vtep_rec_physical_locator_list_entry *ploc_entry;
> -        vtep_update_mmr(vtep_idl_txn, &iter->locators_list,
> -                        iter->vtep_ls, iter->mmr_ext);
> -        LIST_FOR_EACH_POP(ploc_entry, locators_node,
> -                          &iter->locators_list) {
> -            free(ploc_entry);
> -        }
> -        hmap_remove(&ls_map, &iter->hmap_node);
> -        shash_destroy(&iter->added_macs);
> -        shash_destroy(&iter->physical_locators);
> -        free(iter);
> -    }
> -    hmap_destroy(&ls_map);
> -
> -    /* Clean stale 'Mcast_Macs_Remote' */
> -    struct mmr_hash_node_data *mmr_ext;
> -    SHASH_FOR_EACH (node, mcast_macs_rmts) {
> -        mmr_ext = node->data;
> -        vteprec_mcast_macs_remote_delete(mmr_ext->mmr);
> -    }
> -}
> -
> -/* Resets all logical switches' 'tunnel_key' to NULL */
> -static bool
> -vtep_lswitch_cleanup(struct ovsdb_idl *vtep_idl)
> -{
> -   const struct vteprec_logical_switch *vtep_ls;
> -    bool done = true;
> -
> -    VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, vtep_idl) {
> -        if (vtep_ls->n_tunnel_key) {
> -            vteprec_logical_switch_set_tunnel_key(vtep_ls, NULL, 0);
> -            done = false;
> -        }
> -    }
> -
> -    return done;
> -}
> -
> -/* Removes all entries in the 'Ucast_Macs_Remote' table in the vtep
> database.
> - * Returns true when all done (i.e. no entry to remove). */
> -static bool
> -vtep_ucast_macs_cleanup(struct ovsdb_idl *vtep_idl)
> -{
> -    const struct vteprec_ucast_macs_remote *umr;
> -
> -    VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, vtep_idl) {
> -        vteprec_ucast_macs_remote_delete(umr);
> -        return false;
> -    }
> -
> -    return true;
> -}
> -
> -/* Removes all entries in the 'Mcast_Macs_Remote' table in vtep database.
> - * Returns true when all done (i.e. no entry to remove). */
> -static bool
> -vtep_mcast_macs_cleanup(struct ovsdb_idl *vtep_idl)
> -{
> -    const struct vteprec_mcast_macs_remote *mmr;
> -
> -    VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mmr, vtep_idl) {
> -        vteprec_mcast_macs_remote_delete(mmr);
> -        return false;
> -    }
> -
> -    return true;
> -}
> -
> -/* Updates vtep logical switch tunnel keys. */
> -void
> -vtep_run(struct controller_vtep_ctx *ctx)
> -{
> -    if (!ctx->vtep_idl_txn) {
> -        return;
> -    }
> -
> -    struct sset vtep_pswitches = SSET_INITIALIZER(&vtep_pswitches);
> -    struct shash vtep_lswitches = SHASH_INITIALIZER(&vtep_lswitches);
> -    struct shash ucast_macs_rmts = SHASH_INITIALIZER(&ucast_macs_rmts);
> -    struct shash mcast_macs_rmts = SHASH_INITIALIZER(&mcast_macs_rmts);
> -    struct shash physical_locators =
> SHASH_INITIALIZER(&physical_locators);
> -    struct shash vtep_pbs = SHASH_INITIALIZER(&vtep_pbs);
> -    struct shash non_vtep_pbs = SHASH_INITIALIZER(&non_vtep_pbs);
> -    const struct vteprec_physical_switch *vtep_ps;
> -    const struct vteprec_logical_switch *vtep_ls;
> -    const struct vteprec_ucast_macs_remote *umr;
> -    const struct sbrec_port_binding *port_binding_rec;
> -    const struct vteprec_mcast_macs_remote *mmr;
> -    struct shash_node *node;
> -
> -    /* Collects 'Physical_Switch's. */
> -    VTEPREC_PHYSICAL_SWITCH_FOR_EACH (vtep_ps, ctx->vtep_idl) {
> -        sset_add(&vtep_pswitches, vtep_ps->name);
> -    }
> -
> -    /* Collects 'Logical_Switch's. */
> -    VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, ctx->vtep_idl) {
> -        shash_add(&vtep_lswitches, vtep_ls->name, vtep_ls);
> -    }
> -
> -    /* Collects 'Ucast_Macs_Remote's. */
> -    VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, ctx->vtep_idl) {
> -        char *mac_ip_tnlkey =
> -            xasprintf("%s_%s_%"PRId64, umr->MAC,
> -                      umr->locator ? umr->locator->dst_ip : "",
> -                      umr->logical_switch &&
> umr->logical_switch->n_tunnel_key
> -                          ? umr->logical_switch->tunnel_key[0] :
> INT64_MAX);
> -
> -        shash_add(&ucast_macs_rmts, mac_ip_tnlkey, umr);
> -        free(mac_ip_tnlkey);
> -    }
> -
> -    /* Collects 'Mcast_Macs_Remote's. */
> -    VTEPREC_MCAST_MACS_REMOTE_FOR_EACH (mmr, ctx->vtep_idl) {
> -        struct mmr_hash_node_data *mmr_ext = xmalloc(sizeof *mmr_ext);;
> -        char *mac_tnlkey =
> -            xasprintf("%s_%"PRId64, mmr->MAC,
> -                      mmr->logical_switch &&
> mmr->logical_switch->n_tunnel_key
> -                          ? mmr->logical_switch->tunnel_key[0] :
> INT64_MAX);
> -
> -        shash_add(&mcast_macs_rmts, mac_tnlkey, mmr_ext);
> -        mmr_ext->mmr = mmr;
> -
> -        shash_init(&mmr_ext->physical_locators);
> -        for (size_t i = 0; i < mmr->locator_set->n_locators; i++) {
> -            shash_add(&mmr_ext->physical_locators,
> -                      mmr->locator_set->locators[i]->dst_ip,
> -                      mmr->locator_set->locators[i]);
> -        }
> -
> -        free(mac_tnlkey);
> -    }
> -
> -    /* Collects 'Physical_Locator's. */
> -    const struct vteprec_physical_locator *pl;
> -    VTEPREC_PHYSICAL_LOCATOR_FOR_EACH (pl, ctx->vtep_idl) {
> -        shash_add(&physical_locators, pl->dst_ip, pl);
> -    }
> -
> -    /* Collects and classifies 'Port_Binding's. */
> -    SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
> -        struct shash *target =
> -            !strcmp(port_binding_rec->type, "vtep") ? &vtep_pbs :
> &non_vtep_pbs;
> -
> -        if (!port_binding_rec->chassis) {
> -            continue;
> -        }
> -        shash_add(target, port_binding_rec->logical_port,
> port_binding_rec);
> -    }
> -
> -    ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn,
> -                              "ovn-controller-vtep: update logical switch
> "
> -                              "tunnel keys and 'ucast_macs_remote's");
> -
> -    vtep_lswitch_run(&vtep_pbs, &vtep_pswitches, &vtep_lswitches);
> -    vtep_macs_run(ctx->vtep_idl_txn, &ucast_macs_rmts,
> -                  &mcast_macs_rmts, &physical_locators,
> -                  &vtep_lswitches, &non_vtep_pbs);
> -
> -    sset_destroy(&vtep_pswitches);
> -    shash_destroy(&vtep_lswitches);
> -    shash_destroy(&ucast_macs_rmts);
> -    SHASH_FOR_EACH (node, &mcast_macs_rmts) {
> -        struct mmr_hash_node_data *mmr_ext = node->data;
> -        shash_destroy(&mmr_ext->physical_locators);
> -        free(mmr_ext);
> -    }
> -    shash_destroy(&mcast_macs_rmts);
> -    shash_destroy(&physical_locators);
> -    shash_destroy(&vtep_pbs);
> -    shash_destroy(&non_vtep_pbs);
> -}
> -
> -/* Cleans up all related entries in vtep.  Returns true when done (i.e.
> there
> - * is no change made to 'ctx->vtep_idl'), otherwise returns false. */
> -bool
> -vtep_cleanup(struct controller_vtep_ctx *ctx)
> -{
> -    if (!ctx->vtep_idl_txn) {
> -        return false;
> -    }
> -
> -    bool all_done;
> -
> -    ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn,
> -                              "ovn-controller-vtep: cleaning up vtep "
> -                              "configuration");
> -    all_done = vtep_lswitch_cleanup(ctx->vtep_idl);
> -    all_done = vtep_ucast_macs_cleanup(ctx->vtep_idl) && all_done;
> -    all_done = vtep_mcast_macs_cleanup(ctx->vtep_idl) && all_done;
> -
> -    return all_done;
> -}
> diff --git a/ovn/controller-vtep/vtep.h b/ovn/controller-vtep/vtep.h
> deleted file mode 100644
> index 97c87b7a7..000000000
> --- a/ovn/controller-vtep/vtep.h
> +++ /dev/null
> @@ -1,27 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -
> -#ifndef OVN_VTEP_H
> -#define OVN_VTEP_H 1
> -
> -#include <stdbool.h>
> -
> -struct controller_vtep_ctx;
> -
> -void vtep_run(struct controller_vtep_ctx *);
> -bool vtep_cleanup(struct controller_vtep_ctx *);
> -
> -#endif /* ovn/controller-vtep/vtep.h */
> diff --git a/ovn/controller/.gitignore b/ovn/controller/.gitignore
> deleted file mode 100644
> index 4199a3741..000000000
> --- a/ovn/controller/.gitignore
> +++ /dev/null
> @@ -1,2 +0,0 @@
> -/ovn-controller
> -/ovn-controller.8
> diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
> deleted file mode 100644
> index 193ea690b..000000000
> --- a/ovn/controller/automake.mk
> +++ /dev/null
> @@ -1,32 +0,0 @@
> -bin_PROGRAMS += ovn/controller/ovn-controller
> -ovn_controller_ovn_controller_SOURCES = \
> -       ovn/controller/bfd.c \
> -       ovn/controller/bfd.h \
> -       ovn/controller/binding.c \
> -       ovn/controller/binding.h \
> -       ovn/controller/chassis.c \
> -       ovn/controller/chassis.h \
> -       ovn/controller/encaps.c \
> -       ovn/controller/encaps.h \
> -       ovn/controller/ha-chassis.c \
> -       ovn/controller/ha-chassis.h \
> -       ovn/controller/ip-mcast.c \
> -       ovn/controller/ip-mcast.h \
> -       ovn/controller/lflow.c \
> -       ovn/controller/lflow.h \
> -       ovn/controller/lport.c \
> -       ovn/controller/lport.h \
> -       ovn/controller/ofctrl.c \
> -       ovn/controller/ofctrl.h \
> -       ovn/controller/pinctrl.c \
> -       ovn/controller/pinctrl.h \
> -       ovn/controller/patch.c \
> -       ovn/controller/patch.h \
> -       ovn/controller/ovn-controller.c \
> -       ovn/controller/ovn-controller.h \
> -       ovn/controller/physical.c \
> -       ovn/controller/physical.h
> -ovn_controller_ovn_controller_LDADD = ovn/lib/libovn.la lib/
> libopenvswitch.la
> -man_MANS += ovn/controller/ovn-controller.8
> -EXTRA_DIST += ovn/controller/ovn-controller.8.xml
> -CLEANFILES += ovn/controller/ovn-controller.8
> diff --git a/ovn/controller/bfd.c b/ovn/controller/bfd.c
> deleted file mode 100644
> index 22db00af7..000000000
> --- a/ovn/controller/bfd.c
> +++ /dev/null
> @@ -1,268 +0,0 @@
> -/* Copyright (c) 2017 Red Hat, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "bfd.h"
> -#include "encaps.h"
> -#include "lport.h"
> -#include "ovn-controller.h"
> -
> -#include "lib/hash.h"
> -#include "lib/sset.h"
> -#include "lib/util.h"
> -#include "lib/vswitch-idl.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn-controller.h"
> -
> -VLOG_DEFINE_THIS_MODULE(ovn_bfd);
> -
> -void
> -bfd_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> -{
> -    /* NOTE: this assumes that binding.c has added the
> -     * ovsrec_interface table */
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
> -}
> -
> -void
> -bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
> -                             struct sset *active_tunnels)
> -{
> -    int i;
> -
> -    if (!br_int) {
> -        /* Nothing to do if integration bridge doesn't exist. */
> -        return;
> -    }
> -
> -    for (i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> -
> -        if (!strcmp(port_rec->name, br_int->name)) {
> -            continue;
> -        }
> -
> -        int j;
> -        for (j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec;
> -            iface_rec = port_rec->interfaces[j];
> -
> -            /* Check if this is a tunnel interface. */
> -            if (smap_get(&iface_rec->options, "remote_ip")) {
> -                /* Add ovn-chassis-id if the bfd_status of the tunnel
> -                 * is active */
> -                const char *bfd = smap_get(&iface_rec->bfd, "enable");
> -                if (bfd && !strcmp(bfd, "true")) {
> -                    const char *status = smap_get(&iface_rec->bfd_status,
> -                                                  "state");
> -                    if (status && !strcmp(status, "up")) {
> -                        const char *id = smap_get(&port_rec->external_ids,
> -                                                  "ovn-chassis-id");
> -                        if (id) {
> -                            char *chassis_name = NULL;
> -
> -                            if (encaps_tunnel_id_parse(id, &chassis_name,
> -                                                       NULL)) {
> -                                if (!sset_contains(active_tunnels,
> -                                                   chassis_name)) {
> -                                    sset_add(active_tunnels,
> chassis_name);
> -                                }
> -                                free(chassis_name);
> -                            }
> -                        }
> -                    }
> -                }
> -            }
> -        }
> -    }
> -}
> -
> -/* Loops through the HA chassis groups in the SB DB and returns
> - * the set of chassis which the call can establish the BFD sessions
> - * with.
> - * Eg.
> - * If there are 2 HA chassis groups.
> - * Group name - hapgrp1
> - *   - HA chassis - (HA1, HA2, HA3)
> - *   - ref chassis - (C1, C2)
> - *
> - * Group name - hapgrp2
> - *   - HA chassis - (HA1, HA4, HA5)
> - *   - ref chassis - (C1, C3, C4)
> - *
> - * If 'our_chassis' is HA1 then this function returns
> - *  bfd chassis set - (HA2, HA3, HA4 HA5, C1, C2, C3, C4)
> - *
> - * If 'our_chassis' is C1 then this function returns
> - *  bfd chassis set - (HA1, HA2, HA3, HA4, HA5)
> - *
> - * If 'our_chassis' is HA5 then this function returns
> - *  bfd chassis set - (HA1, HA4, C1, C3, C4)
> - *
> - * If 'our_chassis' is C2 then this function returns
> - *  bfd chassis set - (HA1, HA2, HA3)
> - *
> - * If 'our_chassis' is C5 then this function returns empty bfd set.
> - */
> -static void
> -bfd_calculate_chassis(
> -    const struct sbrec_chassis *our_chassis,
> -    const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
> -    struct sset *bfd_chassis)
> -{
> -    const struct sbrec_ha_chassis_group *ha_chassis_grp;
> -    SBREC_HA_CHASSIS_GROUP_TABLE_FOR_EACH (ha_chassis_grp,
> -                                           ha_chassis_grp_table) {
> -        bool is_ha_chassis = false;
> -        struct sset grp_chassis = SSET_INITIALIZER(&grp_chassis);
> -        const struct sbrec_ha_chassis *ha_ch;
> -        bool bfd_setup_required = false;
> -        if (ha_chassis_grp->n_ha_chassis < 2) {
> -            /* No need to consider the chassis group for BFD if
> -             * there is  1 or no chassis in it. */
> -            continue;
> -        }
> -        for (size_t i = 0; i < ha_chassis_grp->n_ha_chassis; i++) {
> -            ha_ch = ha_chassis_grp->ha_chassis[i];
> -            if (!ha_ch->chassis) {
> -                continue;
> -            }
> -            sset_add(&grp_chassis, ha_ch->chassis->name);
> -            if (our_chassis == ha_ch->chassis) {
> -                is_ha_chassis = true;
> -                bfd_setup_required = true;
> -            }
> -        }
> -
> -        if (is_ha_chassis) {
> -            /* It's an HA chassis. So add the ref_chassis to the bfd set.
> */
> -            for (size_t i = 0; i < ha_chassis_grp->n_ref_chassis; i++) {
> -                sset_add(&grp_chassis,
> ha_chassis_grp->ref_chassis[i]->name);
> -            }
> -        } else {
> -            /* This is not an HA chassis. Check if this chassis is present
> -             * in the ref_chassis list. If so add the ha_chassis to the
> -             * sset .*/
> -            for (size_t i = 0; i < ha_chassis_grp->n_ref_chassis; i++) {
> -                if (our_chassis == ha_chassis_grp->ref_chassis[i]) {
> -                    bfd_setup_required = true;
> -                    break;
> -                }
> -            }
> -        }
> -
> -        if (bfd_setup_required) {
> -            const char *name;
> -            SSET_FOR_EACH (name, &grp_chassis) {
> -                sset_add(bfd_chassis, name);
> -            }
> -        }
> -        sset_destroy(&grp_chassis);
> -    }
> -}
> -
> -void
> -bfd_run(const struct ovsrec_interface_table *interface_table,
> -        const struct ovsrec_bridge *br_int,
> -        const struct sbrec_chassis *chassis_rec,
> -        const struct sbrec_ha_chassis_group_table *ha_chassis_grp_table,
> -        const struct sbrec_sb_global_table *sb_global_table)
> -{
> -    if (!chassis_rec) {
> -        return;
> -    }
> -    struct sset bfd_chassis = SSET_INITIALIZER(&bfd_chassis);
> -    bfd_calculate_chassis(chassis_rec, ha_chassis_grp_table,
> -                          &bfd_chassis);
> -
> -    /* Identify tunnels ports(connected to remote chassis id) to enable
> bfd */
> -    struct sset tunnels = SSET_INITIALIZER(&tunnels);
> -    struct sset bfd_ifaces = SSET_INITIALIZER(&bfd_ifaces);
> -    for (size_t k = 0; k < br_int->n_ports; k++) {
> -        const char *tunnel_id = smap_get(&br_int->ports[k]->external_ids,
> -                                          "ovn-chassis-id");
> -        if (tunnel_id) {
> -            char *chassis_name = NULL;
> -            char *port_name = br_int->ports[k]->name;
> -
> -            sset_add(&tunnels, port_name);
> -
> -            if (encaps_tunnel_id_parse(tunnel_id, &chassis_name, NULL)) {
> -                if (sset_contains(&bfd_chassis, chassis_name)) {
> -                    sset_add(&bfd_ifaces, port_name);
> -                }
> -                free(chassis_name);
> -            }
> -        }
> -    }
> -
> -    const struct sbrec_sb_global *sb
> -        = sbrec_sb_global_table_first(sb_global_table);
> -    struct smap bfd = SMAP_INITIALIZER(&bfd);
> -    smap_add(&bfd, "enable", "true");
> -
> -    if (sb) {
> -        const char *min_rx = smap_get(&sb->options, "bfd-min-rx");
> -        const char *decay_min_rx = smap_get(&sb->options,
> "bfd-decay-min-rx");
> -        const char *min_tx = smap_get(&sb->options, "bfd-min-tx");
> -        const char *mult = smap_get(&sb->options, "bfd-mult");
> -        if (min_rx) {
> -            smap_add(&bfd, "min_rx", min_rx);
> -        }
> -        if (decay_min_rx) {
> -            smap_add(&bfd, "decay_min_rx", decay_min_rx);
> -        }
> -        if (min_tx) {
> -            smap_add(&bfd, "min_tx", min_tx);
> -        }
> -        if (mult) {
> -            smap_add(&bfd, "mult", mult);
> -        }
> -    }
> -
> -    /* Enable or disable bfd */
> -    const struct ovsrec_interface *iface;
> -    OVSREC_INTERFACE_TABLE_FOR_EACH (iface, interface_table) {
> -        if (sset_contains(&tunnels, iface->name)) {
> -            if (sset_contains(&bfd_ifaces, iface->name)) {
> -                /* We need to enable BFD for this interface. Configure the
> -                 * BFD params if
> -                 *  - If BFD was disabled earlier
> -                 *  - Or if CMS has updated BFD config options.
> -                 */
> -                if (!smap_equal(&iface->bfd, &bfd)) {
> -                    ovsrec_interface_verify_bfd(iface);
> -                    ovsrec_interface_set_bfd(iface, &bfd);
> -                    VLOG_INFO("Enabled BFD on interface %s", iface->name);
> -                }
> -            } else {
> -                /* We need to disable BFD for this interface if it was
> enabled
> -                 * earlier. */
> -                if (smap_count(&iface->bfd)) {
> -                    ovsrec_interface_verify_bfd(iface);
> -                    ovsrec_interface_set_bfd(iface, NULL);
> -                    VLOG_INFO("Disabled BFD on interface %s",
> iface->name);
> -                }
> -            }
> -        }
> -    }
> -
> -    smap_destroy(&bfd);
> -    sset_destroy(&tunnels);
> -    sset_destroy(&bfd_ifaces);
> -    sset_destroy(&bfd_chassis);
> -}
> diff --git a/ovn/controller/bfd.h b/ovn/controller/bfd.h
> deleted file mode 100644
> index 17fab5323..000000000
> --- a/ovn/controller/bfd.h
> +++ /dev/null
> @@ -1,41 +0,0 @@
> -/* Copyright (c) 2017 Red Hat, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_BFD_H
> -#define OVN_BFD_H 1
> -
> -struct hmap;
> -struct ovsdb_idl;
> -struct ovsdb_idl_index;
> -struct ovsrec_bridge;
> -struct ovsrec_interface_table;
> -struct ovsrec_open_vswitch_table;
> -struct sbrec_chassis;
> -struct sbrec_sb_global_table;
> -struct sbrec_ha_chassis_group_table;
> -struct sset;
> -
> -void bfd_register_ovs_idl(struct ovsdb_idl *);
> -
> -void bfd_run(const struct ovsrec_interface_table *,
> -             const struct ovsrec_bridge *,
> -             const struct sbrec_chassis *,
> -             const struct sbrec_ha_chassis_group_table *,
> -             const struct sbrec_sb_global_table *);
> -
> -void  bfd_calculate_active_tunnels(const struct ovsrec_bridge *br_int,
> -                                   struct sset *active_tunnels);
> -
> -#endif
> diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
> deleted file mode 100644
> index ace0f811b..000000000
> --- a/ovn/controller/binding.c
> +++ /dev/null
> @@ -1,764 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "binding.h"
> -#include "ha-chassis.h"
> -#include "lflow.h"
> -#include "lport.h"
> -
> -#include "lib/bitmap.h"
> -#include "openvswitch/poll-loop.h"
> -#include "lib/sset.h"
> -#include "lib/util.h"
> -#include "lib/netdev.h"
> -#include "lib/vswitch-idl.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/chassis-index.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn-controller.h"
> -
> -VLOG_DEFINE_THIS_MODULE(binding);
> -
> -#define OVN_QOS_TYPE "linux-htb"
> -
> -struct qos_queue {
> -    struct hmap_node node;
> -    uint32_t queue_id;
> -    uint32_t max_rate;
> -    uint32_t burst;
> -};
> -
> -void
> -binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> -{
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_qos);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl,
> &ovsrec_interface_col_external_ids);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_status);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
> -}
> -
> -static void
> -get_local_iface_ids(const struct ovsrec_bridge *br_int,
> -                    struct shash *lport_to_iface,
> -                    struct sset *local_lports,
> -                    struct sset *egress_ifaces)
> -{
> -    int i;
> -
> -    for (i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> -        const char *iface_id;
> -        int j;
> -
> -        if (!strcmp(port_rec->name, br_int->name)) {
> -            continue;
> -        }
> -
> -        for (j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec;
> -
> -            iface_rec = port_rec->interfaces[j];
> -            iface_id = smap_get(&iface_rec->external_ids, "iface-id");
> -            int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0;
> -
> -            if (iface_id && ofport > 0) {
> -                shash_add(lport_to_iface, iface_id, iface_rec);
> -                sset_add(local_lports, iface_id);
> -            }
> -
> -            /* Check if this is a tunnel interface. */
> -            if (smap_get(&iface_rec->options, "remote_ip")) {
> -                const char *tunnel_iface
> -                    = smap_get(&iface_rec->status, "tunnel_egress_iface");
> -                if (tunnel_iface) {
> -                    sset_add(egress_ifaces, tunnel_iface);
> -                }
> -            }
> -        }
> -    }
> -}
> -
> -static void
> -add_local_datapath__(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> -                     struct ovsdb_idl_index
> *sbrec_port_binding_by_datapath,
> -                     struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                     const struct sbrec_datapath_binding *datapath,
> -                     bool has_local_l3gateway, int depth,
> -                     struct hmap *local_datapaths)
> -{
> -    uint32_t dp_key = datapath->tunnel_key;
> -    struct local_datapath *ld = get_local_datapath(local_datapaths,
> dp_key);
> -    if (ld) {
> -        if (has_local_l3gateway) {
> -            ld->has_local_l3gateway = true;
> -        }
> -        return;
> -    }
> -
> -    ld = xzalloc(sizeof *ld);
> -    hmap_insert(local_datapaths, &ld->hmap_node, dp_key);
> -    ld->datapath = datapath;
> -    ld->localnet_port = NULL;
> -    ld->has_local_l3gateway = has_local_l3gateway;
> -
> -    if (depth >= 100) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        VLOG_WARN_RL(&rl, "datapaths nested too deep");
> -        return;
> -    }
> -
> -    struct sbrec_port_binding *target =
> -        sbrec_port_binding_index_init_row(sbrec_port_binding_by_datapath);
> -    sbrec_port_binding_index_set_datapath(target, datapath);
> -
> -    const struct sbrec_port_binding *pb;
> -    SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
> -                                       sbrec_port_binding_by_datapath) {
> -        if (!strcmp(pb->type, "patch")) {
> -            const char *peer_name = smap_get(&pb->options, "peer");
> -            if (peer_name) {
> -                const struct sbrec_port_binding *peer;
> -
> -                peer = lport_lookup_by_name(sbrec_port_binding_by_name,
> -                                            peer_name);
> -
> -                if (peer && peer->datapath) {
> -                    add_local_datapath__(sbrec_datapath_binding_by_key,
> -                                         sbrec_port_binding_by_datapath,
> -                                         sbrec_port_binding_by_name,
> -                                         peer->datapath, false,
> -                                         depth + 1, local_datapaths);
> -                    ld->n_peer_ports++;
> -                    ld->peer_ports = xrealloc(ld->peer_ports,
> -                                              ld->n_peer_ports *
> -                                              sizeof *ld->peer_ports);
> -                    ld->peer_ports[ld->n_peer_ports - 1] = peer;
> -                }
> -            }
> -        }
> -    }
> -    sbrec_port_binding_index_destroy_row(target);
> -}
> -
> -static void
> -add_local_datapath(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                   struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                   struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                   const struct sbrec_datapath_binding *datapath,
> -                   bool has_local_l3gateway, struct hmap *local_datapaths)
> -{
> -    add_local_datapath__(sbrec_datapath_binding_by_key,
> -                         sbrec_port_binding_by_datapath,
> -                         sbrec_port_binding_by_name,
> -                         datapath, has_local_l3gateway, 0,
> local_datapaths);
> -}
> -
> -static void
> -get_qos_params(const struct sbrec_port_binding *pb, struct hmap
> *queue_map)
> -{
> -    uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0);
> -    uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0);
> -    uint32_t queue_id = smap_get_int(&pb->options, "qdisc_queue_id", 0);
> -
> -    if ((!max_rate && !burst) || !queue_id) {
> -        /* Qos is not configured for this port. */
> -        return;
> -    }
> -
> -    struct qos_queue *node = xzalloc(sizeof *node);
> -    hmap_insert(queue_map, &node->node, hash_int(queue_id, 0));
> -    node->max_rate = max_rate;
> -    node->burst = burst;
> -    node->queue_id = queue_id;
> -}
> -
> -static const struct ovsrec_qos *
> -get_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
> -             const struct ovsrec_qos_table *qos_table)
> -{
> -    const struct ovsrec_qos *qos;
> -    OVSREC_QOS_TABLE_FOR_EACH (qos, qos_table) {
> -        if (!strcmp(qos->type, "linux-noop")) {
> -            return qos;
> -        }
> -    }
> -
> -    if (!ovs_idl_txn) {
> -        return NULL;
> -    }
> -    qos = ovsrec_qos_insert(ovs_idl_txn);
> -    ovsrec_qos_set_type(qos, "linux-noop");
> -    return qos;
> -}
> -
> -static bool
> -set_noop_qos(struct ovsdb_idl_txn *ovs_idl_txn,
> -             const struct ovsrec_port_table *port_table,
> -             const struct ovsrec_qos_table *qos_table,
> -             struct sset *egress_ifaces)
> -{
> -    if (!ovs_idl_txn) {
> -        return false;
> -    }
> -
> -    const struct ovsrec_qos *noop_qos = get_noop_qos(ovs_idl_txn,
> qos_table);
> -    if (!noop_qos) {
> -        return false;
> -    }
> -
> -    const struct ovsrec_port *port;
> -    size_t count = 0;
> -
> -    OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
> -        if (sset_contains(egress_ifaces, port->name)) {
> -            ovsrec_port_set_qos(port, noop_qos);
> -            count++;
> -        }
> -        if (sset_count(egress_ifaces) == count) {
> -            break;
> -        }
> -    }
> -    return true;
> -}
> -
> -static void
> -set_qos_type(struct netdev *netdev, const char *type)
> -{
> -    int error = netdev_set_qos(netdev, type, NULL);
> -    if (error) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        VLOG_WARN_RL(&rl, "%s: could not set qdisc type \"%s\" (%s)",
> -                     netdev_get_name(netdev), type, ovs_strerror(error));
> -    }
> -}
> -
> -static void
> -setup_qos(const char *egress_iface, struct hmap *queue_map)
> -{
> -    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> -    struct netdev *netdev_phy;
> -
> -    if (!egress_iface) {
> -        /* Queues cannot be configured. */
> -        return;
> -    }
> -
> -    int error = netdev_open(egress_iface, NULL, &netdev_phy);
> -    if (error) {
> -        VLOG_WARN_RL(&rl, "%s: could not open netdev (%s)",
> -                     egress_iface, ovs_strerror(error));
> -        return;
> -    }
> -
> -    /* Check current qdisc. */
> -    const char *qdisc_type;
> -    struct smap qdisc_details;
> -
> -    smap_init(&qdisc_details);
> -    if (netdev_get_qos(netdev_phy, &qdisc_type, &qdisc_details) != 0 ||
> -        qdisc_type[0] == '\0') {
> -        smap_destroy(&qdisc_details);
> -        netdev_close(netdev_phy);
> -        /* Qos is not supported. */
> -        return;
> -    }
> -    smap_destroy(&qdisc_details);
> -
> -    /* If we're not actually being requested to do any QoS:
> -     *
> -     *     - If the current qdisc type is OVN_QOS_TYPE, then we clear the
> qdisc
> -     *       type to "".  Otherwise, it's possible that our own leftover
> qdisc
> -     *       settings could cause strange behavior on egress.  Also, QoS
> is
> -     *       expensive and may waste CPU time even if it's not really in
> use.
> -     *
> -     *       OVN isn't the only software that can configure qdiscs, and
> -     *       physical interfaces are shared resources, so there is some
> risk in
> -     *       this strategy: we could disrupt some other program's QoS.
> -     *       Probably, to entirely avoid this possibility we would need
> to add
> -     *       a configuration setting.
> -     *
> -     *     - Otherwise leave the qdisc alone. */
> -    if (hmap_is_empty(queue_map)) {
> -        if (!strcmp(qdisc_type, OVN_QOS_TYPE)) {
> -            set_qos_type(netdev_phy, "");
> -        }
> -        netdev_close(netdev_phy);
> -        return;
> -    }
> -
> -    /* Configure qdisc. */
> -    if (strcmp(qdisc_type, OVN_QOS_TYPE)) {
> -        set_qos_type(netdev_phy, OVN_QOS_TYPE);
> -    }
> -
> -    /* Check and delete if needed. */
> -    struct netdev_queue_dump dump;
> -    unsigned int queue_id;
> -    struct smap queue_details;
> -    struct qos_queue *sb_info;
> -    struct hmap consistent_queues;
> -
> -    smap_init(&queue_details);
> -    hmap_init(&consistent_queues);
> -    NETDEV_QUEUE_FOR_EACH (&queue_id, &queue_details, &dump, netdev_phy) {
> -        bool is_queue_needed = false;
> -
> -        HMAP_FOR_EACH_WITH_HASH (sb_info, node, hash_int(queue_id, 0),
> -                                 queue_map) {
> -            is_queue_needed = true;
> -            if (sb_info->max_rate ==
> -                smap_get_int(&queue_details, "max-rate", 0)
> -                && sb_info->burst == smap_get_int(&queue_details,
> "burst", 0)) {
> -                /* This queue is consistent. */
> -                hmap_insert(&consistent_queues, &sb_info->node,
> -                            hash_int(queue_id, 0));
> -                break;
> -            }
> -        }
> -
> -        if (!is_queue_needed) {
> -            error = netdev_delete_queue(netdev_phy, queue_id);
> -            if (error) {
> -                VLOG_WARN_RL(&rl, "%s: could not delete queue %u (%s)",
> -                             egress_iface, queue_id, ovs_strerror(error));
> -            }
> -        }
> -    }
> -
> -    /* Create/Update queues. */
> -    HMAP_FOR_EACH (sb_info, node, queue_map) {
> -        if (hmap_contains(&consistent_queues, &sb_info->node)) {
> -            hmap_remove(&consistent_queues, &sb_info->node);
> -            continue;
> -        }
> -
> -        smap_clear(&queue_details);
> -        smap_add_format(&queue_details, "max-rate", "%d",
> sb_info->max_rate);
> -        smap_add_format(&queue_details, "burst", "%d", sb_info->burst);
> -        error = netdev_set_queue(netdev_phy, sb_info->queue_id,
> -                                 &queue_details);
> -        if (error) {
> -            VLOG_WARN_RL(&rl, "%s: could not configure queue %u (%s)",
> -                         egress_iface, sb_info->queue_id,
> ovs_strerror(error));
> -        }
> -    }
> -    smap_destroy(&queue_details);
> -    hmap_destroy(&consistent_queues);
> -    netdev_close(netdev_phy);
> -}
> -
> -static void
> -update_local_lport_ids(struct sset *local_lport_ids,
> -                       const struct sbrec_port_binding *binding_rec)
> -{
> -        char buf[16];
> -        snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64,
> -                 binding_rec->datapath->tunnel_key,
> -                 binding_rec->tunnel_key);
> -        sset_add(local_lport_ids, buf);
> -}
> -
> -/*
> - * Get the encap from the chassis for this port. The interface
> - * may have an external_ids:encap-ip=<encap-ip> set; if so we
> - * get the corresponding encap from the chassis.
> - * If "encap-ip" external-ids is not set, we'll not bind the port
> - * to any specific encap rec. and we'll pick up a tunnel port based on
> - * the chassis name alone for the port.
> - */
> -static struct sbrec_encap *
> -sbrec_get_port_encap(const struct sbrec_chassis *chassis_rec,
> -                     const struct ovsrec_interface *iface_rec)
> -{
> -
> -    if (!iface_rec) {
> -        return NULL;
> -    }
> -
> -    const char *encap_ip = smap_get(&iface_rec->external_ids, "encap-ip");
> -    if (!encap_ip) {
> -        return NULL;
> -    }
> -
> -    struct sbrec_encap *best_encap = NULL;
> -    uint32_t best_type = 0;
> -    for (int i = 0; i < chassis_rec->n_encaps; i++) {
> -        if (!strcmp(chassis_rec->encaps[i]->ip, encap_ip)) {
> -            uint32_t tun_type =
> get_tunnel_type(chassis_rec->encaps[i]->type);
> -            if (tun_type > best_type) {
> -                best_type = tun_type;
> -                best_encap = chassis_rec->encaps[i];
> -            }
> -        }
> -    }
> -    return best_encap;
> -}
> -
> -static bool
> -is_our_chassis(const struct sbrec_chassis *chassis_rec,
> -               const struct sbrec_port_binding *binding_rec,
> -               const struct sset *active_tunnels,
> -               const struct shash *lport_to_iface,
> -               const struct sset *local_lports)
> -{
> -    const struct ovsrec_interface *iface_rec
> -        = shash_find_data(lport_to_iface, binding_rec->logical_port);
> -
> -    bool our_chassis = false;
> -    if (iface_rec
> -        || (binding_rec->parent_port && binding_rec->parent_port[0] &&
> -            sset_contains(local_lports, binding_rec->parent_port))) {
> -        /* This port is in our chassis unless it is a localport. */
> -        our_chassis = strcmp(binding_rec->type, "localport");
> -    } else if (!strcmp(binding_rec->type, "l2gateway")) {
> -        const char *chassis_id = smap_get(&binding_rec->options,
> -                                          "l2gateway-chassis");
> -        our_chassis = chassis_id && !strcmp(chassis_id,
> chassis_rec->name);
> -    } else if (!strcmp(binding_rec->type, "chassisredirect") ||
> -               !strcmp(binding_rec->type, "external")) {
> -        our_chassis =
> ha_chassis_group_contains(binding_rec->ha_chassis_group,
> -                                                chassis_rec) &&
> -
> ha_chassis_group_is_active(binding_rec->ha_chassis_group,
> -                                                 active_tunnels,
> chassis_rec);
> -    } else if (!strcmp(binding_rec->type, "l3gateway")) {
> -        const char *chassis_id = smap_get(&binding_rec->options,
> -                                          "l3gateway-chassis");
> -        our_chassis = chassis_id && !strcmp(chassis_id,
> chassis_rec->name);
> -    }
> -
> -    return our_chassis;
> -}
> -
> -static void
> -consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                        struct ovsdb_idl_txn *ovs_idl_txn,
> -                        struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> -                        struct ovsdb_idl_index
> *sbrec_port_binding_by_datapath,
> -                        struct ovsdb_idl_index
> *sbrec_port_binding_by_name,
> -                        const struct sset *active_tunnels,
> -                        const struct sbrec_chassis *chassis_rec,
> -                        const struct sbrec_port_binding *binding_rec,
> -                        struct hmap *qos_map,
> -                        struct hmap *local_datapaths,
> -                        struct shash *lport_to_iface,
> -                        struct sset *local_lports,
> -                        struct sset *local_lport_ids)
> -{
> -    const struct ovsrec_interface *iface_rec
> -        = shash_find_data(lport_to_iface, binding_rec->logical_port);
> -
> -    bool our_chassis = is_our_chassis(chassis_rec, binding_rec,
> active_tunnels,
> -                                      lport_to_iface, local_lports);
> -    if (iface_rec
> -        || (binding_rec->parent_port && binding_rec->parent_port[0] &&
> -            sset_contains(local_lports, binding_rec->parent_port))) {
> -        if (binding_rec->parent_port && binding_rec->parent_port[0]) {
> -            /* Add child logical port to the set of all local ports. */
> -            sset_add(local_lports, binding_rec->logical_port);
> -        }
> -        add_local_datapath(sbrec_datapath_binding_by_key,
> -                           sbrec_port_binding_by_datapath,
> -                           sbrec_port_binding_by_name,
> -                           binding_rec->datapath, false, local_datapaths);
> -        if (iface_rec && qos_map && ovs_idl_txn) {
> -            get_qos_params(binding_rec, qos_map);
> -        }
> -    } else if (!strcmp(binding_rec->type, "l2gateway")) {
> -        if (our_chassis) {
> -            sset_add(local_lports, binding_rec->logical_port);
> -            add_local_datapath(sbrec_datapath_binding_by_key,
> -                               sbrec_port_binding_by_datapath,
> -                               sbrec_port_binding_by_name,
> -                               binding_rec->datapath, false,
> local_datapaths);
> -        }
> -    } else if (!strcmp(binding_rec->type, "chassisredirect")) {
> -        if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
> -                                      chassis_rec)) {
> -            add_local_datapath(sbrec_datapath_binding_by_key,
> -                               sbrec_port_binding_by_datapath,
> -                               sbrec_port_binding_by_name,
> -                               binding_rec->datapath, false,
> local_datapaths);
> -        }
> -    } else if (!strcmp(binding_rec->type, "l3gateway")) {
> -        if (our_chassis) {
> -            add_local_datapath(sbrec_datapath_binding_by_key,
> -                               sbrec_port_binding_by_datapath,
> -                               sbrec_port_binding_by_name,
> -                               binding_rec->datapath, true,
> local_datapaths);
> -        }
> -    } else if (!strcmp(binding_rec->type, "localnet")) {
> -        /* Add all localnet ports to local_lports so that we allocate ct
> zones
> -         * for them. */
> -        sset_add(local_lports, binding_rec->logical_port);
> -    } else if (!strcmp(binding_rec->type, "external")) {
> -        if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
> -                                      chassis_rec)) {
> -            add_local_datapath(sbrec_datapath_binding_by_key,
> -                               sbrec_port_binding_by_datapath,
> -                               sbrec_port_binding_by_name,
> -                               binding_rec->datapath, false,
> local_datapaths);
> -        }
> -    }
> -
> -    if (our_chassis
> -        || !strcmp(binding_rec->type, "patch")
> -        || !strcmp(binding_rec->type, "localport")
> -        || !strcmp(binding_rec->type, "vtep")
> -        || !strcmp(binding_rec->type, "localnet")) {
> -        update_local_lport_ids(local_lport_ids, binding_rec);
> -    }
> -
> -    ovs_assert(ovnsb_idl_txn);
> -    if (ovnsb_idl_txn) {
> -        const char *vif_chassis = smap_get(&binding_rec->options,
> -                                           "requested-chassis");
> -        bool can_bind = !vif_chassis || !vif_chassis[0]
> -                        || !strcmp(vif_chassis, chassis_rec->name)
> -                        || !strcmp(vif_chassis, chassis_rec->hostname);
> -
> -        if (can_bind && our_chassis) {
> -            if (binding_rec->chassis != chassis_rec) {
> -                if (binding_rec->chassis) {
> -                    VLOG_INFO("Changing chassis for lport %s from %s to
> %s.",
> -                              binding_rec->logical_port,
> -                              binding_rec->chassis->name,
> -                              chassis_rec->name);
> -                } else {
> -                    VLOG_INFO("Claiming lport %s for this chassis.",
> -                              binding_rec->logical_port);
> -                }
> -                for (int i = 0; i < binding_rec->n_mac; i++) {
> -                    VLOG_INFO("%s: Claiming %s",
> -                              binding_rec->logical_port,
> binding_rec->mac[i]);
> -                }
> -                sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
> -            }
> -            /* Check if the port encap binding, if any, has changed */
> -            struct sbrec_encap *encap_rec = sbrec_get_port_encap(
> -                                            chassis_rec, iface_rec);
> -            if (encap_rec && binding_rec->encap != encap_rec) {
> -                sbrec_port_binding_set_encap(binding_rec, encap_rec);
> -            }
> -        } else if (binding_rec->chassis == chassis_rec) {
> -            VLOG_INFO("Releasing lport %s from this chassis.",
> -                      binding_rec->logical_port);
> -            if (binding_rec->encap)
> -                sbrec_port_binding_set_encap(binding_rec, NULL);
> -            sbrec_port_binding_set_chassis(binding_rec, NULL);
> -        } else if (our_chassis) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_INFO_RL(&rl,
> -                         "Not claiming lport %s, chassis %s "
> -                         "requested-chassis %s",
> -                         binding_rec->logical_port,
> -                         chassis_rec->name,
> -                         vif_chassis);
> -        }
> -    }
> -}
> -
> -static void
> -consider_localnet_port(const struct sbrec_port_binding *binding_rec,
> -                       struct hmap *local_datapaths)
> -{
> -    struct local_datapath *ld
> -        = get_local_datapath(local_datapaths,
> -                             binding_rec->datapath->tunnel_key);
> -    if (!ld) {
> -        return;
> -    }
> -
> -    if (ld->localnet_port && strcmp(ld->localnet_port->logical_port,
> -                                    binding_rec->logical_port)) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -        VLOG_WARN_RL(&rl, "localnet port '%s' already set for datapath "
> -                     "'%"PRId64"', skipping the new port '%s'.",
> -                     ld->localnet_port->logical_port,
> -                     binding_rec->datapath->tunnel_key,
> -                     binding_rec->logical_port);
> -        return;
> -    }
> -    ld->localnet_port = binding_rec;
> -}
> -
> -void
> -binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -            struct ovsdb_idl_txn *ovs_idl_txn,
> -            struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -            struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -            const struct ovsrec_port_table *port_table,
> -            const struct ovsrec_qos_table *qos_table,
> -            const struct sbrec_port_binding_table *port_binding_table,
> -            const struct ovsrec_bridge *br_int,
> -            const struct sbrec_chassis *chassis_rec,
> -            const struct sset *active_tunnels,
> -            struct hmap *local_datapaths, struct sset *local_lports,
> -            struct sset *local_lport_ids)
> -{
> -    if (!chassis_rec) {
> -        return;
> -    }
> -
> -    const struct sbrec_port_binding *binding_rec;
> -    struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
> -    struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
> -    struct hmap qos_map;
> -
> -    hmap_init(&qos_map);
> -    if (br_int) {
> -        get_local_iface_ids(br_int, &lport_to_iface, local_lports,
> -                            &egress_ifaces);
> -    }
> -
> -    /* Run through each binding record to see if it is resident on this
> -     * chassis and update the binding accordingly.  This includes both
> -     * directly connected logical ports and children of those ports. */
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
> -        consider_local_datapath(ovnsb_idl_txn, ovs_idl_txn,
> -                                sbrec_datapath_binding_by_key,
> -                                sbrec_port_binding_by_datapath,
> -                                sbrec_port_binding_by_name,
> -                                active_tunnels, chassis_rec, binding_rec,
> -                                sset_is_empty(&egress_ifaces) ? NULL :
> -                                &qos_map, local_datapaths,
> &lport_to_iface,
> -                                local_lports, local_lport_ids);
> -
> -    }
> -
> -    /* Run through each binding record to see if it is a localnet port
> -     * on local datapaths discovered from above loop, and update the
> -     * corresponding local datapath accordingly. */
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
> -        if (!strcmp(binding_rec->type, "localnet")) {
> -            consider_localnet_port(binding_rec, local_datapaths);
> -        }
> -    }
> -
> -    if (!sset_is_empty(&egress_ifaces)
> -        && set_noop_qos(ovs_idl_txn, port_table, qos_table,
> &egress_ifaces)) {
> -        const char *entry;
> -        SSET_FOR_EACH (entry, &egress_ifaces) {
> -            setup_qos(entry, &qos_map);
> -        }
> -    }
> -
> -    shash_destroy(&lport_to_iface);
> -    sset_destroy(&egress_ifaces);
> -    hmap_destroy(&qos_map);
> -}
> -
> -/* Returns true if port-binding changes potentially require flow changes
> on
> - * the current chassis. Returns false if we are sure there is no impact.
> */
> -bool
> -binding_evaluate_port_binding_changes(
> -        const struct sbrec_port_binding_table *pb_table,
> -        const struct ovsrec_bridge *br_int,
> -        const struct sbrec_chassis *chassis_rec,
> -        struct sset *active_tunnels,
> -        struct sset *local_lports)
> -{
> -    if (!chassis_rec) {
> -        return true;
> -    }
> -
> -    bool changed = false;
> -
> -    const struct sbrec_port_binding *binding_rec;
> -    struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
> -    struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
> -    if (br_int) {
> -        get_local_iface_ids(br_int, &lport_to_iface, local_lports,
> -                            &egress_ifaces);
> -    }
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding_rec, pb_table) {
> -        /* XXX: currently OVSDB change tracking doesn't support getting
> old
> -         * data when the operation is update, so if a port-binding moved
> from
> -         * this chassis to another, there is no easy way to find out the
> -         * change. To workaround this problem, we just makes sure if
> -         * any port *related to* this chassis has any change, then trigger
> -         * recompute.
> -         *
> -         * - If a regular VIF is unbound from this chassis, the local
> ovsdb
> -         *   interface table will be updated, which will trigger
> recompute.
> -         *
> -         * - If the port is not a regular VIF, always trigger recompute.
> */
> -        if (binding_rec->chassis == chassis_rec
> -            || is_our_chassis(chassis_rec, binding_rec,
> -                              active_tunnels, &lport_to_iface,
> local_lports)
> -            || strcmp(binding_rec->type, "")) {
> -            changed = true;
> -            break;
> -        }
> -    }
> -
> -    shash_destroy(&lport_to_iface);
> -    sset_destroy(&egress_ifaces);
> -    return changed;
> -}
> -
> -/* Returns true if the database is all cleaned up, false if more work is
> - * required. */
> -bool
> -binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                const struct sbrec_port_binding_table *port_binding_table,
> -                const struct sbrec_chassis *chassis_rec)
> -{
> -    if (!ovnsb_idl_txn) {
> -        return false;
> -    }
> -    if (!chassis_rec) {
> -        return true;
> -    }
> -
> -    const struct sbrec_port_binding *binding_rec;
> -    bool any_changes = false;
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding_rec, port_binding_table) {
> -        if (binding_rec->chassis == chassis_rec) {
> -            if (binding_rec->encap)
> -                sbrec_port_binding_set_encap(binding_rec, NULL);
> -            sbrec_port_binding_set_chassis(binding_rec, NULL);
> -            any_changes = true;
> -        }
> -    }
> -
> -    if (any_changes) {
> -        ovsdb_idl_txn_add_comment(
> -            ovnsb_idl_txn,
> -            "ovn-controller: removing all port bindings for '%s'",
> -            chassis_rec->name);
> -    }
> -
> -    return !any_changes;
> -}
> diff --git a/ovn/controller/binding.h b/ovn/controller/binding.h
> deleted file mode 100644
> index 8d9492630..000000000
> --- a/ovn/controller/binding.h
> +++ /dev/null
> @@ -1,57 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -
> -#ifndef OVN_BINDING_H
> -#define OVN_BINDING_H 1
> -
> -#include <stdbool.h>
> -
> -struct hmap;
> -struct ovsdb_idl;
> -struct ovsdb_idl_index;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_port_table;
> -struct ovsrec_qos_table;
> -struct sbrec_chassis;
> -struct sbrec_port_binding_table;
> -struct sset;
> -
> -void binding_register_ovs_idl(struct ovsdb_idl *);
> -void binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                 struct ovsdb_idl_txn *ovs_idl_txn,
> -                 struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -                 struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                 const struct ovsrec_port_table *,
> -                 const struct ovsrec_qos_table *,
> -                 const struct sbrec_port_binding_table *,
> -                 const struct ovsrec_bridge *br_int,
> -                 const struct sbrec_chassis *,
> -                 const struct sset *active_tunnels,
> -                 struct hmap *local_datapaths,
> -                 struct sset *local_lports, struct sset *local_lport_ids);
> -bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                     const struct sbrec_port_binding_table *,
> -                     const struct sbrec_chassis *);
> -bool binding_evaluate_port_binding_changes(
> -        const struct sbrec_port_binding_table *,
> -        const struct ovsrec_bridge *br_int,
> -        const struct sbrec_chassis *,
> -        struct sset *active_tunnels,
> -        struct sset *local_lports);
> -
> -#endif /* ovn/binding.h */
> diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
> deleted file mode 100644
> index 04b98d86c..000000000
> --- a/ovn/controller/chassis.c
> +++ /dev/null
> @@ -1,671 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include <unistd.h>
> -
> -#include "chassis.h"
> -
> -#include "lib/smap.h"
> -#include "lib/sset.h"
> -#include "lib/vswitch-idl.h"
> -#include "openvswitch/dynamic-string.h"
> -#include "openvswitch/vlog.h"
> -#include "openvswitch/ofp-parse.h"
> -#include "ovn/lib/chassis-index.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn-controller.h"
> -#include "lib/util.h"
> -
> -VLOG_DEFINE_THIS_MODULE(chassis);
> -
> -#ifndef HOST_NAME_MAX
> -/* For windows. */
> -#define HOST_NAME_MAX 255
> -#endif /* HOST_NAME_MAX */
> -
> -/*
> - * Structure to hold chassis specific state (currently just chassis-id)
> - * to avoid database lookups when changes happen while the controller is
> - * running.
> - */
> -struct chassis_info {
> -    /* Last ID we initialized the Chassis SB record with. */
> -    struct ds id;
> -
> -    /* True if Chassis SB record is initialized, false otherwise. */
> -    uint32_t id_inited : 1;
> -};
> -
> -static struct chassis_info chassis_state = {
> -    .id = DS_EMPTY_INITIALIZER,
> -    .id_inited = false,
> -};
> -
> -static void
> -chassis_info_set_id(struct chassis_info *info, const char *id)
> -{
> -    ds_clear(&info->id);
> -    ds_put_cstr(&info->id, id);
> -    info->id_inited = true;
> -}
> -
> -static bool
> -chassis_info_id_inited(const struct chassis_info *info)
> -{
> -    return info->id_inited;
> -}
> -
> -static const char *
> -chassis_info_id(const struct chassis_info *info)
> -{
> -    return ds_cstr_ro(&info->id);
> -}
> -
> -/*
> - * Structure for storing the chassis config parsed from the ovs table.
> - */
> -struct ovs_chassis_cfg {
> -    /* Single string fields parsed from external-ids. */
> -    const char *hostname;
> -    const char *bridge_mappings;
> -    const char *datapath_type;
> -    const char *encap_csum;
> -    const char *cms_options;
> -    const char *chassis_macs;
> -
> -    /* Set of encap types parsed from the 'ovn-encap-type' external-id. */
> -    struct sset encap_type_set;
> -    /* Set of encap IPs parsed from the 'ovn-encap-type' external-id. */
> -    struct sset encap_ip_set;
> -    /* Interface type list formatted in the OVN-SB Chassis required
> format. */
> -    struct ds iface_types;
> -};
> -
> -static void
> -ovs_chassis_cfg_init(struct ovs_chassis_cfg *cfg)
> -{
> -    sset_init(&cfg->encap_type_set);
> -    sset_init(&cfg->encap_ip_set);
> -    ds_init(&cfg->iface_types);
> -}
> -
> -static void
> -ovs_chassis_cfg_destroy(struct ovs_chassis_cfg *cfg)
> -{
> -    sset_destroy(&cfg->encap_type_set);
> -    sset_destroy(&cfg->encap_ip_set);
> -    ds_destroy(&cfg->iface_types);
> -}
> -
> -void
> -chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> -{
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_iface_types);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_datapath_type);
> -}
> -
> -static const char *
> -get_hostname(const struct smap *ext_ids)
> -{
> -    const char *hostname = smap_get_def(ext_ids, "hostname", "");
> -
> -    if (strlen(hostname) == 0) {
> -        static char hostname_[HOST_NAME_MAX + 1];
> -
> -        if (gethostname(hostname_, sizeof(hostname_))) {
> -            hostname_[0] = 0;
> -        }
> -
> -        return &hostname_[0];
> -    }
> -
> -    return hostname;
> -}
> -
> -static const char *
> -get_bridge_mappings(const struct smap *ext_ids)
> -{
> -    return smap_get_def(ext_ids, "ovn-bridge-mappings", "");
> -}
> -
> -static const char *
> -get_chassis_mac_mappings(const struct smap *ext_ids)
> -{
> -    return smap_get_def(ext_ids, "ovn-chassis-mac-mappings", "");
> -}
> -
> -static const char *
> -get_cms_options(const struct smap *ext_ids)
> -{
> -    return smap_get_def(ext_ids, "ovn-cms-options", "");
> -}
> -
> -static const char *
> -get_encap_csum(const struct smap *ext_ids)
> -{
> -    return smap_get_def(ext_ids, "ovn-encap-csum", "true");
> -}
> -
> -static const char *
> -get_datapath_type(const struct ovsrec_bridge *br_int)
> -{
> -    if (br_int && br_int->datapath_type) {
> -        return br_int->datapath_type;
> -    }
> -
> -    return "";
> -}
> -
> -static void
> -update_chassis_transport_zones(const struct sset *transport_zones,
> -                               const struct sbrec_chassis *chassis_rec)
> -{
> -    struct sset chassis_tzones_set =
> SSET_INITIALIZER(&chassis_tzones_set);
> -    for (int i = 0; i < chassis_rec->n_transport_zones; i++) {
> -        sset_add(&chassis_tzones_set, chassis_rec->transport_zones[i]);
> -    }
> -
> -    /* Only update the transport zones if something changed */
> -    if (!sset_equals(transport_zones, &chassis_tzones_set)) {
> -        const char **ls_arr = sset_array(transport_zones);
> -        sbrec_chassis_set_transport_zones(chassis_rec, ls_arr,
> -                                          sset_count(transport_zones));
> -        free(ls_arr);
> -    }
> -
> -    sset_destroy(&chassis_tzones_set);
> -}
> -
> -/*
> - * Parse an ovs 'encap_type' string and stores the resulting types in the
> - * 'encap_type_set' string set.
> - */
> -static bool
> -chassis_parse_ovs_encap_type(const char *encap_type,
> -                             struct sset *encap_type_set)
> -{
> -    sset_from_delimited_string(encap_type_set, encap_type, ",");
> -
> -    const char *type;
> -
> -    SSET_FOR_EACH (type, encap_type_set) {
> -        if (!get_tunnel_type(type)) {
> -            VLOG_INFO("Unknown tunnel type: %s", type);
> -        }
> -    }
> -
> -    return true;
> -}
> -
> -/*
> - * Parse an ovs 'encap_ip' string and stores the resulting IP
> representations
> - * in the 'encap_ip_set' string set.
> - */
> -static bool
> -chassis_parse_ovs_encap_ip(const char *encap_ip, struct sset
> *encap_ip_set)
> -{
> -    sset_from_delimited_string(encap_ip_set, encap_ip, ",");
> -    return true;
> -}
> -
> -/*
> - * Parse the ovs 'iface_types' and store them in the format required by
> the
> - * Chassis record.
> - */
> -static bool
> -chassis_parse_ovs_iface_types(char **iface_types, size_t n_iface_types,
> -                              struct ds *iface_types_str)
> -{
> -    for (size_t i = 0; i < n_iface_types; i++) {
> -        ds_put_format(iface_types_str, "%s,", iface_types[i]);
> -    }
> -    ds_chomp(iface_types_str, ',');
> -    return true;
> -}
> -
> -/*
> - * Parse the 'ovs_table' entry and populate 'ovs_cfg'.
> - */
> -static bool
> -chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table
> *ovs_table,
> -                         const struct ovsrec_bridge *br_int,
> -                         struct ovs_chassis_cfg *ovs_cfg)
> -{
> -    const struct ovsrec_open_vswitch *cfg =
> -        ovsrec_open_vswitch_table_first(ovs_table);
> -
> -    if (!cfg) {
> -        VLOG_INFO("No Open_vSwitch row defined.");
> -        return false;
> -    }
> -
> -    const char *encap_type = smap_get(&cfg->external_ids,
> "ovn-encap-type");
> -    const char *encap_ips = smap_get(&cfg->external_ids, "ovn-encap-ip");
> -    if (!encap_type || !encap_ips) {
> -        VLOG_INFO("Need to specify an encap type and ip");
> -        return false;
> -    }
> -
> -    ovs_cfg->hostname = get_hostname(&cfg->external_ids);
> -    ovs_cfg->bridge_mappings = get_bridge_mappings(&cfg->external_ids);
> -    ovs_cfg->datapath_type = get_datapath_type(br_int);
> -    ovs_cfg->encap_csum = get_encap_csum(&cfg->external_ids);
> -    ovs_cfg->cms_options = get_cms_options(&cfg->external_ids);
> -    ovs_cfg->chassis_macs = get_chassis_mac_mappings(&cfg->external_ids);
> -
> -    if (!chassis_parse_ovs_encap_type(encap_type,
> &ovs_cfg->encap_type_set)) {
> -        return false;
> -    }
> -
> -    if (!chassis_parse_ovs_encap_ip(encap_ips, &ovs_cfg->encap_ip_set)) {
> -        sset_destroy(&ovs_cfg->encap_type_set);
> -        return false;
> -    }
> -
> -    if (!chassis_parse_ovs_iface_types(cfg->iface_types,
> -                                       cfg->n_iface_types,
> -                                       &ovs_cfg->iface_types)) {
> -        sset_destroy(&ovs_cfg->encap_type_set);
> -        sset_destroy(&ovs_cfg->encap_ip_set);
> -    }
> -
> -    return true;
> -}
> -
> -static void
> -chassis_build_external_ids(struct smap *ext_ids, const char
> *bridge_mappings,
> -                           const char *datapath_type, const char
> *cms_options,
> -                           const char *chassis_macs, const char
> *iface_types)
> -{
> -    smap_replace(ext_ids, "ovn-bridge-mappings", bridge_mappings);
> -    smap_replace(ext_ids, "datapath-type", datapath_type);
> -    smap_replace(ext_ids, "ovn-cms-options", cms_options);
> -    smap_replace(ext_ids, "iface-types", iface_types);
> -    smap_replace(ext_ids, "ovn-chassis-mac-mappings", chassis_macs);
> -}
> -
> -/*
> - * Returns true if any external-id doesn't match the values in
> 'chassis-rec'.
> - */
> -static bool
> -chassis_external_ids_changed(const char *bridge_mappings,
> -                             const char *datapath_type,
> -                             const char *cms_options,
> -                             const char *chassis_macs,
> -                             const struct ds *iface_types,
> -                             const struct sbrec_chassis *chassis_rec)
> -{
> -    const char *chassis_bridge_mappings =
> -        get_bridge_mappings(&chassis_rec->external_ids);
> -
> -    if (strcmp(bridge_mappings, chassis_bridge_mappings)) {
> -        return true;
> -    }
> -
> -    const char *chassis_datapath_type =
> -        smap_get_def(&chassis_rec->external_ids, "datapath-type", "");
> -
> -    if (strcmp(datapath_type, chassis_datapath_type)) {
> -        return true;
> -    }
> -
> -    const char *chassis_cms_options =
> -        get_cms_options(&chassis_rec->external_ids);
> -
> -    if (strcmp(cms_options, chassis_cms_options)) {
> -        return true;
> -    }
> -
> -    const char *chassis_mac_mappings =
> -        get_chassis_mac_mappings(&chassis_rec->external_ids);
> -    if (strcmp(chassis_macs, chassis_mac_mappings)) {
> -        return true;
> -    }
> -
> -    const char *chassis_iface_types =
> -        smap_get_def(&chassis_rec->external_ids, "iface-types", "");
> -
> -    if (strcmp(ds_cstr_ro(iface_types), chassis_iface_types)) {
> -        return true;
> -    }
> -
> -    return false;
> -}
> -
> -/*
> - * Returns true if the tunnel config obtained by combining
> 'encap_type_set'
> - * with 'encap_ip_set' and 'encap_csum' doesn't match the values in
> - * 'chassis-rec'.
> - */
> -static bool
> -chassis_tunnels_changed(const struct sset *encap_type_set,
> -                        const struct sset *encap_ip_set,
> -                        const char *encap_csum,
> -                        const struct sbrec_chassis *chassis_rec)
> -{
> -    size_t encap_type_count = 0;
> -
> -    for (int i = 0; i < chassis_rec->n_encaps; i++) {
> -        if (strcmp(chassis_rec->name,
> chassis_rec->encaps[i]->chassis_name)) {
> -            return true;
> -        }
> -
> -        if (!sset_contains(encap_type_set, chassis_rec->encaps[i]->type))
> {
> -            return true;
> -        }
> -        encap_type_count++;
> -
> -        if (!sset_contains(encap_ip_set, chassis_rec->encaps[i]->ip)) {
> -            return true;
> -        }
> -
> -        if (strcmp(smap_get_def(&chassis_rec->encaps[i]->options, "csum",
> ""),
> -                   encap_csum)) {
> -            return true;
> -        }
> -    }
> -
> -    size_t tunnel_count =
> -        sset_count(encap_type_set) * sset_count(encap_ip_set);
> -
> -    if (tunnel_count != chassis_rec->n_encaps) {
> -        return true;
> -    }
> -
> -    if (sset_count(encap_type_set) != encap_type_count) {
> -        return true;
> -    }
> -
> -    return false;
> -}
> -
> -/*
> - * Build the new encaps config (full mesh of 'encap_type_set' and
> - * 'encap_ip_set'). Allocates and stores the new 'n_encap' Encap records
> in
> - * 'encaps'.
> - */
> -static struct sbrec_encap **
> -chassis_build_encaps(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                     const struct sset *encap_type_set,
> -                     const struct sset *encap_ip_set,
> -                     const char *chassis_id,
> -                     const char *encap_csum,
> -                     size_t *n_encap)
> -{
> -    size_t tunnel_count = 0;
> -
> -    struct sbrec_encap **encaps =
> -        xmalloc(sset_count(encap_type_set) * sset_count(encap_ip_set) *
> -                sizeof(*encaps));
> -    const struct smap options = SMAP_CONST1(&options, "csum", encap_csum);
> -
> -    const char *encap_ip;
> -    const char *encap_type;
> -
> -    SSET_FOR_EACH (encap_ip, encap_ip_set) {
> -        SSET_FOR_EACH (encap_type, encap_type_set) {
> -            struct sbrec_encap *encap = sbrec_encap_insert(ovnsb_idl_txn);
> -
> -            sbrec_encap_set_type(encap, encap_type);
> -            sbrec_encap_set_ip(encap, encap_ip);
> -            sbrec_encap_set_options(encap, &options);
> -            sbrec_encap_set_chassis_name(encap, chassis_id);
> -
> -            encaps[tunnel_count] = encap;
> -            tunnel_count++;
> -        }
> -    }
> -
> -    *n_encap = tunnel_count;
> -    return encaps;
> -}
> -
> -/*
> - * Returns a pointer to a chassis record from 'chassis_table' that
> - * matches at least one tunnel config.
> - */
> -static const struct sbrec_chassis *
> -chassis_get_stale_record(const struct sbrec_chassis_table *chassis_table,
> -                         const struct ovs_chassis_cfg *ovs_cfg,
> -                         const char *chassis_id)
> -{
> -    const struct sbrec_chassis *chassis_rec;
> -
> -    SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
> -        for (size_t i = 0; i < chassis_rec->n_encaps; i++) {
> -            if (sset_contains(&ovs_cfg->encap_type_set,
> -                              chassis_rec->encaps[i]->type) &&
> -                    sset_contains(&ovs_cfg->encap_ip_set,
> -                                  chassis_rec->encaps[i]->ip)) {
> -                return chassis_rec;
> -            }
> -            if (strcmp(chassis_rec->name, chassis_id) == 0) {
> -                return chassis_rec;
> -            }
> -        }
> -    }
> -
> -    return NULL;
> -}
> -
> -/* If this is a chassis config update after we initialized the record once
> - * then we should always be able to find it with the ID we saved in
> - * chassis_state.
> - * Otherwise (i.e., first time we create the record) then we check if
> there's
> - * a stale record from a previous controller run that didn't end
> gracefully
> - * and reuse it. If not then we create a new record.
> - */
> -static const struct sbrec_chassis *
> -chassis_get_record(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                   struct ovsdb_idl_index *sbrec_chassis_by_name,
> -                   const struct sbrec_chassis_table *chassis_table,
> -                   const struct ovs_chassis_cfg *ovs_cfg,
> -                   const char *chassis_id)
> -{
> -    const struct sbrec_chassis *chassis_rec;
> -
> -    if (chassis_info_id_inited(&chassis_state)) {
> -        chassis_rec = chassis_lookup_by_name(sbrec_chassis_by_name,
> -
>  chassis_info_id(&chassis_state));
> -        if (!chassis_rec) {
> -            VLOG_WARN("Could not find Chassis : stored (%s) ovs (%s)",
> -                      chassis_info_id(&chassis_state), chassis_id);
> -        }
> -    } else {
> -        chassis_rec =
> -            chassis_get_stale_record(chassis_table, ovs_cfg, chassis_id);
> -
> -        if (!chassis_rec && ovnsb_idl_txn) {
> -            chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn);
> -        }
> -    }
> -    return chassis_rec;
> -}
> -
> -/* Update a Chassis record based on the config in the ovs config. */
> -static void
> -chassis_update(const struct sbrec_chassis *chassis_rec,
> -               struct ovsdb_idl_txn *ovnsb_idl_txn,
> -               const struct ovs_chassis_cfg *ovs_cfg,
> -               const char *chassis_id,
> -               const struct sset *transport_zones)
> -{
> -    if (strcmp(chassis_id, chassis_rec->name)) {
> -        sbrec_chassis_set_name(chassis_rec, chassis_id);
> -    }
> -
> -    if (strcmp(ovs_cfg->hostname, chassis_rec->hostname)) {
> -        sbrec_chassis_set_hostname(chassis_rec, ovs_cfg->hostname);
> -    }
> -
> -    if (chassis_external_ids_changed(ovs_cfg->bridge_mappings,
> -                                     ovs_cfg->datapath_type,
> -                                     ovs_cfg->cms_options,
> -                                     ovs_cfg->chassis_macs,
> -                                     &ovs_cfg->iface_types,
> -                                     chassis_rec)) {
> -        struct smap ext_ids;
> -
> -        smap_clone(&ext_ids, &chassis_rec->external_ids);
> -        chassis_build_external_ids(&ext_ids, ovs_cfg->bridge_mappings,
> -                                   ovs_cfg->datapath_type,
> -                                   ovs_cfg->cms_options,
> -                                   ovs_cfg->chassis_macs,
> -                                   ds_cstr_ro(&ovs_cfg->iface_types));
> -        sbrec_chassis_verify_external_ids(chassis_rec);
> -        sbrec_chassis_set_external_ids(chassis_rec, &ext_ids);
> -        smap_destroy(&ext_ids);
> -    }
> -
> -    update_chassis_transport_zones(transport_zones, chassis_rec);
> -
> -    /* If any of the encaps should change, update them. */
> -    bool tunnels_changed =
> -        chassis_tunnels_changed(&ovs_cfg->encap_type_set,
> -                                &ovs_cfg->encap_ip_set,
> ovs_cfg->encap_csum,
> -                                chassis_rec);
> -    if (!tunnels_changed) {
> -        return;
> -    }
> -
> -    struct sbrec_encap **encaps;
> -    size_t n_encap;
> -
> -    encaps =
> -        chassis_build_encaps(ovnsb_idl_txn, &ovs_cfg->encap_type_set,
> -                             &ovs_cfg->encap_ip_set, chassis_id,
> -                             ovs_cfg->encap_csum, &n_encap);
> -    sbrec_chassis_set_encaps(chassis_rec, encaps, n_encap);
> -    free(encaps);
> -}
> -
> -/* Returns this chassis's Chassis record, if it is available. */
> -const struct sbrec_chassis *
> -chassis_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -            struct ovsdb_idl_index *sbrec_chassis_by_name,
> -            const struct ovsrec_open_vswitch_table *ovs_table,
> -            const struct sbrec_chassis_table *chassis_table,
> -            const char *chassis_id,
> -            const struct ovsrec_bridge *br_int,
> -            const struct sset *transport_zones)
> -{
> -    struct ovs_chassis_cfg ovs_cfg;
> -
> -    /* Get the chassis config from the ovs table. */
> -    ovs_chassis_cfg_init(&ovs_cfg);
> -    if (!chassis_parse_ovs_config(ovs_table, br_int, &ovs_cfg)) {
> -        return NULL;
> -    }
> -
> -    const struct sbrec_chassis *chassis_rec =
> -        chassis_get_record(ovnsb_idl_txn, sbrec_chassis_by_name,
> -                           chassis_table, &ovs_cfg, chassis_id);
> -
> -    /* If we found (or created) a record, update it with the correct
> config
> -     * and store the current chassis_id for fast lookup in case it gets
> -     * modified in the ovs table.
> -     */
> -    if (chassis_rec && ovnsb_idl_txn) {
> -        chassis_update(chassis_rec, ovnsb_idl_txn, &ovs_cfg, chassis_id,
> -                       transport_zones);
> -        chassis_info_set_id(&chassis_state, chassis_id);
> -        ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
> -                                  "ovn-controller: registering chassis
> '%s'",
> -                                  chassis_id);
> -    }
> -
> -    ovs_chassis_cfg_destroy(&ovs_cfg);
> -    return chassis_rec;
> -}
> -
> -bool
> -chassis_get_mac(const struct sbrec_chassis *chassis_rec,
> -                const char *bridge_mapping,
> -                struct eth_addr *chassis_mac)
> -{
> -    const char *tokens
> -        = get_chassis_mac_mappings(&chassis_rec->external_ids);
> -    if (!tokens[0]) {
> -       return false;
> -    }
> -
> -    char *save_ptr = NULL;
> -    bool ret = false;
> -    char *tokstr = xstrdup(tokens);
> -
> -    /* Format for a chassis mac configuration is:
> -     * ovn-chassis-mac-mappings="bridge-name1:MAC1,bridge-name2:MAC2"
> -     */
> -    for (char *token = strtok_r(tokstr, ",", &save_ptr);
> -         token != NULL;
> -         token = strtok_r(NULL, ",", &save_ptr)) {
> -        char *save_ptr2 = NULL;
> -        char *chassis_mac_bridge = strtok_r(token, ":", &save_ptr2);
> -        char *chassis_mac_str = strtok_r(NULL, "", &save_ptr2);
> -
> -        if (!strcmp(chassis_mac_bridge, bridge_mapping)) {
> -            struct eth_addr temp_mac;
> -
> -            /* Return the first chassis mac. */
> -            char *err_str = str_to_mac(chassis_mac_str, &temp_mac);
> -            if (err_str) {
> -                free(err_str);
> -                continue;
> -            }
> -
> -            ret = true;
> -            *chassis_mac = temp_mac;
> -            break;
> -        }
> -    }
> -
> -    free(tokstr);
> -    return ret;
> -}
> -
> -/* Returns true if the database is all cleaned up, false if more work is
> - * required. */
> -bool
> -chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                const struct sbrec_chassis *chassis_rec)
> -{
> -    if (!chassis_rec) {
> -        return true;
> -    }
> -    if (ovnsb_idl_txn) {
> -        ovsdb_idl_txn_add_comment(ovnsb_idl_txn,
> -                                  "ovn-controller: unregistering chassis
> '%s'",
> -                                  chassis_rec->name);
> -        sbrec_chassis_delete(chassis_rec);
> -    }
> -    return false;
> -}
> -
> -/*
> - * Returns the last initialized chassis-id.
> - */
> -const char *
> -chassis_get_id(void)
> -{
> -    if (chassis_info_id_inited(&chassis_state)) {
> -        return chassis_info_id(&chassis_state);
> -    }
> -
> -    return NULL;
> -}
> diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h
> deleted file mode 100644
> index 16a131a3b..000000000
> --- a/ovn/controller/chassis.h
> +++ /dev/null
> @@ -1,46 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_CHASSIS_H
> -#define OVN_CHASSIS_H 1
> -
> -#include <stdbool.h>
> -
> -struct ovsdb_idl;
> -struct ovsdb_idl_index;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_open_vswitch_table;
> -struct sbrec_chassis;
> -struct sbrec_chassis_table;
> -struct sset;
> -struct eth_addr;
> -
> -void chassis_register_ovs_idl(struct ovsdb_idl *);
> -const struct sbrec_chassis *chassis_run(
> -    struct ovsdb_idl_txn *ovnsb_idl_txn,
> -    struct ovsdb_idl_index *sbrec_chassis_by_name,
> -    const struct ovsrec_open_vswitch_table *,
> -    const struct sbrec_chassis_table *,
> -    const char *chassis_id, const struct ovsrec_bridge *br_int,
> -    const struct sset *transport_zones);
> -bool chassis_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                     const struct sbrec_chassis *);
> -bool chassis_get_mac(const struct sbrec_chassis *chassis,
> -                     const char *bridge_mapping,
> -                     struct eth_addr *chassis_mac);
> -const char *chassis_get_id(void);
> -
> -#endif /* ovn/chassis.h */
> diff --git a/ovn/controller/encaps.c b/ovn/controller/encaps.c
> deleted file mode 100644
> index d4a436df3..000000000
> --- a/ovn/controller/encaps.c
> +++ /dev/null
> @@ -1,409 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "encaps.h"
> -
> -#include "lib/hash.h"
> -#include "lib/sset.h"
> -#include "lib/util.h"
> -#include "lib/vswitch-idl.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn-controller.h"
> -
> -VLOG_DEFINE_THIS_MODULE(encaps);
> -
> -/*
> - * Given there could be multiple tunnels with different IPs to the same
> - * chassis we annotate the ovn-chassis-id with
> - * <chassis_name>OVN_MVTEP_CHASSISID_DELIM<IP>.
> - */
> -#define        OVN_MVTEP_CHASSISID_DELIM '@'
> -
> -void
> -encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> -{
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options);
> -}
> -
> -/* Enough context to create a new tunnel, using tunnel_add(). */
> -struct tunnel_ctx {
> -    /* Maps from a chassis name to "struct chassis_node *". */
> -    struct shash chassis;
> -
> -    /* Names of all ports in the bridge, to allow checking uniqueness when
> -     * adding a new tunnel. */
> -    struct sset port_names;
> -
> -    struct ovsdb_idl_txn *ovs_txn;
> -    const struct ovsrec_bridge *br_int;
> -};
> -
> -struct chassis_node {
> -    const struct ovsrec_port *port;
> -    const struct ovsrec_bridge *bridge;
> -};
> -
> -static char *
> -tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
> -{
> -    int i;
> -
> -    for (i = 0; i < UINT16_MAX; i++) {
> -        char *port_name;
> -        port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
> -
> -        if (!sset_contains(&tc->port_names, port_name)) {
> -            return port_name;
> -        }
> -
> -        free(port_name);
> -    }
> -
> -    return NULL;
> -}
> -
> -/*
> - * Returns a tunnel-id of the form 'chassis_id'-delimiter-'encap_ip'.
> - */
> -char *
> -encaps_tunnel_id_create(const char *chassis_id, const char *encap_ip)
> -{
> -    return xasprintf("%s%c%s", chassis_id, OVN_MVTEP_CHASSISID_DELIM,
> -                     encap_ip);
> -}
> -
> -/*
> - * Parses a 'tunnel_id' of the form <chassis_name><delimiter><IP>.
> - * If the 'chassis_id' argument is not NULL the function will allocate
> memory
> - * and store the chassis-id part of the tunnel-id at '*chassis_id'.
> - * If the 'encap_ip' argument is not NULL the function will allocate
> memory
> - * and store the encapsulation IP part of the tunnel-id at '*encap_ip'.
> - */
> -bool
> -encaps_tunnel_id_parse(const char *tunnel_id, char **chassis_id,
> -                       char **encap_ip)
> -{
> -    /* Find the delimiter.  Fail if there is no delimiter or if
> <chassis_name>
> -     * or <IP> is the empty string.*/
> -    const char *d = strchr(tunnel_id, OVN_MVTEP_CHASSISID_DELIM);
> -    if (d == tunnel_id || !d || !d[1]) {
> -        return false;
> -    }
> -
> -    if (chassis_id) {
> -        *chassis_id = xmemdup0(tunnel_id, d - tunnel_id);
> -    }
> -    if (encap_ip) {
> -        *encap_ip = xstrdup(d + 1);
> -    }
> -    return true;
> -}
> -
> -/*
> - * Returns true if 'tunnel_id' contains 'chassis_id' and, if specified,
> the
> - * given 'encap_ip'. Returns false otherwise.
> - */
> -bool
> -encaps_tunnel_id_match(const char *tunnel_id, const char *chassis_id,
> -                       const char *encap_ip)
> -{
> -    while (*tunnel_id == *chassis_id) {
> -        if (!*tunnel_id) {
> -            /* 'tunnel_id' and 'chassis_id' are equal strings.  This is a
> -             * mismatch because 'tunnel_id' is missing the delimiter and
> IP. */
> -            return false;
> -        }
> -        tunnel_id++;
> -        chassis_id++;
> -    }
> -
> -    /* We found the first byte that disagrees between 'tunnel_id' and
> -     * 'chassis_id'.  If we consumed all of 'chassis_id' and arrived at
> the
> -     * delimiter in 'tunnel_id' (and if 'encap_ip' is correct, if it was
> -     * supplied), it's a match. */
> -    return (*tunnel_id == OVN_MVTEP_CHASSISID_DELIM
> -            && *chassis_id == '\0'
> -            && (!encap_ip || !strcmp(tunnel_id + 1, encap_ip)));
> -}
> -
> -static void
> -tunnel_add(struct tunnel_ctx *tc, const struct sbrec_sb_global *sbg,
> -           const char *new_chassis_id, const struct sbrec_encap *encap)
> -{
> -    struct smap options = SMAP_INITIALIZER(&options);
> -    smap_add(&options, "remote_ip", encap->ip);
> -    smap_add(&options, "key", "flow");
> -    const char *dst_port = smap_get(&encap->options, "dst_port");
> -    const char *csum = smap_get(&encap->options, "csum");
> -    char *tunnel_entry_id = NULL;
> -
> -    /*
> -     * Since a chassis may have multiple encap-ip, we can't just add the
> -     * chassis name as as the "ovn-chassis-id" for the port; we use the
> -     * combination of the chassis_name and the encap-ip to identify
> -     * a specific tunnel to the chassis.
> -     */
> -    tunnel_entry_id = encaps_tunnel_id_create(new_chassis_id, encap->ip);
> -    if (csum && (!strcmp(csum, "true") || !strcmp(csum, "false"))) {
> -        smap_add(&options, "csum", csum);
> -    }
> -    if (dst_port) {
> -        smap_add(&options, "dst_port", dst_port);
> -    }
> -
> -    /* Add auth info if ipsec is enabled. */
> -    if (sbg->ipsec) {
> -        smap_add(&options, "remote_name", new_chassis_id);
> -    }
> -
> -    /* If there's an existing chassis record that does not need any
> change,
> -     * keep it.  Otherwise, create a new record (if there was an existing
> -     * record, the new record will supplant it and encaps_run() will
> delete
> -     * it). */
> -    struct chassis_node *chassis = shash_find_data(&tc->chassis,
> -                                                   tunnel_entry_id);
> -    if (chassis
> -        && chassis->port->n_interfaces == 1
> -        && !strcmp(chassis->port->interfaces[0]->type, encap->type)
> -        && smap_equal(&chassis->port->interfaces[0]->options, &options)) {
> -        shash_find_and_delete(&tc->chassis, tunnel_entry_id);
> -        free(chassis);
> -        goto exit;
> -    }
> -
> -    /* Choose a name for the new port.  If we're replacing an old port,
> reuse
> -     * its name, otherwise generate a new, unique name. */
> -    char *port_name = (chassis
> -                       ? xstrdup(chassis->port->name)
> -                       : tunnel_create_name(tc, new_chassis_id));
> -    if (!port_name) {
> -        VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
> -                  new_chassis_id);
> -        goto exit;
> -    }
> -
> -    struct ovsrec_interface *iface = ovsrec_interface_insert(tc->ovs_txn);
> -    ovsrec_interface_set_name(iface, port_name);
> -    ovsrec_interface_set_type(iface, encap->type);
> -    ovsrec_interface_set_options(iface, &options);
> -
> -    struct ovsrec_port *port = ovsrec_port_insert(tc->ovs_txn);
> -    ovsrec_port_set_name(port, port_name);
> -    ovsrec_port_set_interfaces(port, &iface, 1);
> -    const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id",
> tunnel_entry_id);
> -    ovsrec_port_set_external_ids(port, &id);
> -
> -    ovsrec_bridge_update_ports_addvalue(tc->br_int, port);
> -
> -    sset_add_and_free(&tc->port_names, port_name);
> -
> -exit:
> -    free(tunnel_entry_id);
> -    smap_destroy(&options);
> -}
> -
> -struct sbrec_encap *
> -preferred_encap(const struct sbrec_chassis *chassis_rec)
> -{
> -    struct sbrec_encap *best_encap = NULL;
> -    uint32_t best_type = 0;
> -
> -    for (int i = 0; i < chassis_rec->n_encaps; i++) {
> -        uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
> -        if (tun_type > best_type) {
> -            best_type = tun_type;
> -            best_encap = chassis_rec->encaps[i];
> -        }
> -    }
> -
> -    return best_encap;
> -}
> -
> -/*
> - * For each peer chassis, get a preferred tunnel type and create as many
> tunnels
> - * as there are VTEP of that type (differentiated by remote_ip) on that
> chassis.
> - */
> -static int
> -chassis_tunnel_add(const struct sbrec_chassis *chassis_rec, const struct
> sbrec_sb_global *sbg, struct tunnel_ctx *tc)
> -{
> -    struct sbrec_encap *encap = preferred_encap(chassis_rec);
> -    int tuncnt = 0;
> -
> -    if (!encap) {
> -        VLOG_INFO("chassis_tunnel_add: No supported encaps for '%s'",
> chassis_rec->name);
> -        return tuncnt;
> -    }
> -
> -    uint32_t pref_type = get_tunnel_type(encap->type);
> -    for (int i = 0; i < chassis_rec->n_encaps; i++) {
> -        uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
> -        if (tun_type != pref_type) {
> -            continue;
> -        }
> -        tunnel_add(tc, sbg, chassis_rec->name, chassis_rec->encaps[i]);
> -        tuncnt++;
> -    }
> -    return tuncnt;
> -}
> -
> -/*
> -* Returns true if transport_zones and chassis_rec->transport_zones
> -* have at least one common transport zone.
> -*/
> -static bool
> -chassis_tzones_overlap(const struct sset *transport_zones,
> -                       const struct sbrec_chassis *chassis_rec)
> -{
> -    /* If neither Chassis belongs to any transport zones, return true to
> -     * form a tunnel between them */
> -    if (!chassis_rec->n_transport_zones &&
> sset_is_empty(transport_zones)) {
> -        return true;
> -    }
> -
> -    for (int i = 0; i < chassis_rec->n_transport_zones; i++) {
> -        if (sset_contains(transport_zones,
> chassis_rec->transport_zones[i])) {
> -            return true;
> -        }
> -    }
> -    return false;
> -}
> -
> -void
> -encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -           const struct ovsrec_bridge_table *bridge_table,
> -           const struct ovsrec_bridge *br_int,
> -           const struct sbrec_chassis_table *chassis_table,
> -           const char *chassis_id,
> -           const struct sbrec_sb_global *sbg,
> -           const struct sset *transport_zones)
> -{
> -    if (!ovs_idl_txn || !br_int) {
> -        return;
> -    }
> -
> -    const struct sbrec_chassis *chassis_rec;
> -    const struct ovsrec_bridge *br;
> -
> -    struct tunnel_ctx tc = {
> -        .chassis = SHASH_INITIALIZER(&tc.chassis),
> -        .port_names = SSET_INITIALIZER(&tc.port_names),
> -        .br_int = br_int
> -    };
> -
> -    tc.ovs_txn = ovs_idl_txn;
> -    ovsdb_idl_txn_add_comment(tc.ovs_txn,
> -                              "ovn-controller: modifying OVS tunnels
> '%s'",
> -                              chassis_id);
> -
> -    /* Collect all port names into tc.port_names.
> -     *
> -     * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
> -    OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
> -        for (size_t i = 0; i < br->n_ports; i++) {
> -            const struct ovsrec_port *port = br->ports[i];
> -            sset_add(&tc.port_names, port->name);
> -
> -            /*
> -             * note that the id here is not just the chassis name, but the
> -             * combination of <chassis_name><delim><encap_ip>
> -             */
> -            const char *id = smap_get(&port->external_ids,
> "ovn-chassis-id");
> -            if (id) {
> -                if (!shash_find(&tc.chassis, id)) {
> -                    struct chassis_node *chassis = xzalloc(sizeof
> *chassis);
> -                    chassis->bridge = br;
> -                    chassis->port = port;
> -                    shash_add_assert(&tc.chassis, id, chassis);
> -                } else {
> -                    /* Duplicate port for ovn-chassis-id.  Arbitrarily
> choose
> -                     * to delete this one. */
> -                    ovsrec_bridge_update_ports_delvalue(br, port);
> -                }
> -            }
> -        }
> -    }
> -
> -    SBREC_CHASSIS_TABLE_FOR_EACH (chassis_rec, chassis_table) {
> -        if (strcmp(chassis_rec->name, chassis_id)) {
> -            /* Create tunnels to the other Chassis belonging to the
> -             * same transport zone */
> -            if (!chassis_tzones_overlap(transport_zones, chassis_rec)) {
> -                VLOG_DBG("Skipping encap creation for Chassis '%s'
> because "
> -                         "it belongs to different transport zones",
> -                         chassis_rec->name);
> -                continue;
> -            }
> -
> -            if (chassis_tunnel_add(chassis_rec, sbg, &tc) == 0) {
> -                VLOG_INFO("Creating encap for '%s' failed",
> chassis_rec->name);
> -                continue;
> -            }
> -        }
> -    }
> -
> -    /* Delete any existing OVN tunnels that were not still around. */
> -    struct shash_node *node, *next_node;
> -    SHASH_FOR_EACH_SAFE (node, next_node, &tc.chassis) {
> -        struct chassis_node *chassis = node->data;
> -        ovsrec_bridge_update_ports_delvalue(chassis->bridge,
> chassis->port);
> -        shash_delete(&tc.chassis, node);
> -        free(chassis);
> -    }
> -    shash_destroy(&tc.chassis);
> -    sset_destroy(&tc.port_names);
> -}
> -
> -/* Returns true if the database is all cleaned up, false if more work is
> - * required. */
> -bool
> -encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
> -               const struct ovsrec_bridge *br_int)
> -{
> -    if (!br_int) {
> -        return true;
> -    }
> -
> -    /* Delete all the OVS-created tunnels from the integration bridge. */
> -    struct ovsrec_port **ports
> -        = xmalloc(sizeof *br_int->ports * br_int->n_ports);
> -    size_t n = 0;
> -    for (size_t i = 0; i < br_int->n_ports; i++) {
> -        if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id"))
> {
> -            ports[n++] = br_int->ports[i];
> -        }
> -    }
> -
> -    bool any_changes = n != br_int->n_ports;
> -    if (any_changes && ovs_idl_txn) {
> -        ovsdb_idl_txn_add_comment(ovs_idl_txn,
> -                                  "ovn-controller: destroying tunnels");
> -        ovsrec_bridge_verify_ports(br_int);
> -        ovsrec_bridge_set_ports(br_int, ports, n);
> -    }
> -    free(ports);
> -
> -    return !any_changes;
> -}
> diff --git a/ovn/controller/encaps.h b/ovn/controller/encaps.h
> deleted file mode 100644
> index afa41830a..000000000
> --- a/ovn/controller/encaps.h
> +++ /dev/null
> @@ -1,48 +0,0 @@
> -/* Copyright (c) 2015 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_ENCAPS_H
> -#define OVN_ENCAPS_H 1
> -
> -#include <stdbool.h>
> -
> -struct ovsdb_idl;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_bridge_table;
> -struct sbrec_chassis_table;
> -struct sbrec_sb_global;
> -struct ovsrec_open_vswitch_table;
> -struct sset;
> -
> -void encaps_register_ovs_idl(struct ovsdb_idl *);
> -void encaps_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -                const struct ovsrec_bridge_table *,
> -                const struct ovsrec_bridge *br_int,
> -                const struct sbrec_chassis_table *,
> -                const char *chassis_id,
> -                const struct sbrec_sb_global *,
> -                const struct sset *transport_zones);
> -
> -bool encaps_cleanup(struct ovsdb_idl_txn *ovs_idl_txn,
> -                    const struct ovsrec_bridge *br_int);
> -
> -char *encaps_tunnel_id_create(const char *chassis_id, const char
> *encap_ip);
> -bool  encaps_tunnel_id_parse(const char *tunnel_id, char **chassis_id,
> -                             char **encap_ip);
> -bool  encaps_tunnel_id_match(const char *tunnel_id, const char
> *chassis_id,
> -                             const char *encap_ip);
> -
> -#endif /* ovn/encaps.h */
> diff --git a/ovn/controller/ha-chassis.c b/ovn/controller/ha-chassis.c
> deleted file mode 100644
> index 498e5ce5a..000000000
> --- a/ovn/controller/ha-chassis.c
> +++ /dev/null
> @@ -1,203 +0,0 @@
> -/* Copyright (c) 2019 Red Hat, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "ha-chassis.h"
> -#include "lib/sset.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -
> -VLOG_DEFINE_THIS_MODULE(ha_chassis);
> -
> -static int
> -compare_chassis_prio_(const void *a_, const void *b_)
> -{
> -    const struct sbrec_ha_chassis *ch_a = a_;
> -    const struct sbrec_ha_chassis *ch_b = b_;
> -    int prio_diff = ch_b->priority - ch_a->priority;
> -    if (!prio_diff) {
> -        return strcmp(ch_b->chassis->name, ch_a->chassis->name);
> -    }
> -    return prio_diff;
> -}
> -
> -/* Returns the ordered HA chassis list in the HA chassis group.
> - * Eg. If an HA chassis group has 3 HA chassis
> - *   - HA1 - pri 30
> - *   - HA2 - pri 40 and
> - *   - HA3 - pri 20
> - * and the ref_chassis of HA chassis group is set to - C1 and C2.
> - *
> - * If active_tunnels is NULL, then it returns the ordered list
> - *   -  (HA2, HA1, HA3)
> - *
> - * If active_tunnels is set to - (HA1, HA2, C1, C2) and
> - * local_chassis is HA3, then it returns the ordered list
> - *  - (HA2, HA1, HA3)
> - *
> - * If active_tunnels is set to - (HA1, C1, C2) and
> - * local_chassis is HA3, then it returns the ordered list
> - *  - (HA1, HA3)
> - *
> - * If active_tunnels is set to - (C1, C2) and
> - * local_chassis is HA3, then it returns the ordered list
> - *  - (HA3)
> - *
> - * If active_tunnels is set is empty and local_chassis is HA3,
> - * then it returns NULL.
> - */
> -static struct ha_chassis_ordered *
> -get_ordered_ha_chassis_list(const struct sbrec_ha_chassis_group
> *ha_ch_grp,
> -                            const struct sset *active_tunnels,
> -                            const struct sbrec_chassis *local_chassis)
> -{
> -    struct sbrec_ha_chassis *ha_ch_order =
> -        xzalloc(sizeof *ha_ch_order * ha_ch_grp->n_ha_chassis);
> -
> -    size_t n_ha_ch = 0;
> -
> -    for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
> -        if (!ha_ch_grp->ha_chassis[i]->chassis) {
> -            continue;
> -        }
> -
> -        /* Don't add it to the list for ordering if it is not active. */
> -        if (ha_ch_grp->ha_chassis[i]->chassis != local_chassis &&
> -            active_tunnels &&
> -            !sset_contains(active_tunnels,
> -                           ha_ch_grp->ha_chassis[i]->chassis->name)) {
> -            continue;
> -        }
> -
> -        ha_ch_order[n_ha_ch].chassis = ha_ch_grp->ha_chassis[i]->chassis;
> -        ha_ch_order[n_ha_ch].priority =
> ha_ch_grp->ha_chassis[i]->priority;
> -        n_ha_ch++;
> -    }
> -
> -    if (!n_ha_ch) {
> -        free(ha_ch_order);
> -        return NULL;
> -    }
> -
> -    struct ha_chassis_ordered *ordered_ha_ch;
> -    if (n_ha_ch == 1) {
> -        if (active_tunnels) {
> -            /* If n_ha_ch is 1, it means only the local chassis is in the
> -            * ha_ch_order list. Check if this local chassis has active
> -            * bfd session with any of the referenced chassis. If so,
> -            * then the local chassis can be active. Otherwise it can't.
> -            * This can happen in the following scenario.
> -            * Lets say we have chassis HA1 (prioirty 20) and HA2
> (priority 10)
> -            * in the ha_chasis_group and compute chassis C1 and C2 are in
> the
> -            * reference chassis list. If HA1 chassis has lost the link and
> -            * when this function is called for HA2 we need to consider
> -            * HA2 as active since it has active BFD sessions with C1 and
> C2.
> -            * On HA1 chassis, this function won't be called since
> -            * active_tunnels set will be empty.
> -            * */
> -            bool can_local_chassis_be_active = false;
> -            for (size_t i = 0; i < ha_ch_grp->n_ref_chassis; i++) {
> -                if (sset_contains(active_tunnels,
> -                                ha_ch_grp->ref_chassis[i]->name)) {
> -                    can_local_chassis_be_active = true;
> -                    break;
> -                }
> -            }
> -            if (!can_local_chassis_be_active) {
> -                free(ha_ch_order);
> -                return NULL;
> -            }
> -        }
> -    } else {
> -        qsort(ha_ch_order, n_ha_ch, sizeof *ha_ch_order,
> -              compare_chassis_prio_);
> -    }
> -
> -    ordered_ha_ch = xmalloc(sizeof *ordered_ha_ch);
> -    ordered_ha_ch->ha_ch = ha_ch_order;
> -    ordered_ha_ch->n_ha_ch = n_ha_ch;
> -
> -    return ordered_ha_ch;
> -}
> -
> -void
> -ha_chassis_destroy_ordered(struct ha_chassis_ordered *ordered_ha_ch)
> -{
> -    if (ordered_ha_ch) {
> -        free(ordered_ha_ch->ha_ch);
> -        free(ordered_ha_ch);
> -    }
> -}
> -
> -
> -/* Returns true if the local_chassis is the master of
> - * the HA chassis group, false otherwise. */
> -bool
> -ha_chassis_group_is_active(
> -    const struct sbrec_ha_chassis_group *ha_ch_grp,
> -    const struct sset *active_tunnels,
> -    const struct sbrec_chassis *local_chassis)
> -{
> -    if (!ha_ch_grp || !ha_ch_grp->n_ha_chassis) {
> -        return false;
> -    }
> -
> -    if (ha_ch_grp->n_ha_chassis == 1) {
> -        return (ha_ch_grp->ha_chassis[0]->chassis == local_chassis);
> -    }
> -
> -    if (sset_is_empty(active_tunnels)) {
> -        /* If active tunnel sset is empty, it means it has lost
> -         * connectivity with other chassis. */
> -        return false;
> -    }
> -
> -    struct ha_chassis_ordered *ordered_ha_ch =
> -        get_ordered_ha_chassis_list(ha_ch_grp, active_tunnels,
> local_chassis);
> -    if (!ordered_ha_ch) {
> -        return false;
> -    }
> -
> -    struct sbrec_chassis *active_ch = ordered_ha_ch->ha_ch[0].chassis;
> -    ha_chassis_destroy_ordered(ordered_ha_ch);
> -
> -    return (active_ch == local_chassis);
> -}
> -
> -bool
> -ha_chassis_group_contains(
> -    const struct sbrec_ha_chassis_group *ha_chassis_grp,
> -    const struct sbrec_chassis *chassis)
> -{
> -    if (ha_chassis_grp && chassis) {
> -        for (size_t i = 0; i < ha_chassis_grp->n_ha_chassis; i++) {
> -            if (ha_chassis_grp->ha_chassis[i]->chassis == chassis) {
> -                return true;
> -            }
> -        }
> -    }
> -    return false;
> -}
> -
> -struct ha_chassis_ordered *
> -ha_chassis_get_ordered(const struct sbrec_ha_chassis_group
> *ha_chassis_grp)
> -{
> -    if (!ha_chassis_grp || !ha_chassis_grp->n_ha_chassis) {
> -        return NULL;
> -    }
> -
> -    return get_ordered_ha_chassis_list(ha_chassis_grp, NULL, NULL);
> -}
> diff --git a/ovn/controller/ha-chassis.h b/ovn/controller/ha-chassis.h
> deleted file mode 100644
> index 3768c2a5c..000000000
> --- a/ovn/controller/ha-chassis.h
> +++ /dev/null
> @@ -1,50 +0,0 @@
> -/* Copyright (c) 2019 Red Hat, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_HA_CHASSIS_H
> -#define OVN_HA_CHASSIS_H 1
> -
> -#include <stdint.h>
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/list.h"
> -
> -struct sbrec_chassis;
> -struct sbrec_ha_chassis_group;
> -struct sset;
> -
> -struct ha_chassis_ordered {
> -    struct sbrec_ha_chassis *ha_ch;
> -    size_t n_ha_ch;
> -};
> -
> -/* Returns true if the local chassis is the active gateway among a set
> - * of gateway_chassis.  Return false if the local chassis is currently a
> - * backup in a set of multiple gateway_chassis. */
> -bool ha_chassis_group_is_active(
> -    const struct sbrec_ha_chassis_group *ha_chassis_grp,
> -    const struct sset *active_tunnels,
> -    const struct sbrec_chassis *local_chassis);
> -
> -bool ha_chassis_group_contains(
> -    const struct sbrec_ha_chassis_group *ha_chassis_grp,
> -    const struct sbrec_chassis *chassis);
> -
> -struct ha_chassis_ordered *ha_chassis_get_ordered(
> -    const struct sbrec_ha_chassis_group *ha_chassis_grp);
> -
> -void ha_chassis_destroy_ordered(
> -    struct ha_chassis_ordered *ordered_ha_ch);
> -
> -#endif /* OVN_HA_CHASSIS_H */
> diff --git a/ovn/controller/ip-mcast.c b/ovn/controller/ip-mcast.c
> deleted file mode 100644
> index ef36be2ca..000000000
> --- a/ovn/controller/ip-mcast.c
> +++ /dev/null
> @@ -1,164 +0,0 @@
> -/* Copyright (c) 2019, Red Hat, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "ip-mcast.h"
> -#include "lport.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -
> -/*
> - * Used for (faster) updating of IGMP_Group ports.
> - */
> -struct igmp_group_port {
> -    struct hmap_node hmap_node;
> -    const struct sbrec_port_binding *port;
> -};
> -
> -struct ovsdb_idl_index *
> -igmp_group_index_create(struct ovsdb_idl *idl)
> -{
> -    const struct ovsdb_idl_index_column cols[] = {
> -        { .column = &sbrec_igmp_group_col_address },
> -        { .column = &sbrec_igmp_group_col_datapath },
> -        { .column = &sbrec_igmp_group_col_chassis },
> -    };
> -
> -    return ovsdb_idl_index_create(idl, cols, ARRAY_SIZE(cols));
> -}
> -
> -/* Looks up an IGMP group based on an IPv4 (mapped in IPv6) or IPv6
> 'address'
> - * and 'datapath'.
> - */
> -const struct sbrec_igmp_group *
> -igmp_group_lookup(struct ovsdb_idl_index *igmp_groups,
> -                  const struct in6_addr *address,
> -                  const struct sbrec_datapath_binding *datapath,
> -                  const struct sbrec_chassis *chassis)
> -{
> -    char addr_str[INET6_ADDRSTRLEN];
> -
> -    if (!ipv6_string_mapped(addr_str, address)) {
> -        return NULL;
> -    }
> -
> -    struct sbrec_igmp_group *target =
> -        sbrec_igmp_group_index_init_row(igmp_groups);
> -
> -    sbrec_igmp_group_index_set_address(target, addr_str);
> -    sbrec_igmp_group_index_set_datapath(target, datapath);
> -    sbrec_igmp_group_index_set_chassis(target, chassis);
> -
> -    const struct sbrec_igmp_group *g =
> -        sbrec_igmp_group_index_find(igmp_groups, target);
> -    sbrec_igmp_group_index_destroy_row(target);
> -    return g;
> -}
> -
> -/* Creates and returns a new IGMP group based on an IPv4 (mapped in IPv6)
> or
> - * IPv6 'address', 'datapath' and 'chassis'.
> - */
> -struct sbrec_igmp_group *
> -igmp_group_create(struct ovsdb_idl_txn *idl_txn,
> -                  const struct in6_addr *address,
> -                  const struct sbrec_datapath_binding *datapath,
> -                  const struct sbrec_chassis *chassis)
> -{
> -    char addr_str[INET6_ADDRSTRLEN];
> -
> -    if (!ipv6_string_mapped(addr_str, address)) {
> -        return NULL;
> -    }
> -
> -    struct sbrec_igmp_group *g = sbrec_igmp_group_insert(idl_txn);
> -
> -    sbrec_igmp_group_set_address(g, addr_str);
> -    sbrec_igmp_group_set_datapath(g, datapath);
> -    sbrec_igmp_group_set_chassis(g, chassis);
> -
> -    return g;
> -}
> -
> -void
> -igmp_group_update_ports(const struct sbrec_igmp_group *g,
> -                        struct ovsdb_idl_index *datapaths,
> -                        struct ovsdb_idl_index *port_bindings,
> -                        const struct mcast_snooping *ms OVS_UNUSED,
> -                        const struct mcast_group *mc_group)
> -    OVS_REQ_RDLOCK(ms->rwlock)
> -{
> -    struct igmp_group_port *old_ports_storage =
> -        (g->n_ports ? xmalloc(g->n_ports * sizeof *old_ports_storage) :
> NULL);
> -
> -    struct hmap old_ports = HMAP_INITIALIZER(&old_ports);
> -
> -    for (size_t i = 0; i < g->n_ports; i++) {
> -        struct igmp_group_port *old_port = &old_ports_storage[i];
> -
> -        old_port->port = g->ports[i];
> -        hmap_insert(&old_ports, &old_port->hmap_node,
> -                    old_port->port->tunnel_key);
> -    }
> -
> -    struct mcast_group_bundle *bundle;
> -    uint64_t dp_key = g->datapath->tunnel_key;
> -
> -    LIST_FOR_EACH (bundle, bundle_node, &mc_group->bundle_lru) {
> -        uint32_t port_key = (uintptr_t)bundle->port;
> -        const struct sbrec_port_binding *sbrec_port =
> -            lport_lookup_by_key(datapaths, port_bindings, dp_key,
> port_key);
> -        if (!sbrec_port) {
> -            continue;
> -        }
> -
> -        struct hmap_node *node = hmap_first_with_hash(&old_ports,
> port_key);
> -        if (!node) {
> -            sbrec_igmp_group_update_ports_addvalue(g, sbrec_port);
> -        } else {
> -            hmap_remove(&old_ports, node);
> -        }
> -    }
> -
> -    struct igmp_group_port *igmp_port;
> -    HMAP_FOR_EACH_POP (igmp_port, hmap_node, &old_ports) {
> -        sbrec_igmp_group_update_ports_delvalue(g, igmp_port->port);
> -    }
> -
> -    free(old_ports_storage);
> -    hmap_destroy(&old_ports);
> -}
> -
> -void
> -igmp_group_delete(const struct sbrec_igmp_group *g)
> -{
> -    sbrec_igmp_group_delete(g);
> -}
> -
> -bool
> -igmp_group_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                   struct ovsdb_idl_index *igmp_groups)
> -{
> -    const struct sbrec_igmp_group *g;
> -
> -    if (!ovnsb_idl_txn) {
> -        return true;
> -    }
> -
> -    SBREC_IGMP_GROUP_FOR_EACH_BYINDEX (g, igmp_groups) {
> -        igmp_group_delete(g);
> -    }
> -
> -    return true;
> -}
> diff --git a/ovn/controller/ip-mcast.h b/ovn/controller/ip-mcast.h
> deleted file mode 100644
> index 6014f43d5..000000000
> --- a/ovn/controller/ip-mcast.h
> +++ /dev/null
> @@ -1,52 +0,0 @@
> -/* Copyright (c) 2019, Red Hat, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_IP_MCAST_H
> -#define OVN_IP_MCAST_H 1
> -
> -#include "mcast-snooping.h"
> -
> -struct ovsdb_idl;
> -struct ovsdb_idl_txn;
> -
> -struct sbrec_chassis;
> -struct sbrec_datapath_binding;
> -
> -struct ovsdb_idl_index *igmp_group_index_create(struct ovsdb_idl *);
> -const struct sbrec_igmp_group *igmp_group_lookup(
> -    struct ovsdb_idl_index *igmp_groups,
> -    const struct in6_addr *address,
> -    const struct sbrec_datapath_binding *datapath,
> -    const struct sbrec_chassis *chassis);
> -
> -struct sbrec_igmp_group *igmp_group_create(
> -    struct ovsdb_idl_txn *idl_txn,
> -    const struct in6_addr *address,
> -    const struct sbrec_datapath_binding *datapath,
> -    const struct sbrec_chassis *chassis);
> -
> -void igmp_group_update_ports(const struct sbrec_igmp_group *g,
> -                             struct ovsdb_idl_index *datapaths,
> -                             struct ovsdb_idl_index *port_bindings,
> -                             const struct mcast_snooping *ms,
> -                             const struct mcast_group *mc_group)
> -    OVS_REQ_RDLOCK(ms->rwlock);
> -
> -void igmp_group_delete(const struct sbrec_igmp_group *g);
> -
> -bool igmp_group_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> -                        struct ovsdb_idl_index *igmp_groups);
> -
> -#endif /* ovn/controller/ip-mcast.h */
> diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
> deleted file mode 100644
> index 1aafafb33..000000000
> --- a/ovn/controller/lflow.c
> +++ /dev/null
> @@ -1,898 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "lflow.h"
> -#include "coverage.h"
> -#include "ha-chassis.h"
> -#include "lport.h"
> -#include "ofctrl.h"
> -#include "openvswitch/dynamic-string.h"
> -#include "openvswitch/ofp-actions.h"
> -#include "openvswitch/ofpbuf.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn-controller.h"
> -#include "ovn/actions.h"
> -#include "ovn/expr.h"
> -#include "ovn/lib/ovn-l7.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn/lib/extend-table.h"
> -#include "packets.h"
> -#include "physical.h"
> -#include "simap.h"
> -#include "sset.h"
> -
> -VLOG_DEFINE_THIS_MODULE(lflow);
> -
> -COVERAGE_DEFINE(lflow_run);
> -
> -/* Symbol table. */
> -
> -/* Contains "struct expr_symbol"s for fields supported by OVN lflows. */
> -static struct shash symtab;
> -
> -void
> -lflow_init(void)
> -{
> -    ovn_init_symtab(&symtab);
> -}
> -
> -struct lookup_port_aux {
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath;
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name;
> -    const struct sbrec_datapath_binding *dp;
> -};
> -
> -struct condition_aux {
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name;
> -    const struct sbrec_chassis *chassis;
> -    const struct sset *active_tunnels;
> -};
> -
> -static bool consider_logical_flow(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_logical_flow *,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *,
> -    struct hmap *dhcp_opts,
> -    struct hmap *dhcpv6_opts,
> -    struct hmap *nd_ra_opts,
> -    struct controller_event_options *controller_event_opts,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *lfrr,
> -    uint32_t *conj_id_ofs);
> -
> -static bool
> -lookup_port_cb(const void *aux_, const char *port_name, unsigned int
> *portp)
> -{
> -    const struct lookup_port_aux *aux = aux_;
> -
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(aux->sbrec_port_binding_by_name,
> port_name);
> -    if (pb && pb->datapath == aux->dp) {
> -        *portp = pb->tunnel_key;
> -        return true;
> -    }
> -
> -    const struct sbrec_multicast_group *mg = mcgroup_lookup_by_dp_name(
> -        aux->sbrec_multicast_group_by_name_datapath, aux->dp, port_name);
> -    if (mg) {
> -        *portp = mg->tunnel_key;
> -        return true;
> -    }
> -
> -    return false;
> -}
> -
> -static bool
> -is_chassis_resident_cb(const void *c_aux_, const char *port_name)
> -{
> -    const struct condition_aux *c_aux = c_aux_;
> -
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(c_aux->sbrec_port_binding_by_name,
> port_name);
> -    if (!pb) {
> -        return false;
> -    }
> -    if (strcmp(pb->type, "chassisredirect")) {
> -        /* for non-chassisredirect ports */
> -        return pb->chassis && pb->chassis == c_aux->chassis;
> -    } else {
> -        if (ha_chassis_group_contains(pb->ha_chassis_group,
> -                                      c_aux->chassis)) {
> -            bool active = ha_chassis_group_is_active(pb->ha_chassis_group,
> -
>  c_aux->active_tunnels,
> -                                                     c_aux->chassis);
> -            return active;
> -        }
> -        return false;
> -    }
> -}
> -
> -static bool
> -is_switch(const struct sbrec_datapath_binding *ldp)
> -{
> -    return smap_get(&ldp->external_ids, "logical-switch") != NULL;
> -
> -}
> -
> -void
> -lflow_resource_init(struct lflow_resource_ref *lfrr)
> -{
> -    hmap_init(&lfrr->ref_lflow_table);
> -    hmap_init(&lfrr->lflow_ref_table);
> -}
> -
> -void
> -lflow_resource_destroy(struct lflow_resource_ref *lfrr)
> -{
> -    struct ref_lflow_node *rlfn, *rlfn_next;
> -    HMAP_FOR_EACH_SAFE (rlfn, rlfn_next, node, &lfrr->ref_lflow_table) {
> -        free(rlfn->ref_name);
> -        struct lflow_ref_list_node *lrln, *next;
> -        LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) {
> -            ovs_list_remove(&lrln->ref_list);
> -            ovs_list_remove(&lrln->lflow_list);
> -            free(lrln);
> -        }
> -        hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
> -        free(rlfn);
> -    }
> -    hmap_destroy(&lfrr->ref_lflow_table);
> -
> -    struct lflow_ref_node *lfrn, *lfrn_next;
> -    HMAP_FOR_EACH_SAFE (lfrn, lfrn_next, node, &lfrr->lflow_ref_table) {
> -        hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
> -        free(lfrn);
> -    }
> -    hmap_destroy(&lfrr->lflow_ref_table);
> -}
> -
> -void
> -lflow_resource_clear(struct lflow_resource_ref *lfrr)
> -{
> -    lflow_resource_destroy(lfrr);
> -    lflow_resource_init(lfrr);
> -}
> -
> -static struct ref_lflow_node*
> -ref_lflow_lookup(struct hmap *ref_lflow_table,
> -                 enum ref_type type, const char *ref_name)
> -{
> -    struct ref_lflow_node *rlfn;
> -
> -    HMAP_FOR_EACH_WITH_HASH (rlfn, node, hash_string(ref_name, type),
> -                             ref_lflow_table) {
> -        if (rlfn->type == type && !strcmp(rlfn->ref_name, ref_name)) {
> -            return rlfn;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static struct lflow_ref_node*
> -lflow_ref_lookup(struct hmap *lflow_ref_table,
> -                 const struct uuid *lflow_uuid)
> -{
> -    struct lflow_ref_node *lfrn;
> -
> -    HMAP_FOR_EACH_WITH_HASH (lfrn, node, uuid_hash(lflow_uuid),
> -                             lflow_ref_table) {
> -        if (uuid_equals(&lfrn->lflow_uuid, lflow_uuid)) {
> -            return lfrn;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static void
> -lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type,
> -                   const char *ref_name, const struct uuid *lflow_uuid)
> -{
> -    struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
> -                                                   type, ref_name);
> -    if (!rlfn) {
> -        rlfn = xzalloc(sizeof *rlfn);
> -        rlfn->node.hash = hash_string(ref_name, type);
> -        rlfn->type = type;
> -        rlfn->ref_name = xstrdup(ref_name);
> -        ovs_list_init(&rlfn->ref_lflow_head);
> -        hmap_insert(&lfrr->ref_lflow_table, &rlfn->node, rlfn->node.hash);
> -    }
> -
> -    struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
> -                                                   lflow_uuid);
> -    if (!lfrn) {
> -        lfrn = xzalloc(sizeof *lfrn);
> -        lfrn->node.hash = uuid_hash(lflow_uuid);
> -        lfrn->lflow_uuid = *lflow_uuid;
> -        ovs_list_init(&lfrn->lflow_ref_head);
> -        hmap_insert(&lfrr->lflow_ref_table, &lfrn->node, lfrn->node.hash);
> -    }
> -
> -    struct lflow_ref_list_node *lrln = xzalloc(sizeof *lrln);
> -    lrln->type = type;
> -    lrln->ref_name = xstrdup(ref_name);
> -    lrln->lflow_uuid = *lflow_uuid;
> -    ovs_list_push_back(&rlfn->ref_lflow_head, &lrln->ref_list);
> -    ovs_list_push_back(&lfrn->lflow_ref_head, &lrln->lflow_list);
> -}
> -
> -static void
> -lflow_resource_destroy_lflow(struct lflow_resource_ref *lfrr,
> -                            const struct uuid *lflow_uuid)
> -{
> -    struct lflow_ref_node *lfrn = lflow_ref_lookup(&lfrr->lflow_ref_table,
> -                                                   lflow_uuid);
> -    if (!lfrn) {
> -        return;
> -    }
> -
> -    hmap_remove(&lfrr->lflow_ref_table, &lfrn->node);
> -    struct lflow_ref_list_node *lrln, *next;
> -    LIST_FOR_EACH_SAFE (lrln, next, lflow_list, &lfrn->lflow_ref_head) {
> -        ovs_list_remove(&lrln->ref_list);
> -        ovs_list_remove(&lrln->lflow_list);
> -        free(lrln);
> -    }
> -    free(lfrn);
> -}
> -
> -/* Adds the logical flows from the Logical_Flow table to flow tables. */
> -static void
> -add_logical_flows(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_dhcp_options_table *dhcp_options_table,
> -    const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
> -    const struct sbrec_logical_flow_table *logical_flow_table,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *chassis,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *flow_table,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *lfrr,
> -    uint32_t *conj_id_ofs)
> -{
> -    const struct sbrec_logical_flow *lflow;
> -
> -    struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
> -    struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
> -    const struct sbrec_dhcp_options *dhcp_opt_row;
> -    SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
> -        dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
> -                     dhcp_opt_row->type);
> -    }
> -
> -
> -    const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
> -    SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
> -                                         dhcpv6_options_table) {
> -       dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name,
> dhcpv6_opt_row->code,
> -                    dhcpv6_opt_row->type);
> -    }
> -
> -    struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
> -    nd_ra_opts_init(&nd_ra_opts);
> -
> -    struct controller_event_options controller_event_opts;
> -    controller_event_opts_init(&controller_event_opts);
> -
> -    SBREC_LOGICAL_FLOW_TABLE_FOR_EACH (lflow, logical_flow_table) {
> -        if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
> -                                   sbrec_port_binding_by_name,
> -                                   lflow, local_datapaths,
> -                                   chassis, &dhcp_opts, &dhcpv6_opts,
> -                                   &nd_ra_opts, &controller_event_opts,
> -                                   addr_sets, port_groups,
> -                                   active_tunnels, local_lport_ids,
> -                                   flow_table, group_table, meter_table,
> -                                   lfrr, conj_id_ofs)) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> -            VLOG_ERR_RL(&rl, "Conjunction id overflow when processing
> lflow "
> -                        UUID_FMT, UUID_ARGS(&lflow->header_.uuid));
> -        }
> -    }
> -
> -    dhcp_opts_destroy(&dhcp_opts);
> -    dhcp_opts_destroy(&dhcpv6_opts);
> -    nd_ra_opts_destroy(&nd_ra_opts);
> -    controller_event_opts_destroy(&controller_event_opts);
> -}
> -
> -bool
> -lflow_handle_changed_flows(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_dhcp_options_table *dhcp_options_table,
> -    const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
> -    const struct sbrec_logical_flow_table *logical_flow_table,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *chassis,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *flow_table,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *lfrr,
> -    uint32_t *conj_id_ofs)
> -{
> -    bool ret = true;
> -    const struct sbrec_logical_flow *lflow;
> -
> -    struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
> -    struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
> -    const struct sbrec_dhcp_options *dhcp_opt_row;
> -    SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
> -        dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
> -                     dhcp_opt_row->type);
> -    }
> -
> -
> -    const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
> -    SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH (dhcpv6_opt_row,
> -                                         dhcpv6_options_table) {
> -       dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name,
> dhcpv6_opt_row->code,
> -                    dhcpv6_opt_row->type);
> -    }
> -
> -    struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
> -    nd_ra_opts_init(&nd_ra_opts);
> -
> -    /* Handle removed flows first, and then other flows, so that when
> -     * the flows being added and removed have same match conditions
> -     * can be processed in the proper order */
> -    SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table)
> {
> -        /* Remove any flows that should be removed. */
> -        if (sbrec_logical_flow_is_deleted(lflow)) {
> -            VLOG_DBG("handle deleted lflow "UUID_FMT,
> -                     UUID_ARGS(&lflow->header_.uuid));
> -            ofctrl_remove_flows(flow_table, &lflow->header_.uuid);
> -            /* Delete entries from lflow resource reference. */
> -            lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
> -        }
> -    }
> -
> -    struct controller_event_options controller_event_opts;
> -    controller_event_opts_init(&controller_event_opts);
> -
> -    SBREC_LOGICAL_FLOW_TABLE_FOR_EACH_TRACKED (lflow, logical_flow_table)
> {
> -        if (!sbrec_logical_flow_is_deleted(lflow)) {
> -            /* Now, add/modify existing flows. If the logical
> -             * flow is a modification, just remove the flows
> -             * for this row, and then add new flows. */
> -            if (!sbrec_logical_flow_is_new(lflow)) {
> -                VLOG_DBG("handle updated lflow "UUID_FMT,
> -                         UUID_ARGS(&lflow->header_.uuid));
> -                ofctrl_remove_flows(flow_table, &lflow->header_.uuid);
> -                /* Delete entries from lflow resource reference. */
> -                lflow_resource_destroy_lflow(lfrr, &lflow->header_.uuid);
> -            }
> -            VLOG_DBG("handle new lflow "UUID_FMT,
> -                     UUID_ARGS(&lflow->header_.uuid));
> -            if
> (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
> -                                       sbrec_port_binding_by_name,
> -                                       lflow, local_datapaths,
> -                                       chassis, &dhcp_opts, &dhcpv6_opts,
> -                                       &nd_ra_opts,
> &controller_event_opts,
> -                                       addr_sets, port_groups,
> -                                       active_tunnels, local_lport_ids,
> -                                       flow_table, group_table,
> meter_table,
> -                                       lfrr, conj_id_ofs)) {
> -                ret = false;
> -                break;
> -            }
> -        }
> -    }
> -    dhcp_opts_destroy(&dhcp_opts);
> -    dhcp_opts_destroy(&dhcpv6_opts);
> -    nd_ra_opts_destroy(&nd_ra_opts);
> -    controller_event_opts_destroy(&controller_event_opts);
> -    return ret;
> -}
> -
> -bool
> -lflow_handle_changed_ref(
> -    enum ref_type ref_type,
> -    const char *ref_name,
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_dhcp_options_table *dhcp_options_table,
> -    const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
> -    const struct sbrec_logical_flow_table *logical_flow_table,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *chassis,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *flow_table,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *lfrr,
> -    uint32_t *conj_id_ofs,
> -    bool *changed)
> -{
> -    struct ref_lflow_node *rlfn = ref_lflow_lookup(&lfrr->ref_lflow_table,
> -                                                   ref_type, ref_name);
> -    if (!rlfn) {
> -        *changed = false;
> -        return true;
> -    }
> -    VLOG_DBG("Handle changed lflow reference for resource type: %d,"
> -             " name: %s.", ref_type, ref_name);
> -    *changed = false;
> -    bool ret = true;
> -
> -    hmap_remove(&lfrr->ref_lflow_table, &rlfn->node);
> -
> -    struct lflow_ref_list_node *lrln, *next;
> -    /* Detach the rlfn->ref_lflow_head nodes from the lfrr table and clean
> -     * up all other nodes related to the lflows that uses the resource,
> -     * so that the old nodes won't interfere with updating the lfrr table
> -     * when reparsing the lflows. */
> -    LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
> -        ovs_list_remove(&lrln->lflow_list);
> -        lflow_resource_destroy_lflow(lfrr, &lrln->lflow_uuid);
> -    }
> -
> -    struct hmap dhcp_opts = HMAP_INITIALIZER(&dhcp_opts);
> -    struct hmap dhcpv6_opts = HMAP_INITIALIZER(&dhcpv6_opts);
> -    const struct sbrec_dhcp_options *dhcp_opt_row;
> -    SBREC_DHCP_OPTIONS_TABLE_FOR_EACH (dhcp_opt_row, dhcp_options_table) {
> -        dhcp_opt_add(&dhcp_opts, dhcp_opt_row->name, dhcp_opt_row->code,
> -                     dhcp_opt_row->type);
> -    }
> -
> -    const struct sbrec_dhcpv6_options *dhcpv6_opt_row;
> -    SBREC_DHCPV6_OPTIONS_TABLE_FOR_EACH(dhcpv6_opt_row,
> dhcpv6_options_table) {
> -       dhcp_opt_add(&dhcpv6_opts, dhcpv6_opt_row->name,
> dhcpv6_opt_row->code,
> -                    dhcpv6_opt_row->type);
> -    }
> -
> -    struct hmap nd_ra_opts = HMAP_INITIALIZER(&nd_ra_opts);
> -    nd_ra_opts_init(&nd_ra_opts);
> -
> -    struct controller_event_options controller_event_opts;
> -    controller_event_opts_init(&controller_event_opts);
> -
> -    /* Re-parse the related lflows. */
> -    LIST_FOR_EACH (lrln, ref_list, &rlfn->ref_lflow_head) {
> -        const struct sbrec_logical_flow *lflow =
> -            sbrec_logical_flow_table_get_for_uuid(logical_flow_table,
> -                                                  &lrln->lflow_uuid);
> -        if (!lflow) {
> -            VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
> -                     " name: %s - not found.",
> -                     UUID_ARGS(&lrln->lflow_uuid),
> -                     ref_type, ref_name);
> -            continue;
> -        }
> -        VLOG_DBG("Reprocess lflow "UUID_FMT" for resource type: %d,"
> -                 " name: %s.",
> -                 UUID_ARGS(&lrln->lflow_uuid),
> -                 ref_type, ref_name);
> -        ofctrl_remove_flows(flow_table, &lrln->lflow_uuid);
> -
> -        if (!consider_logical_flow(sbrec_multicast_group_by_name_datapath,
> -                                   sbrec_port_binding_by_name,
> -                                   lflow, local_datapaths,
> -                                   chassis, &dhcp_opts, &dhcpv6_opts,
> -                                   &nd_ra_opts, &controller_event_opts,
> -                                   addr_sets, port_groups,
> -                                   active_tunnels, local_lport_ids,
> -                                   flow_table, group_table, meter_table,
> -                                   lfrr, conj_id_ofs)) {
> -            ret = false;
> -            break;
> -        }
> -        *changed = true;
> -    }
> -
> -    LIST_FOR_EACH_SAFE (lrln, next, ref_list, &rlfn->ref_lflow_head) {
> -        ovs_list_remove(&lrln->ref_list);
> -        free(lrln);
> -    }
> -    free(rlfn);
> -
> -    dhcp_opts_destroy(&dhcp_opts);
> -    dhcp_opts_destroy(&dhcpv6_opts);
> -    nd_ra_opts_destroy(&nd_ra_opts);
> -    controller_event_opts_destroy(&controller_event_opts);
> -    return ret;
> -}
> -
> -static bool
> -update_conj_id_ofs(uint32_t *conj_id_ofs, uint32_t n_conjs)
> -{
> -    if (*conj_id_ofs + n_conjs < *conj_id_ofs) {
> -        /* overflow */
> -        return false;
> -    }
> -    *conj_id_ofs += n_conjs;
> -    return true;
> -}
> -
> -static bool
> -consider_logical_flow(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_logical_flow *lflow,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *chassis,
> -    struct hmap *dhcp_opts,
> -    struct hmap *dhcpv6_opts,
> -    struct hmap *nd_ra_opts,
> -    struct controller_event_options *controller_event_opts,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *flow_table,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *lfrr,
> -    uint32_t *conj_id_ofs)
> -{
> -    /* Determine translation of logical table IDs to physical table IDs.
> */
> -    bool ingress = !strcmp(lflow->pipeline, "ingress");
> -
> -    const struct sbrec_datapath_binding *ldp = lflow->logical_datapath;
> -    if (!ldp) {
> -        VLOG_DBG("lflow "UUID_FMT" has no datapath binding, skip",
> -                 UUID_ARGS(&lflow->header_.uuid));
> -        return true;
> -    }
> -    if (!get_local_datapath(local_datapaths, ldp->tunnel_key)) {
> -        VLOG_DBG("lflow "UUID_FMT" is not for local datapath, skip",
> -                 UUID_ARGS(&lflow->header_.uuid));
> -        return true;
> -    }
> -
> -    /* Determine translation of logical table IDs to physical table IDs.
> */
> -    uint8_t first_ptable = (ingress
> -                            ? OFTABLE_LOG_INGRESS_PIPELINE
> -                            : OFTABLE_LOG_EGRESS_PIPELINE);
> -    uint8_t ptable = first_ptable + lflow->table_id;
> -    uint8_t output_ptable = (ingress
> -                             ? OFTABLE_REMOTE_OUTPUT
> -                             : OFTABLE_SAVE_INPORT);
> -
> -    /* Parse OVN logical actions.
> -     *
> -     * XXX Deny changes to 'outport' in egress pipeline. */
> -    uint64_t ovnacts_stub[1024 / 8];
> -    struct ofpbuf ovnacts = OFPBUF_STUB_INITIALIZER(ovnacts_stub);
> -    struct ovnact_parse_params pp = {
> -        .symtab = &symtab,
> -        .dhcp_opts = dhcp_opts,
> -        .dhcpv6_opts = dhcpv6_opts,
> -        .nd_ra_opts = nd_ra_opts,
> -        .controller_event_opts = controller_event_opts,
> -
> -        .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
> -        .n_tables = LOG_PIPELINE_LEN,
> -        .cur_ltable = lflow->table_id,
> -    };
> -    struct expr *prereqs;
> -    char *error;
> -
> -    error = ovnacts_parse_string(lflow->actions, &pp, &ovnacts, &prereqs);
> -    if (error) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        VLOG_WARN_RL(&rl, "error parsing actions \"%s\": %s",
> -                     lflow->actions, error);
> -        free(error);
> -        ovnacts_free(ovnacts.data, ovnacts.size);
> -        ofpbuf_uninit(&ovnacts);
> -        return true;
> -    }
> -
> -    /* Translate OVN match into table of OpenFlow matches. */
> -    struct hmap matches;
> -    struct expr *expr;
> -
> -    struct sset addr_sets_ref = SSET_INITIALIZER(&addr_sets_ref);
> -    expr = expr_parse_string(lflow->match, &symtab, addr_sets,
> port_groups,
> -                             &addr_sets_ref, &error);
> -    const char *addr_set_name;
> -    SSET_FOR_EACH (addr_set_name, &addr_sets_ref) {
> -        lflow_resource_add(lfrr, REF_TYPE_ADDRSET, addr_set_name,
> -                           &lflow->header_.uuid);
> -    }
> -    sset_destroy(&addr_sets_ref);
> -
> -    if (!error) {
> -        if (prereqs) {
> -            expr = expr_combine(EXPR_T_AND, expr, prereqs);
> -            prereqs = NULL;
> -        }
> -        expr = expr_annotate(expr, &symtab, &error);
> -    }
> -    if (error) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        VLOG_WARN_RL(&rl, "error parsing match \"%s\": %s",
> -                     lflow->match, error);
> -        expr_destroy(prereqs);
> -        free(error);
> -        ovnacts_free(ovnacts.data, ovnacts.size);
> -        ofpbuf_uninit(&ovnacts);
> -        return true;
> -    }
> -
> -    struct lookup_port_aux aux = {
> -        .sbrec_multicast_group_by_name_datapath
> -            = sbrec_multicast_group_by_name_datapath,
> -        .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
> -        .dp = lflow->logical_datapath
> -    };
> -    struct condition_aux cond_aux = {
> -        .sbrec_port_binding_by_name = sbrec_port_binding_by_name,
> -        .chassis = chassis,
> -        .active_tunnels = active_tunnels,
> -    };
> -    expr = expr_simplify(expr, is_chassis_resident_cb, &cond_aux);
> -    expr = expr_normalize(expr);
> -    uint32_t n_conjs = expr_to_matches(expr, lookup_port_cb, &aux,
> -                                       &matches);
> -    expr_destroy(expr);
> -
> -    if (hmap_is_empty(&matches)) {
> -        VLOG_DBG("lflow "UUID_FMT" matches are empty, skip",
> -                 UUID_ARGS(&lflow->header_.uuid));
> -        ovnacts_free(ovnacts.data, ovnacts.size);
> -        ofpbuf_uninit(&ovnacts);
> -        expr_matches_destroy(&matches);
> -        return true;
> -    }
> -
> -    /* Encode OVN logical actions into OpenFlow. */
> -    uint64_t ofpacts_stub[1024 / 8];
> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> -    struct ovnact_encode_params ep = {
> -        .lookup_port = lookup_port_cb,
> -        .aux = &aux,
> -        .is_switch = is_switch(ldp),
> -        .group_table = group_table,
> -        .meter_table = meter_table,
> -        .lflow_uuid = lflow->header_.uuid,
> -
> -        .pipeline = ingress ? OVNACT_P_INGRESS : OVNACT_P_EGRESS,
> -        .ingress_ptable = OFTABLE_LOG_INGRESS_PIPELINE,
> -        .egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE,
> -        .output_ptable = output_ptable,
> -        .mac_bind_ptable = OFTABLE_MAC_BINDING,
> -    };
> -    ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
> -    ovnacts_free(ovnacts.data, ovnacts.size);
> -    ofpbuf_uninit(&ovnacts);
> -
> -    /* Prepare the OpenFlow matches for adding to the flow table. */
> -    struct expr_match *m;
> -    HMAP_FOR_EACH (m, hmap_node, &matches) {
> -        match_set_metadata(&m->match,
> -                           htonll(lflow->logical_datapath->tunnel_key));
> -        if (m->match.wc.masks.conj_id) {
> -            m->match.flow.conj_id += *conj_id_ofs;
> -        }
> -        if (is_switch(ldp)) {
> -            unsigned int reg_index
> -                = (ingress ? MFF_LOG_INPORT : MFF_LOG_OUTPORT) - MFF_REG0;
> -            int64_t port_id = m->match.flow.regs[reg_index];
> -            if (port_id) {
> -                int64_t dp_id = lflow->logical_datapath->tunnel_key;
> -                char buf[16];
> -                snprintf(buf, sizeof(buf), "%"PRId64"_%"PRId64, dp_id,
> port_id);
> -                if (!sset_contains(local_lport_ids, buf)) {
> -                    VLOG_DBG("lflow "UUID_FMT
> -                             " port %s in match is not local, skip",
> -                             UUID_ARGS(&lflow->header_.uuid),
> -                             buf);
> -                    continue;
> -                }
> -            }
> -        }
> -        if (!m->n) {
> -            ofctrl_add_flow(flow_table, ptable, lflow->priority,
> -                            lflow->header_.uuid.parts[0], &m->match,
> &ofpacts,
> -                            &lflow->header_.uuid);
> -        } else {
> -            uint64_t conj_stubs[64 / 8];
> -            struct ofpbuf conj;
> -
> -            ofpbuf_use_stub(&conj, conj_stubs, sizeof conj_stubs);
> -            for (int i = 0; i < m->n; i++) {
> -                const struct cls_conjunction *src = &m->conjunctions[i];
> -                struct ofpact_conjunction *dst;
> -
> -                dst = ofpact_put_CONJUNCTION(&conj);
> -                dst->id = src->id + *conj_id_ofs;
> -                dst->clause = src->clause;
> -                dst->n_clauses = src->n_clauses;
> -            }
> -            ofctrl_add_flow(flow_table, ptable, lflow->priority, 0,
> &m->match,
> -                            &conj, &lflow->header_.uuid);
> -            ofpbuf_uninit(&conj);
> -        }
> -    }
> -
> -    /* Clean up. */
> -    expr_matches_destroy(&matches);
> -    ofpbuf_uninit(&ofpacts);
> -    return update_conj_id_ofs(conj_id_ofs, n_conjs);
> -}
> -
> -static void
> -put_load(const uint8_t *data, size_t len,
> -         enum mf_field_id dst, int ofs, int n_bits,
> -         struct ofpbuf *ofpacts)
> -{
> -    struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
> -                                                       mf_from_id(dst),
> NULL,
> -                                                       NULL);
> -    bitwise_copy(data, len, 0, sf->value, sf->field->n_bytes, ofs,
> n_bits);
> -    bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs,
> n_bits);
> -}
> -
> -static void
> -consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                       const struct sbrec_mac_binding *b,
> -                       struct ovn_desired_flow_table *flow_table)
> -{
> -    const struct sbrec_port_binding *pb
> -        = lport_lookup_by_name(sbrec_port_binding_by_name,
> b->logical_port);
> -    if (!pb) {
> -        return;
> -    }
> -
> -    struct eth_addr mac;
> -    if (!eth_addr_from_string(b->mac, &mac)) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -        VLOG_WARN_RL(&rl, "bad 'mac' %s", b->mac);
> -        return;
> -    }
> -
> -    struct match match = MATCH_CATCHALL_INITIALIZER;
> -    if (strchr(b->ip, '.')) {
> -        ovs_be32 ip;
> -        if (!ip_parse(b->ip, &ip)) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
> -            return;
> -        }
> -        match_set_reg(&match, 0, ntohl(ip));
> -    } else {
> -        struct in6_addr ip6;
> -        if (!ipv6_parse(b->ip, &ip6)) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
> -            return;
> -        }
> -        ovs_be128 value;
> -        memcpy(&value, &ip6, sizeof(value));
> -        match_set_xxreg(&match, 0, ntoh128(value));
> -    }
> -
> -    match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
> -    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
> -
> -    uint64_t stub[1024 / 8];
> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
> -    put_load(mac.ea, sizeof mac.ea, MFF_ETH_DST, 0, 48, &ofpacts);
> -    ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &match,
> &ofpacts,
> -                    &b->header_.uuid);
> -    ofpbuf_uninit(&ofpacts);
> -}
> -
> -/* Adds an OpenFlow flow to flow tables for each MAC binding in the OVN
> - * southbound database. */
> -static void
> -add_neighbor_flows(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                   const struct sbrec_mac_binding_table
> *mac_binding_table,
> -                   struct ovn_desired_flow_table *flow_table)
> -{
> -    const struct sbrec_mac_binding *b;
> -    SBREC_MAC_BINDING_TABLE_FOR_EACH (b, mac_binding_table) {
> -        consider_neighbor_flow(sbrec_port_binding_by_name, b, flow_table);
> -    }
> -}
> -
> -/* Handles neighbor changes in mac_binding table. */
> -void
> -lflow_handle_changed_neighbors(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_mac_binding_table *mac_binding_table,
> -    struct ovn_desired_flow_table *flow_table)
> -{
> -
> -    const struct sbrec_mac_binding *mb;
> -    /* Handle deleted mac_bindings first, to avoid *duplicated flow*
> problem
> -     * when same flow needs to be added. */
> -    SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (mb, mac_binding_table) {
> -        /* Remove any flows that should be removed. */
> -        if (sbrec_mac_binding_is_deleted(mb)) {
> -            VLOG_DBG("handle deleted mac_binding "UUID_FMT,
> -                     UUID_ARGS(&mb->header_.uuid));
> -            ofctrl_remove_flows(flow_table, &mb->header_.uuid);
> -        }
> -    }
> -    SBREC_MAC_BINDING_TABLE_FOR_EACH_TRACKED (mb, mac_binding_table) {
> -        if (!sbrec_mac_binding_is_deleted(mb)) {
> -            if (!sbrec_mac_binding_is_new(mb)) {
> -                VLOG_DBG("handle updated mac_binding "UUID_FMT,
> -                         UUID_ARGS(&mb->header_.uuid));
> -                ofctrl_remove_flows(flow_table, &mb->header_.uuid);
> -            }
> -            VLOG_DBG("handle new mac_binding "UUID_FMT,
> -                     UUID_ARGS(&mb->header_.uuid));
> -            consider_neighbor_flow(sbrec_port_binding_by_name, mb,
> flow_table);
> -        }
> -    }
> -}
> -
> -
> -/* Translates logical flows in the Logical_Flow table in the OVN_SB
> database
> - * into OpenFlow flows.  See ovn-architecture(7) for more information. */
> -void
> -lflow_run(struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -          struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -          const struct sbrec_dhcp_options_table *dhcp_options_table,
> -          const struct sbrec_dhcpv6_options_table *dhcpv6_options_table,
> -          const struct sbrec_logical_flow_table *logical_flow_table,
> -          const struct sbrec_mac_binding_table *mac_binding_table,
> -          const struct sbrec_chassis *chassis,
> -          const struct hmap *local_datapaths,
> -          const struct shash *addr_sets,
> -          const struct shash *port_groups,
> -          const struct sset *active_tunnels,
> -          const struct sset *local_lport_ids,
> -          struct ovn_desired_flow_table *flow_table,
> -          struct ovn_extend_table *group_table,
> -          struct ovn_extend_table *meter_table,
> -          struct lflow_resource_ref *lfrr,
> -          uint32_t *conj_id_ofs)
> -{
> -    COVERAGE_INC(lflow_run);
> -
> -    add_logical_flows(sbrec_multicast_group_by_name_datapath,
> -                      sbrec_port_binding_by_name, dhcp_options_table,
> -                      dhcpv6_options_table, logical_flow_table,
> -                      local_datapaths, chassis, addr_sets, port_groups,
> -                      active_tunnels, local_lport_ids, flow_table,
> group_table,
> -                      meter_table, lfrr, conj_id_ofs);
> -    add_neighbor_flows(sbrec_port_binding_by_name, mac_binding_table,
> -                       flow_table);
> -}
> -
> -void
> -lflow_destroy(void)
> -{
> -    expr_symtab_destroy(&symtab);
> -    shash_destroy(&symtab);
> -    ovn_destroy_ovnfields();
> -}
> diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
> deleted file mode 100644
> index 4e1086eb6..000000000
> --- a/ovn/controller/lflow.h
> +++ /dev/null
> @@ -1,184 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_LFLOW_H
> -#define OVN_LFLOW_H 1
> -
> -#include "ovn/logical-fields.h"
> -
> -/* Logical_Flow table translation to OpenFlow
> - * ==========================================
> - *
> - * The Logical_Flow table obtained from the OVN_Southbound database works
> in
> - * terms of logical entities, that is, logical flows among logical
> datapaths
> - * and logical ports.  This code translates these logical flows into
> OpenFlow
> - * flows that, again, work in terms of logical entities implemented
> through
> - * OpenFlow extensions (e.g. registers represent the logical input and
> output
> - * ports).
> - *
> - * Physical-to-logical and logical-to-physical translation are
> implemented in
> - * physical.[ch] as separate OpenFlow tables that run before and after,
> - * respectively, the logical pipeline OpenFlow tables.
> - */
> -
> -#include <stdint.h>
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/uuid.h"
> -#include "openvswitch/list.h"
> -
> -struct ovn_extend_table;
> -struct ovsdb_idl_index;
> -struct ovn_desired_flow_table;
> -struct hmap;
> -struct hmap_node;
> -struct sbrec_chassis;
> -struct sbrec_dhcp_options_table;
> -struct sbrec_dhcpv6_options_table;
> -struct sbrec_logical_flow_table;
> -struct sbrec_mac_binding_table;
> -struct simap;
> -struct sset;
> -struct uuid;
> -
> -/* OpenFlow table numbers.
> - *
> - * These are heavily documented in ovn-architecture(7), please update it
> if
> - * you make any changes. */
> -#define OFTABLE_PHY_TO_LOG            0
> -#define OFTABLE_LOG_INGRESS_PIPELINE  8 /* First of LOG_PIPELINE_LEN
> tables. */
> -#define OFTABLE_REMOTE_OUTPUT        32
> -#define OFTABLE_LOCAL_OUTPUT         33
> -#define OFTABLE_CHECK_LOOPBACK       34
> -#define OFTABLE_LOG_EGRESS_PIPELINE  40 /* First of LOG_PIPELINE_LEN
> tables. */
> -#define OFTABLE_SAVE_INPORT          64
> -#define OFTABLE_LOG_TO_PHY           65
> -#define OFTABLE_MAC_BINDING          66
> -
> -/* The number of tables for the ingress and egress pipelines. */
> -#define LOG_PIPELINE_LEN 24
> -
> -enum ref_type {
> -    REF_TYPE_ADDRSET,
> -    REF_TYPE_PORTGROUP
> -};
> -
> -/* Maintains the relationship for a pair of named resource and
> - * a lflow, indexed by both ref_lflow_table and lflow_ref_table. */
> -struct lflow_ref_list_node {
> -    struct ovs_list lflow_list; /* list for same lflow */
> -    struct ovs_list ref_list; /* list for same ref */
> -    enum ref_type type;
> -    char *ref_name;
> -    struct uuid lflow_uuid;
> -};
> -
> -struct ref_lflow_node {
> -    struct hmap_node node;
> -    enum ref_type type; /* key */
> -    char *ref_name; /* key */
> -    struct ovs_list ref_lflow_head;
> -};
> -
> -struct lflow_ref_node {
> -    struct hmap_node node;
> -    struct uuid lflow_uuid; /* key */
> -    struct ovs_list lflow_ref_head;
> -};
> -
> -struct lflow_resource_ref {
> -    /* A map from a referenced resource type & name (e.g. address_set AS1)
> -     * to a list of lflows that are referencing the named resource. Data
> -     * type of each node in this hmap is struct ref_lflow_node. The
> -     * ref_lflow_head in each node points to a list of
> -     * lflow_ref_list_node.ref_list. */
> -    struct hmap ref_lflow_table;
> -
> -    /* A map from a lflow uuid to a list of named resources that are
> -     * referenced by the lflow. Data type of each node in this hmap is
> -     * struct lflow_ref_node. The lflow_ref_head in each node points to
> -     * a list of lflow_ref_list_node.lflow_list. */
> -    struct hmap lflow_ref_table;
> -};
> -
> -void lflow_resource_init(struct lflow_resource_ref *);
> -void lflow_resource_destroy(struct lflow_resource_ref *);
> -void lflow_resource_clear(struct lflow_resource_ref *);
> -
> -void lflow_init(void);
> -void lflow_run(struct ovsdb_idl_index
> *sbrec_multicast_group_by_name_datapath,
> -               struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -               const struct sbrec_dhcp_options_table *,
> -               const struct sbrec_dhcpv6_options_table *,
> -               const struct sbrec_logical_flow_table *,
> -               const struct sbrec_mac_binding_table *,
> -               const struct sbrec_chassis *chassis,
> -               const struct hmap *local_datapaths,
> -               const struct shash *addr_sets,
> -               const struct shash *port_groups,
> -               const struct sset *active_tunnels,
> -               const struct sset *local_lport_ids,
> -               struct ovn_desired_flow_table *,
> -               struct ovn_extend_table *group_table,
> -               struct ovn_extend_table *meter_table,
> -               struct lflow_resource_ref *,
> -               uint32_t *conj_id_ofs);
> -
> -bool lflow_handle_changed_flows(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_dhcp_options_table *,
> -    const struct sbrec_dhcpv6_options_table *,
> -    const struct sbrec_logical_flow_table *,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *,
> -    uint32_t *conj_id_ofs);
> -
> -bool lflow_handle_changed_ref(
> -    enum ref_type,
> -    const char *ref_name,
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_dhcp_options_table *,
> -    const struct sbrec_dhcpv6_options_table *,
> -    const struct sbrec_logical_flow_table *,
> -    const struct hmap *local_datapaths,
> -    const struct sbrec_chassis *,
> -    const struct shash *addr_sets,
> -    const struct shash *port_groups,
> -    const struct sset *active_tunnels,
> -    const struct sset *local_lport_ids,
> -    struct ovn_desired_flow_table *,
> -    struct ovn_extend_table *group_table,
> -    struct ovn_extend_table *meter_table,
> -    struct lflow_resource_ref *,
> -    uint32_t *conj_id_ofs,
> -    bool *changed);
> -
> -void lflow_handle_changed_neighbors(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct sbrec_mac_binding_table *,
> -    struct ovn_desired_flow_table *);
> -
> -void lflow_destroy(void);
> -
> -#endif /* ovn/lflow.h */
> diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
> deleted file mode 100644
> index cc5c5fbb2..000000000
> --- a/ovn/controller/lport.c
> +++ /dev/null
> @@ -1,102 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "lib/sset.h"
> -#include "lport.h"
> -#include "hash.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -VLOG_DEFINE_THIS_MODULE(lport);
> -
> -const struct sbrec_port_binding *
> -lport_lookup_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                     const char *name)
> -{
> -    struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_name);
> -    sbrec_port_binding_index_set_logical_port(pb, name);
> -
> -    const struct sbrec_port_binding *retval =
> sbrec_port_binding_index_find(
> -        sbrec_port_binding_by_name, pb);
> -
> -    sbrec_port_binding_index_destroy_row(pb);
> -
> -    return retval;
> -}
> -
> -const struct sbrec_port_binding *
> -lport_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -                    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -                    uint64_t dp_key, uint64_t port_key)
> -{
> -    /* Lookup datapath corresponding to dp_key. */
> -    const struct sbrec_datapath_binding *db = datapath_lookup_by_key(
> -        sbrec_datapath_binding_by_key, dp_key);
> -    if (!db) {
> -        return NULL;
> -    }
> -
> -    /* Build key for an indexed lookup. */
> -    struct sbrec_port_binding *pb = sbrec_port_binding_index_init_row(
> -        sbrec_port_binding_by_key);
> -    sbrec_port_binding_index_set_datapath(pb, db);
> -    sbrec_port_binding_index_set_tunnel_key(pb, port_key);
> -
> -    const struct sbrec_port_binding *retval =
> sbrec_port_binding_index_find(
> -        sbrec_port_binding_by_key, pb);
> -
> -    sbrec_port_binding_index_destroy_row(pb);
> -
> -    return retval;
> -}
> -
> -const struct sbrec_datapath_binding *
> -datapath_lookup_by_key(struct ovsdb_idl_index
> *sbrec_datapath_binding_by_key,
> -                       uint64_t dp_key)
> -{
> -    struct sbrec_datapath_binding *db =
> sbrec_datapath_binding_index_init_row(
> -        sbrec_datapath_binding_by_key);
> -    sbrec_datapath_binding_index_set_tunnel_key(db, dp_key);
> -
> -    const struct sbrec_datapath_binding *retval
> -        = sbrec_datapath_binding_index_find(sbrec_datapath_binding_by_key,
> -                                            db);
> -
> -    sbrec_datapath_binding_index_destroy_row(db);
> -
> -    return retval;
> -}
> -
> -const struct sbrec_multicast_group *
> -mcgroup_lookup_by_dp_name(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    const struct sbrec_datapath_binding *db, const char *name)
> -{
> -    /* Build key for an indexed lookup. */
> -    struct sbrec_multicast_group *mc =
> sbrec_multicast_group_index_init_row(
> -        sbrec_multicast_group_by_name_datapath);
> -    sbrec_multicast_group_index_set_name(mc, name);
> -    sbrec_multicast_group_index_set_datapath(mc, db);
> -
> -    const struct sbrec_multicast_group *retval
> -        = sbrec_multicast_group_index_find(
> -            sbrec_multicast_group_by_name_datapath, mc);
> -
> -    sbrec_multicast_group_index_destroy_row(mc);
> -
> -    return retval;
> -}
> diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
> deleted file mode 100644
> index 7dcd5bee0..000000000
> --- a/ovn/controller/lport.h
> +++ /dev/null
> @@ -1,52 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_LPORT_H
> -#define OVN_LPORT_H 1
> -
> -#include <stdint.h>
> -
> -struct ovsdb_idl_index;
> -struct sbrec_chassis;
> -struct sbrec_datapath_binding;
> -struct sbrec_multicast_group;
> -struct sbrec_port_binding;
> -
> -
> -/* Database indexes.
> - * =================
> - *
> - * If the database IDL were a little smarter, it would allow us to
> directly
> - * look up data based on values of its fields.  It's not that smart
> (yet), so
> - * instead we define our own indexes.
> - */
> -
> -const struct sbrec_port_binding *lport_lookup_by_name(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const char *name);
> -
> -const struct sbrec_port_binding *lport_lookup_by_key(
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -    uint64_t dp_key, uint64_t port_key);
> -
> -const struct sbrec_datapath_binding *datapath_lookup_by_key(
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key, uint64_t
> dp_key);
> -
> -const struct sbrec_multicast_group *mcgroup_lookup_by_dp_name(
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath,
> -    const struct sbrec_datapath_binding *, const char *name);
> -
> -#endif /* ovn/lport.h */
> diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
> deleted file mode 100644
> index 043abd69d..000000000
> --- a/ovn/controller/ofctrl.c
> +++ /dev/null
> @@ -1,1393 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "bitmap.h"
> -#include "byte-order.h"
> -#include "dirs.h"
> -#include "dp-packet.h"
> -#include "flow.h"
> -#include "hash.h"
> -#include "hindex.h"
> -#include "lflow.h"
> -#include "ofctrl.h"
> -#include "openflow/openflow.h"
> -#include "openvswitch/dynamic-string.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/list.h"
> -#include "openvswitch/match.h"
> -#include "openvswitch/ofp-actions.h"
> -#include "openvswitch/ofp-flow.h"
> -#include "openvswitch/ofp-group.h"
> -#include "openvswitch/ofp-match.h"
> -#include "openvswitch/ofp-msgs.h"
> -#include "openvswitch/ofp-meter.h"
> -#include "openvswitch/ofp-packet.h"
> -#include "openvswitch/ofp-print.h"
> -#include "openvswitch/ofp-util.h"
> -#include "openvswitch/ofpbuf.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn-controller.h"
> -#include "ovn/actions.h"
> -#include "ovn/lib/extend-table.h"
> -#include "openvswitch/poll-loop.h"
> -#include "physical.h"
> -#include "openvswitch/rconn.h"
> -#include "socket-util.h"
> -#include "util.h"
> -#include "vswitch-idl.h"
> -
> -VLOG_DEFINE_THIS_MODULE(ofctrl);
> -
> -/* An OpenFlow flow. */
> -struct ovn_flow {
> -    struct hmap_node match_hmap_node; /* For match based hashing. */
> -    struct hindex_node uuid_hindex_node; /* For uuid based hashing. */
> -    struct ovs_list list_node; /* For handling lists of flows. */
> -
> -    /* Key. */
> -    uint8_t table_id;
> -    uint16_t priority;
> -    struct minimatch match;
> -
> -    /* Data. */
> -    struct uuid sb_uuid;
> -    struct ofpact *ofpacts;
> -    size_t ofpacts_len;
> -    uint64_t cookie;
> -};
> -
> -static uint32_t ovn_flow_match_hash(const struct ovn_flow *);
> -static struct ovn_flow *ovn_flow_lookup(struct hmap *flow_table,
> -                                        const struct ovn_flow *target,
> -                                        bool cmp_sb_uuid);
> -static char *ovn_flow_to_string(const struct ovn_flow *);
> -static void ovn_flow_log(const struct ovn_flow *, const char *action);
> -static void ovn_flow_destroy(struct ovn_flow *);
> -
> -/* OpenFlow connection to the switch. */
> -static struct rconn *swconn;
> -
> -/* Symbol table for OVN expressions. */
> -static struct shash symtab;
> -
> -/* Last seen sequence number for 'swconn'.  When this differs from
> - * rconn_get_connection_seqno(rconn), 'swconn' has reconnected. */
> -static unsigned int seqno;
> -
> -/* Connection state machine. */
> -#define STATES                                  \
> -    STATE(S_NEW)                                \
> -    STATE(S_TLV_TABLE_REQUESTED)                \
> -    STATE(S_TLV_TABLE_MOD_SENT)                 \
> -    STATE(S_CLEAR_FLOWS)                        \
> -    STATE(S_UPDATE_FLOWS)
> -enum ofctrl_state {
> -#define STATE(NAME) NAME,
> -    STATES
> -#undef STATE
> -};
> -
> -/* An in-flight update to the switch's flow table.
> - *
> - * When we receive a barrier reply from the switch with the given 'xid',
> we
> - * know that the switch is caught up to northbound database sequence
> number
> - * 'nb_cfg' (and make that available to the client via
> ofctrl_get_cur_cfg(), so
> - * that it can store it into our Chassis record's nb_cfg column). */
> -struct ofctrl_flow_update {
> -    struct ovs_list list_node;  /* In 'flow_updates'. */
> -    ovs_be32 xid;               /* OpenFlow transaction ID for barrier. */
> -    int64_t nb_cfg;             /* Northbound database sequence number. */
> -};
> -
> -static struct ofctrl_flow_update *
> -ofctrl_flow_update_from_list_node(const struct ovs_list *list_node)
> -{
> -    return CONTAINER_OF(list_node, struct ofctrl_flow_update, list_node);
> -}
> -
> -/* Currently in-flight updates. */
> -static struct ovs_list flow_updates;
> -
> -/* nb_cfg of latest committed flow update. */
> -static int64_t cur_cfg;
> -
> -/* Current state. */
> -static enum ofctrl_state state;
> -
> -/* Transaction IDs for messages in flight to the switch. */
> -static ovs_be32 xid, xid2;
> -
> -/* Counter for in-flight OpenFlow messages on 'swconn'.  We only send a
> new
> - * round of flow table modifications to the switch when the counter falls
> to
> - * zero, to avoid unbounded buffering. */
> -static struct rconn_packet_counter *tx_counter;
> -
> -/* Flow table of "struct ovn_flow"s, that holds the flow table currently
> - * installed in the switch. */
> -static struct hmap installed_flows;
> -
> -/* A reference to the group_table. */
> -static struct ovn_extend_table *groups;
> -
> -/* A reference to the meter_table. */
> -static struct ovn_extend_table *meters;
> -
> -/* MFF_* field ID for our Geneve option.  In S_TLV_TABLE_MOD_SENT, this is
> - * the option we requested (we don't know whether we obtained it yet).  In
> - * S_CLEAR_FLOWS or S_UPDATE_FLOWS, this is really the option we have. */
> -static enum mf_field_id mff_ovn_geneve;
> -
> -/* Indicates if flows need to be reinstalled for scenarios when ovs
> - * is restarted, even if there is no change in the desired flow table. */
> -static bool need_reinstall_flows;
> -
> -static ovs_be32 queue_msg(struct ofpbuf *);
> -
> -static struct ofpbuf *encode_flow_mod(struct ofputil_flow_mod *);
> -
> -static struct ofpbuf *encode_group_mod(const struct ofputil_group_mod *);
> -
> -static struct ofpbuf *encode_meter_mod(const struct ofputil_meter_mod *);
> -
> -static void ovn_installed_flow_table_clear(void);
> -static void ovn_installed_flow_table_destroy(void);
> -
> -static void ofctrl_recv(const struct ofp_header *, enum ofptype);
> -
> -void
> -ofctrl_init(struct ovn_extend_table *group_table,
> -            struct ovn_extend_table *meter_table,
> -            int inactivity_probe_interval)
> -{
> -    swconn = rconn_create(inactivity_probe_interval, 0,
> -                          DSCP_DEFAULT, 1 << OFP13_VERSION);
> -    tx_counter = rconn_packet_counter_create();
> -    hmap_init(&installed_flows);
> -    ovs_list_init(&flow_updates);
> -    ovn_init_symtab(&symtab);
> -    groups = group_table;
> -    meters = meter_table;
> -}
> -
> -/* S_NEW, for a new connection.
> - *
> - * Sends NXT_TLV_TABLE_REQUEST and transitions to
> - * S_TLV_TABLE_REQUESTED. */
> -
> -static void
> -run_S_NEW(void)
> -{
> -    struct ofpbuf *buf = ofpraw_alloc(OFPRAW_NXT_TLV_TABLE_REQUEST,
> -                                      rconn_get_version(swconn), 0);
> -    xid = queue_msg(buf);
> -    state = S_TLV_TABLE_REQUESTED;
> -}
> -
> -static void
> -recv_S_NEW(const struct ofp_header *oh OVS_UNUSED,
> -           enum ofptype type OVS_UNUSED,
> -           struct shash *pending_ct_zones OVS_UNUSED)
> -{
> -    OVS_NOT_REACHED();
> -}
> -
> -/* S_TLV_TABLE_REQUESTED, when NXT_TLV_TABLE_REQUEST has been sent
> - * and we're waiting for a reply.
> - *
> - * If we receive an NXT_TLV_TABLE_REPLY:
> - *
> - *     - If it contains our tunnel metadata option, assign its field ID to
> - *       mff_ovn_geneve and transition to S_CLEAR_FLOWS.
> - *
> - *     - Otherwise, if there is an unused tunnel metadata field ID, send
> - *       NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST, and transition to
> - *       S_TLV_TABLE_MOD_SENT.
> - *
> - *     - Otherwise, log an error, disable Geneve, and transition to
> - *       S_CLEAR_FLOWS.
> - *
> - * If we receive an OFPT_ERROR:
> - *
> - *     - Log an error, disable Geneve, and transition to S_CLEAR_FLOWS. */
> -
> -static void
> -run_S_TLV_TABLE_REQUESTED(void)
> -{
> -}
> -
> -static bool
> -process_tlv_table_reply(const struct ofputil_tlv_table_reply *reply)
> -{
> -    const struct ofputil_tlv_map *map;
> -    uint64_t md_free = UINT64_MAX;
> -    BUILD_ASSERT(TUN_METADATA_NUM_OPTS == 64);
> -
> -    LIST_FOR_EACH (map, list_node, &reply->mappings) {
> -        if (map->option_class == OVN_GENEVE_CLASS
> -            && map->option_type == OVN_GENEVE_TYPE
> -            && map->option_len == OVN_GENEVE_LEN) {
> -            if (map->index >= TUN_METADATA_NUM_OPTS) {
> -                VLOG_ERR("desired Geneve tunnel option 0x%"PRIx16","
> -                         "%"PRIu8",%"PRIu8" already in use with "
> -                         "unsupported index %"PRIu16,
> -                         map->option_class, map->option_type,
> -                         map->option_len, map->index);
> -                return false;
> -            } else {
> -                mff_ovn_geneve = MFF_TUN_METADATA0 + map->index;
> -                state = S_CLEAR_FLOWS;
> -                return true;
> -            }
> -        }
> -
> -        if (map->index < TUN_METADATA_NUM_OPTS) {
> -            md_free &= ~(UINT64_C(1) << map->index);
> -        }
> -    }
> -
> -    VLOG_DBG("OVN Geneve option not found");
> -    if (!md_free) {
> -        VLOG_ERR("no Geneve options free for use by OVN");
> -        return false;
> -    }
> -
> -    unsigned int index = rightmost_1bit_idx(md_free);
> -    mff_ovn_geneve = MFF_TUN_METADATA0 + index;
> -    struct ofputil_tlv_map tm;
> -    tm.option_class = OVN_GENEVE_CLASS;
> -    tm.option_type = OVN_GENEVE_TYPE;
> -    tm.option_len = OVN_GENEVE_LEN;
> -    tm.index = index;
> -
> -    struct ofputil_tlv_table_mod ttm;
> -    ttm.command = NXTTMC_ADD;
> -    ovs_list_init(&ttm.mappings);
> -    ovs_list_push_back(&ttm.mappings, &tm.list_node);
> -
> -    xid = queue_msg(ofputil_encode_tlv_table_mod(OFP13_VERSION, &ttm));
> -    xid2 = queue_msg(ofputil_encode_barrier_request(OFP13_VERSION));
> -    state = S_TLV_TABLE_MOD_SENT;
> -
> -    return true;
> -}
> -
> -static void
> -recv_S_TLV_TABLE_REQUESTED(const struct ofp_header *oh, enum ofptype type,
> -                           struct shash *pending_ct_zones OVS_UNUSED)
> -{
> -    if (oh->xid != xid) {
> -        ofctrl_recv(oh, type);
> -        return;
> -    } else if (type == OFPTYPE_NXT_TLV_TABLE_REPLY) {
> -        struct ofputil_tlv_table_reply reply;
> -        enum ofperr error = ofputil_decode_tlv_table_reply(oh, &reply);
> -        if (!error) {
> -            bool ok = process_tlv_table_reply(&reply);
> -            ofputil_uninit_tlv_table(&reply.mappings);
> -            if (ok) {
> -                return;
> -            }
> -        } else {
> -            VLOG_ERR("failed to decode TLV table request (%s)",
> -                     ofperr_to_string(error));
> -        }
> -    } else if (type == OFPTYPE_ERROR) {
> -        VLOG_ERR("switch refused to allocate Geneve option (%s)",
> -                 ofperr_to_string(ofperr_decode_msg(oh, NULL)));
> -    } else {
> -        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
> -        VLOG_ERR("unexpected reply to TLV table request (%s)", s);
> -        free(s);
> -    }
> -
> -    /* Error path. */
> -    mff_ovn_geneve = 0;
> -    state = S_CLEAR_FLOWS;
> -}
> -
> -/* S_TLV_TABLE_MOD_SENT, when NXT_TLV_TABLE_MOD and OFPT_BARRIER_REQUEST
> - * have been sent and we're waiting for a reply to one or the other.
> - *
> - * If we receive an OFPT_ERROR:
> - *
> - *     - If the error is NXTTMFC_ALREADY_MAPPED or NXTTMFC_DUP_ENTRY, we
> - *       raced with some other controller.  Transition to S_NEW.
> - *
> - *     - Otherwise, log an error, disable Geneve, and transition to
> - *       S_CLEAR_FLOWS.
> - *
> - * If we receive OFPT_BARRIER_REPLY:
> - *
> - *     - Set the tunnel metadata field ID to the one that we requested.
> - *       Transition to S_CLEAR_FLOWS.
> - */
> -
> -static void
> -run_S_TLV_TABLE_MOD_SENT(void)
> -{
> -}
> -
> -static void
> -recv_S_TLV_TABLE_MOD_SENT(const struct ofp_header *oh, enum ofptype type,
> -                          struct shash *pending_ct_zones OVS_UNUSED)
> -{
> -    if (oh->xid != xid && oh->xid != xid2) {
> -        ofctrl_recv(oh, type);
> -    } else if (oh->xid == xid2 && type == OFPTYPE_BARRIER_REPLY) {
> -        state = S_CLEAR_FLOWS;
> -    } else if (oh->xid == xid && type == OFPTYPE_ERROR) {
> -        enum ofperr error = ofperr_decode_msg(oh, NULL);
> -        if (error == OFPERR_NXTTMFC_ALREADY_MAPPED ||
> -            error == OFPERR_NXTTMFC_DUP_ENTRY) {
> -            VLOG_INFO("raced with another controller adding "
> -                      "Geneve option (%s); trying again",
> -                      ofperr_to_string(error));
> -            state = S_NEW;
> -        } else {
> -            VLOG_ERR("error adding Geneve option (%s)",
> -                     ofperr_to_string(error));
> -            goto error;
> -        }
> -    } else {
> -        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 1);
> -        VLOG_ERR("unexpected reply to Geneve option allocation request
> (%s)",
> -                 s);
> -        free(s);
> -        goto error;
> -    }
> -    return;
> -
> -error:
> -    state = S_CLEAR_FLOWS;
> -}
> -
> -/* S_CLEAR_FLOWS, after we've established a Geneve metadata field ID and
> it's
> - * time to set up some flows.
> - *
> - * Sends an OFPT_TABLE_MOD to clear all flows, then transitions to
> - * S_UPDATE_FLOWS. */
> -
> -static void
> -run_S_CLEAR_FLOWS(void)
> -{
> -    VLOG_DBG("clearing all flows");
> -
> -    need_reinstall_flows = true;
> -    /* Send a flow_mod to delete all flows. */
> -    struct ofputil_flow_mod fm = {
> -        .table_id = OFPTT_ALL,
> -        .command = OFPFC_DELETE,
> -    };
> -    minimatch_init_catchall(&fm.match);
> -    queue_msg(encode_flow_mod(&fm));
> -    minimatch_destroy(&fm.match);
> -
> -    /* Send a group_mod to delete all groups. */
> -    struct ofputil_group_mod gm;
> -    memset(&gm, 0, sizeof gm);
> -    gm.command = OFPGC11_DELETE;
> -    gm.group_id = OFPG_ALL;
> -    gm.command_bucket_id = OFPG15_BUCKET_ALL;
> -    ovs_list_init(&gm.buckets);
> -    queue_msg(encode_group_mod(&gm));
> -    ofputil_uninit_group_mod(&gm);
> -
> -    /* Clear installed_flows, to match the state of the switch. */
> -    ovn_installed_flow_table_clear();
> -
> -    /* Clear existing groups, to match the state of the switch. */
> -    if (groups) {
> -        ovn_extend_table_clear(groups, true);
> -    }
> -
> -    /* Send a meter_mod to delete all meters. */
> -    struct ofputil_meter_mod mm;
> -    memset(&mm, 0, sizeof mm);
> -    mm.command = OFPMC13_DELETE;
> -    mm.meter.meter_id = OFPM13_ALL;
> -    queue_msg(encode_meter_mod(&mm));
> -
> -    /* Clear existing meters, to match the state of the switch. */
> -    if (meters) {
> -        ovn_extend_table_clear(meters, true);
> -    }
> -
> -    /* All flow updates are irrelevant now. */
> -    struct ofctrl_flow_update *fup, *next;
> -    LIST_FOR_EACH_SAFE (fup, next, list_node, &flow_updates) {
> -        ovs_list_remove(&fup->list_node);
> -        free(fup);
> -    }
> -
> -    state = S_UPDATE_FLOWS;
> -}
> -
> -static void
> -recv_S_CLEAR_FLOWS(const struct ofp_header *oh, enum ofptype type,
> -                   struct shash *pending_ct_zones OVS_UNUSED)
> -{
> -    ofctrl_recv(oh, type);
> -}
> -
> -/* S_UPDATE_FLOWS, for maintaining the flow table over time.
> - *
> - * Compare the installed flows to the ones we want.  Send OFPT_FLOW_MOD as
> - * necessary.
> - *
> - * This is a terminal state.  We only transition out of it if the
> connection
> - * drops. */
> -
> -static void
> -run_S_UPDATE_FLOWS(void)
> -{
> -    /* Nothing to do here.
> -     *
> -     * Being in this state enables ofctrl_put() to work, however. */
> -}
> -
> -static void
> -recv_S_UPDATE_FLOWS(const struct ofp_header *oh, enum ofptype type,
> -                    struct shash *pending_ct_zones)
> -{
> -    if (type == OFPTYPE_BARRIER_REPLY &&
> !ovs_list_is_empty(&flow_updates)) {
> -        struct ofctrl_flow_update *fup =
> ofctrl_flow_update_from_list_node(
> -            ovs_list_front(&flow_updates));
> -        if (fup->xid == oh->xid) {
> -            if (fup->nb_cfg >= cur_cfg) {
> -                cur_cfg = fup->nb_cfg;
> -            }
> -            ovs_list_remove(&fup->list_node);
> -            free(fup);
> -        }
> -
> -        /* If the barrier xid is associated with an outstanding conntrack
> -         * flush, the flush succeeded.  Move the pending ct zone entry
> -         * to the next stage. */
> -        struct shash_node *iter;
> -        SHASH_FOR_EACH(iter, pending_ct_zones) {
> -            struct ct_zone_pending_entry *ctzpe = iter->data;
> -            if (ctzpe->state == CT_ZONE_OF_SENT && ctzpe->of_xid ==
> oh->xid) {
> -                ctzpe->state = CT_ZONE_DB_QUEUED;
> -            }
> -        }
> -    } else {
> -        ofctrl_recv(oh, type);
> -    }
> -}
> -
> -
> -enum mf_field_id
> -ofctrl_get_mf_field_id(void)
> -{
> -    if (!rconn_is_connected(swconn)) {
> -        return 0;
> -    }
> -    return (state == S_CLEAR_FLOWS || state == S_UPDATE_FLOWS
> -            ? mff_ovn_geneve : 0);
> -}
> -
> -/* Runs the OpenFlow state machine against 'br_int', which is local to the
> - * hypervisor on which we are running.  Attempts to negotiate a Geneve
> option
> - * field for class OVN_GENEVE_CLASS, type OVN_GENEVE_TYPE. */
> -void
> -ofctrl_run(const struct ovsrec_bridge *br_int, struct shash
> *pending_ct_zones)
> -{
> -    char *target = xasprintf("unix:%s/%s.mgmt", ovs_rundir(),
> br_int->name);
> -    if (strcmp(target, rconn_get_target(swconn))) {
> -        VLOG_INFO("%s: connecting to switch", target);
> -        rconn_connect(swconn, target, target);
> -    }
> -    free(target);
> -
> -    rconn_run(swconn);
> -
> -    if (!rconn_is_connected(swconn)) {
> -        return;
> -    }
> -    if (seqno != rconn_get_connection_seqno(swconn)) {
> -        seqno = rconn_get_connection_seqno(swconn);
> -        state = S_NEW;
> -
> -        /* Reset the state of any outstanding ct flushes to resend them.
> */
> -        struct shash_node *iter;
> -        SHASH_FOR_EACH(iter, pending_ct_zones) {
> -            struct ct_zone_pending_entry *ctzpe = iter->data;
> -            if (ctzpe->state == CT_ZONE_OF_SENT) {
> -                ctzpe->state = CT_ZONE_OF_QUEUED;
> -            }
> -        }
> -    }
> -
> -    bool progress = true;
> -    for (int i = 0; progress && i < 50; i++) {
> -        /* Allow the state machine to run. */
> -        enum ofctrl_state old_state = state;
> -        switch (state) {
> -#define STATE(NAME) case NAME: run_##NAME(); break;
> -            STATES
> -#undef STATE
> -        default:
> -            OVS_NOT_REACHED();
> -        }
> -
> -        /* Try to process a received packet. */
> -        struct ofpbuf *msg = rconn_recv(swconn);
> -        if (msg) {
> -            const struct ofp_header *oh = msg->data;
> -            enum ofptype type;
> -            enum ofperr error;
> -
> -            error = ofptype_decode(&type, oh);
> -            if (!error) {
> -                switch (state) {
> -#define STATE(NAME) case NAME: recv_##NAME(oh, type, pending_ct_zones);
> break;
> -                    STATES
> -#undef STATE
> -                default:
> -                    OVS_NOT_REACHED();
> -                }
> -            } else {
> -                char *s = ofp_to_string(oh, ntohs(oh->length), NULL,
> NULL, 1);
> -                VLOG_WARN("could not decode OpenFlow message (%s): %s",
> -                          ofperr_to_string(error), s);
> -                free(s);
> -            }
> -
> -            ofpbuf_delete(msg);
> -        }
> -
> -        /* If we did some work, plan to go around again. */
> -        progress = old_state != state || msg;
> -    }
> -    if (progress) {
> -        /* We bailed out to limit the amount of work we do in one go, to
> allow
> -         * other code a chance to run.  We were still making progress at
> that
> -         * point, so ensure that we come back again without waiting. */
> -        poll_immediate_wake();
> -    }
> -}
> -
> -void
> -ofctrl_wait(void)
> -{
> -    rconn_run_wait(swconn);
> -    rconn_recv_wait(swconn);
> -}
> -
> -void
> -ofctrl_destroy(void)
> -{
> -    rconn_destroy(swconn);
> -    ovn_installed_flow_table_destroy();
> -    rconn_packet_counter_destroy(tx_counter);
> -    expr_symtab_destroy(&symtab);
> -    shash_destroy(&symtab);
> -}
> -
> -int64_t
> -ofctrl_get_cur_cfg(void)
> -{
> -    return cur_cfg;
> -}
> -
> -static ovs_be32
> -queue_msg(struct ofpbuf *msg)
> -{
> -    const struct ofp_header *oh = msg->data;
> -    ovs_be32 xid_ = oh->xid;
> -    rconn_send(swconn, msg, tx_counter);
> -    return xid_;
> -}
> -
> -static void
> -log_openflow_rl(struct vlog_rate_limit *rl, enum vlog_level level,
> -                const struct ofp_header *oh, const char *title)
> -{
> -    if (!vlog_should_drop(&this_module, level, rl)) {
> -        char *s = ofp_to_string(oh, ntohs(oh->length), NULL, NULL, 2);
> -        vlog(&this_module, level, "%s: %s", title, s);
> -        free(s);
> -    }
> -}
> -
> -static void
> -ofctrl_recv(const struct ofp_header *oh, enum ofptype type)
> -{
> -    if (type == OFPTYPE_ECHO_REQUEST) {
> -        queue_msg(ofputil_encode_echo_reply(oh));
> -    } else if (type == OFPTYPE_ERROR) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
> -        log_openflow_rl(&rl, VLL_INFO, oh, "OpenFlow error");
> -    } else {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(30, 300);
> -        log_openflow_rl(&rl, VLL_DBG, oh, "OpenFlow packet ignored");
> -    }
> -}
> -
> -/* Flow table interfaces to the rest of ovn-controller. */
> -
> -/* Adds a flow to 'desired_flows' with the specified 'match' and
> 'actions' to
> - * the OpenFlow table numbered 'table_id' with the given 'priority' and
> - * OpenFlow 'cookie'.  The caller retains ownership of 'match' and
> 'actions'.
> - *
> - * The flow is also added to a hash index based on sb_uuid.
> - *
> - * This just assembles the desired flow table in memory.  Nothing is
> actually
> - * sent to the switch until a later call to ofctrl_put().
> - *
> - * The caller should initialize its own hmap to hold the flows. */
> -void
> -ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table,
> -                          uint8_t table_id, uint16_t priority,
> -                          uint64_t cookie, const struct match *match,
> -                          const struct ofpbuf *actions,
> -                          const struct uuid *sb_uuid,
> -                          bool log_duplicate_flow)
> -{
> -    struct ovn_flow *f = xmalloc(sizeof *f);
> -    f->table_id = table_id;
> -    f->priority = priority;
> -    minimatch_init(&f->match, match);
> -    f->ofpacts = xmemdup(actions->data, actions->size);
> -    f->ofpacts_len = actions->size;
> -    f->sb_uuid = *sb_uuid;
> -    f->match_hmap_node.hash = ovn_flow_match_hash(f);
> -    f->uuid_hindex_node.hash = uuid_hash(&f->sb_uuid);
> -    f->cookie = cookie;
> -
> -    ovn_flow_log(f, "ofctrl_add_flow");
> -
> -    if (ovn_flow_lookup(&flow_table->match_flow_table, f, true)) {
> -        if (log_duplicate_flow) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> -            if (!VLOG_DROP_DBG(&rl)) {
> -                char *s = ovn_flow_to_string(f);
> -                VLOG_DBG("dropping duplicate flow: %s", s);
> -                free(s);
> -            }
> -        }
> -        ovn_flow_destroy(f);
> -        return;
> -    }
> -
> -    hmap_insert(&flow_table->match_flow_table, &f->match_hmap_node,
> -                f->match_hmap_node.hash);
> -    hindex_insert(&flow_table->uuid_flow_table, &f->uuid_hindex_node,
> -                  f->uuid_hindex_node.hash);
> -}
> -
> -/* Removes a bundles of flows from the flow table. */
> -void
> -ofctrl_remove_flows(struct ovn_desired_flow_table *flow_table,
> -                    const struct uuid *sb_uuid)
> -{
> -    struct ovn_flow *f, *next;
> -    HINDEX_FOR_EACH_WITH_HASH_SAFE (f, next, uuid_hindex_node,
> -                                    uuid_hash(sb_uuid),
> -                                    &flow_table->uuid_flow_table) {
> -        if (uuid_equals(&f->sb_uuid, sb_uuid)) {
> -            ovn_flow_log(f, "ofctrl_remove_flow");
> -            hmap_remove(&flow_table->match_flow_table,
> -                        &f->match_hmap_node);
> -            hindex_remove(&flow_table->uuid_flow_table,
> &f->uuid_hindex_node);
> -            ovn_flow_destroy(f);
> -        }
> -    }
> -
> -    /* remove any related group and meter info */
> -    ovn_extend_table_remove_desired(groups, sb_uuid);
> -    ovn_extend_table_remove_desired(meters, sb_uuid);
> -}
> -
> -void
> -ofctrl_add_flow(struct ovn_desired_flow_table *desired_flows,
> -                uint8_t table_id, uint16_t priority, uint64_t cookie,
> -                const struct match *match, const struct ofpbuf *actions,
> -                const struct uuid *sb_uuid)
> -{
> -    ofctrl_check_and_add_flow(desired_flows, table_id, priority, cookie,
> -                              match, actions, sb_uuid, true);
> -}
> -
> -/* ovn_flow. */
> -
> -/* Returns a hash of the match key in 'f'. */
> -static uint32_t
> -ovn_flow_match_hash(const struct ovn_flow *f)
> -{
> -    return hash_2words((f->table_id << 16) | f->priority,
> -                       minimatch_hash(&f->match, 0));
> -}
> -
> -/* Duplicate an ovn_flow structure. */
> -struct ovn_flow *
> -ofctrl_dup_flow(struct ovn_flow *src)
> -{
> -    struct ovn_flow *dst = xmalloc(sizeof *dst);
> -    dst->table_id = src->table_id;
> -    dst->priority = src->priority;
> -    minimatch_clone(&dst->match, &src->match);
> -    dst->ofpacts = xmemdup(src->ofpacts, src->ofpacts_len);
> -    dst->ofpacts_len = src->ofpacts_len;
> -    dst->sb_uuid = src->sb_uuid;
> -    dst->match_hmap_node.hash = ovn_flow_match_hash(dst);
> -    dst->uuid_hindex_node.hash = uuid_hash(&src->sb_uuid);
> -    return dst;
> -}
> -
> -/* Finds and returns an ovn_flow in 'flow_table' whose key is identical to
> - * 'target''s key, or NULL if there is none. */
> -static struct ovn_flow *
> -ovn_flow_lookup(struct hmap *flow_table, const struct ovn_flow *target,
> -                bool cmp_sb_uuid)
> -{
> -    struct ovn_flow *f;
> -
> -    HMAP_FOR_EACH_WITH_HASH (f, match_hmap_node,
> target->match_hmap_node.hash,
> -                             flow_table) {
> -        if (f->table_id == target->table_id
> -            && f->priority == target->priority
> -            && minimatch_equal(&f->match, &target->match)) {
> -            if (!cmp_sb_uuid || uuid_equals(&target->sb_uuid,
> &f->sb_uuid)) {
> -                return f;
> -            }
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static char *
> -ovn_flow_to_string(const struct ovn_flow *f)
> -{
> -    struct ds s = DS_EMPTY_INITIALIZER;
> -    ds_put_format(&s, "sb_uuid="UUID_FMT", ", UUID_ARGS(&f->sb_uuid));
> -    ds_put_format(&s, "table_id=%"PRIu8", ", f->table_id);
> -    ds_put_format(&s, "priority=%"PRIu16", ", f->priority);
> -    minimatch_format(&f->match, NULL, NULL, &s, OFP_DEFAULT_PRIORITY);
> -    ds_put_cstr(&s, ", actions=");
> -    struct ofpact_format_params fp = { .s = &s };
> -    ofpacts_format(f->ofpacts, f->ofpacts_len, &fp);
> -    return ds_steal_cstr(&s);
> -}
> -
> -static void
> -ovn_flow_log(const struct ovn_flow *f, const char *action)
> -{
> -    if (VLOG_IS_DBG_ENABLED()) {
> -        char *s = ovn_flow_to_string(f);
> -        VLOG_DBG("%s flow: %s", action, s);
> -        free(s);
> -    }
> -}
> -
> -static void
> -ovn_flow_destroy(struct ovn_flow *f)
> -{
> -    if (f) {
> -        minimatch_destroy(&f->match);
> -        free(f->ofpacts);
> -        free(f);
> -    }
> -}
> -
> -/* Flow tables of struct ovn_flow. */
> -void
> -ovn_desired_flow_table_init(struct ovn_desired_flow_table *flow_table)
> -{
> -    hmap_init(&flow_table->match_flow_table);
> -    hindex_init(&flow_table->uuid_flow_table);
> -}
> -
> -void
> -ovn_desired_flow_table_clear(struct ovn_desired_flow_table *flow_table)
> -{
> -    struct ovn_flow *f, *next;
> -    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node,
> -                        &flow_table->match_flow_table) {
> -        hmap_remove(&flow_table->match_flow_table, &f->match_hmap_node);
> -        hindex_remove(&flow_table->uuid_flow_table, &f->uuid_hindex_node);
> -        ovn_flow_destroy(f);
> -    }
> -}
> -
> -void
> -ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *flow_table)
> -{
> -    ovn_desired_flow_table_clear(flow_table);
> -    hmap_destroy(&flow_table->match_flow_table);
> -    hindex_destroy(&flow_table->uuid_flow_table);
> -}
> -
> -static void
> -ovn_installed_flow_table_clear(void)
> -{
> -    struct ovn_flow *f, *next;
> -    HMAP_FOR_EACH_SAFE (f, next, match_hmap_node, &installed_flows) {
> -        hmap_remove(&installed_flows, &f->match_hmap_node);
> -        ovn_flow_destroy(f);
> -    }
> -}
> -
> -static void
> -ovn_installed_flow_table_destroy(void)
> -{
> -    ovn_installed_flow_table_clear();
> -    hmap_destroy(&installed_flows);
> -}
> -
> -/* Flow table update. */
> -
> -static struct ofpbuf *
> -encode_flow_mod(struct ofputil_flow_mod *fm)
> -{
> -    fm->buffer_id = UINT32_MAX;
> -    fm->out_port = OFPP_ANY;
> -    fm->out_group = OFPG_ANY;
> -    return ofputil_encode_flow_mod(fm, OFPUTIL_P_OF13_OXM);
> -}
> -
> -static void
> -add_flow_mod(struct ofputil_flow_mod *fm, struct ovs_list *msgs)
> -{
> -    struct ofpbuf *msg = encode_flow_mod(fm);
> -    ovs_list_push_back(msgs, &msg->list_node);
> -}
> -
> -/* group_table. */
> -
> -static struct ofpbuf *
> -encode_group_mod(const struct ofputil_group_mod *gm)
> -{
> -    return ofputil_encode_group_mod(OFP13_VERSION, gm, NULL, -1);
> -}
> -
> -static void
> -add_group_mod(const struct ofputil_group_mod *gm, struct ovs_list *msgs)
> -{
> -    struct ofpbuf *msg = encode_group_mod(gm);
> -    ovs_list_push_back(msgs, &msg->list_node);
> -}
> -
> -
> -static struct ofpbuf *
> -encode_meter_mod(const struct ofputil_meter_mod *mm)
> -{
> -    return ofputil_encode_meter_mod(OFP13_VERSION, mm);
> -}
> -
> -static void
> -add_meter_mod(const struct ofputil_meter_mod *mm, struct ovs_list *msgs)
> -{
> -    struct ofpbuf *msg = encode_meter_mod(mm);
> -    ovs_list_push_back(msgs, &msg->list_node);
> -}
> -
> -static void
> -add_ct_flush_zone(uint16_t zone_id, struct ovs_list *msgs)
> -{
> -    struct ofpbuf *msg = ofpraw_alloc(OFPRAW_NXT_CT_FLUSH_ZONE,
> -                                      rconn_get_version(swconn), 0);
> -    struct nx_zone_id *nzi = ofpbuf_put_zeros(msg, sizeof *nzi);
> -    nzi->zone_id = htons(zone_id);
> -
> -    ovs_list_push_back(msgs, &msg->list_node);
> -}
> -
> -static void
> -add_meter_string(struct ovn_extend_table_info *m_desired,
> -                 struct ovs_list *msgs)
> -{
> -    /* Create and install new meter. */
> -    struct ofputil_meter_mod mm;
> -    enum ofputil_protocol usable_protocols;
> -    char *meter_string = xasprintf("meter=%"PRIu32",%s",
> -                                   m_desired->table_id,
> -                                   &m_desired->name[9]);
> -    char *error = parse_ofp_meter_mod_str(&mm, meter_string, OFPMC13_ADD,
> -                                          &usable_protocols);
> -    if (!error) {
> -        add_meter_mod(&mm, msgs);
> -    } else {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -        VLOG_ERR_RL(&rl, "new meter %s %s", error, meter_string);
> -        free(error);
> -    }
> -    free(meter_string);
> -}
> -
> -static void
> -add_meter(struct ovn_extend_table_info *m_desired,
> -          const struct sbrec_meter_table *meter_table,
> -          struct ovs_list *msgs)
> -{
> -    const struct sbrec_meter *sb_meter;
> -    SBREC_METER_TABLE_FOR_EACH (sb_meter, meter_table) {
> -        if (!strcmp(m_desired->name, sb_meter->name)) {
> -            break;
> -        }
> -    }
> -
> -    if (!sb_meter) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -        VLOG_ERR_RL(&rl, "could not find meter named \"%s\"",
> m_desired->name);
> -        return;
> -    }
> -
> -    struct ofputil_meter_mod mm;
> -    mm.command = OFPMC13_ADD;
> -    mm.meter.meter_id = m_desired->table_id;
> -    mm.meter.flags = OFPMF13_STATS;
> -
> -    if (!strcmp(sb_meter->unit, "pktps")) {
> -        mm.meter.flags |= OFPMF13_PKTPS;
> -    } else {
> -        mm.meter.flags |= OFPMF13_KBPS;
> -    }
> -
> -    mm.meter.n_bands = sb_meter->n_bands;
> -    mm.meter.bands = xcalloc(mm.meter.n_bands, sizeof *mm.meter.bands);
> -
> -    for (size_t i = 0; i < sb_meter->n_bands; i++) {
> -        struct sbrec_meter_band *sb_band = sb_meter->bands[i];
> -        struct ofputil_meter_band *mm_band = &mm.meter.bands[i];
> -
> -        if (!strcmp(sb_band->action, "drop")) {
> -            mm_band->type = OFPMBT13_DROP;
> -        }
> -
> -        mm_band->prec_level = 0;
> -        mm_band->rate = sb_band->rate;
> -        mm_band->burst_size = sb_band->burst_size;
> -
> -        if (mm_band->burst_size) {
> -            mm.meter.flags |= OFPMF13_BURST;
> -        }
> -    }
> -
> -    add_meter_mod(&mm, msgs);
> -    free(mm.meter.bands);
> -}
> -
> -/* The flow table can be updated if the connection to the switch is up and
> - * in the correct state and not backlogged with existing flow_mods.  (Our
> - * criteria for being backlogged appear very conservative, but the socket
> - * between ovn-controller and OVS provides some buffering.) */
> -static bool
> -ofctrl_can_put(void)
> -{
> -    if (state != S_UPDATE_FLOWS
> -        || rconn_packet_counter_n_packets(tx_counter)
> -        || rconn_get_version(swconn) < 0) {
> -        return false;
> -    }
> -    return true;
> -}
> -
> -/* Replaces the flow table on the switch, if possible, by the flows added
> - * with ofctrl_add_flow().
> - *
> - * Replaces the group table and meter table on the switch, if possible,
> - * by the contents of '->desired'.
> - *
> - * Sends conntrack flush messages to each zone in 'pending_ct_zones' that
> - * is in the CT_ZONE_OF_QUEUED state and then moves the zone into the
> - * CT_ZONE_OF_SENT state.
> - *
> - * This should be called after ofctrl_run() within the main loop. */
> -void
> -ofctrl_put(struct ovn_desired_flow_table *flow_table,
> -           struct shash *pending_ct_zones,
> -           const struct sbrec_meter_table *meter_table,
> -           int64_t nb_cfg,
> -           bool flow_changed)
> -{
> -    static bool skipped_last_time = false;
> -    static int64_t old_nb_cfg = 0;
> -    bool need_put = false;
> -    if (flow_changed || skipped_last_time || need_reinstall_flows) {
> -        need_put = true;
> -    } else if (nb_cfg != old_nb_cfg) {
> -        /* nb_cfg changed since last ofctrl_put() call */
> -        if (cur_cfg == old_nb_cfg) {
> -            /* we were up-to-date already, so just update with the
> -             * new nb_cfg */
> -            cur_cfg = nb_cfg;
> -        } else {
> -            need_put = true;
> -        }
> -    }
> -
> -    old_nb_cfg = nb_cfg;
> -
> -    if (!need_put) {
> -        VLOG_DBG("ofctrl_put not needed");
> -        return;
> -    }
> -    if (!ofctrl_can_put()) {
> -        VLOG_DBG("ofctrl_put can't be performed");
> -        skipped_last_time = true;
> -        return;
> -    }
> -
> -    skipped_last_time = false;
> -    need_reinstall_flows = false;
> -
> -    /* OpenFlow messages to send to the switch to bring it up-to-date. */
> -    struct ovs_list msgs = OVS_LIST_INITIALIZER(&msgs);
> -
> -    /* Iterate through ct zones that need to be flushed. */
> -    struct shash_node *iter;
> -    SHASH_FOR_EACH(iter, pending_ct_zones) {
> -        struct ct_zone_pending_entry *ctzpe = iter->data;
> -        if (ctzpe->state == CT_ZONE_OF_QUEUED) {
> -            add_ct_flush_zone(ctzpe->zone, &msgs);
> -            ctzpe->state = CT_ZONE_OF_SENT;
> -            ctzpe->of_xid = 0;
> -        }
> -    }
> -
> -    /* Iterate through all the desired groups. If there are new ones,
> -     * add them to the switch. */
> -    struct ovn_extend_table_info *desired;
> -    EXTEND_TABLE_FOR_EACH_UNINSTALLED (desired, groups) {
> -        /* Create and install new group. */
> -        struct ofputil_group_mod gm;
> -        enum ofputil_protocol usable_protocols;
> -        char *group_string = xasprintf("group_id=%"PRIu32",%s",
> -                                       desired->table_id,
> -                                       desired->name);
> -        char *error = parse_ofp_group_mod_str(&gm, OFPGC11_ADD,
> group_string,
> -                                              NULL, NULL,
> &usable_protocols);
> -        if (!error) {
> -            add_group_mod(&gm, &msgs);
> -        } else {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_ERR_RL(&rl, "new group %s %s", error, group_string);
> -            free(error);
> -        }
> -        free(group_string);
> -        ofputil_uninit_group_mod(&gm);
> -    }
> -
> -    /* Iterate through all the desired meters. If there are new ones,
> -     * add them to the switch. */
> -    struct ovn_extend_table_info *m_desired;
> -    EXTEND_TABLE_FOR_EACH_UNINSTALLED (m_desired, meters) {
> -        if (!strncmp(m_desired->name, "__string: ", 10)) {
> -            /* The "set-meter" action creates a meter entry name that
> -             * describes the meter itself. */
> -            add_meter_string(m_desired, &msgs);
> -        } else {
> -            add_meter(m_desired, meter_table, &msgs);
> -        }
> -    }
> -
> -    /* Iterate through all of the installed flows.  If any of them are no
> -     * longer desired, delete them; if any of them should have different
> -     * actions, update them. */
> -    struct ovn_flow *i, *next;
> -    HMAP_FOR_EACH_SAFE (i, next, match_hmap_node, &installed_flows) {
> -        struct ovn_flow *d =
> ovn_flow_lookup(&flow_table->match_flow_table,
> -                                             i, false);
> -        if (!d) {
> -            /* Installed flow is no longer desirable.  Delete it from the
> -             * switch and from installed_flows. */
> -            struct ofputil_flow_mod fm = {
> -                .match = i->match,
> -                .priority = i->priority,
> -                .table_id = i->table_id,
> -                .command = OFPFC_DELETE_STRICT,
> -            };
> -            add_flow_mod(&fm, &msgs);
> -            ovn_flow_log(i, "removing installed");
> -
> -            hmap_remove(&installed_flows, &i->match_hmap_node);
> -            ovn_flow_destroy(i);
> -        } else {
> -            if (!uuid_equals(&i->sb_uuid, &d->sb_uuid)) {
> -                /* Update installed flow's UUID. */
> -                i->sb_uuid = d->sb_uuid;
> -            }
> -            if (!ofpacts_equal(i->ofpacts, i->ofpacts_len,
> -                               d->ofpacts, d->ofpacts_len)) {
> -                /* Update actions in installed flow. */
> -                struct ofputil_flow_mod fm = {
> -                    .match = i->match,
> -                    .priority = i->priority,
> -                    .table_id = i->table_id,
> -                    .ofpacts = d->ofpacts,
> -                    .ofpacts_len = d->ofpacts_len,
> -                    .command = OFPFC_MODIFY_STRICT,
> -                };
> -                add_flow_mod(&fm, &msgs);
> -                ovn_flow_log(i, "updating installed");
> -
> -                /* Replace 'i''s actions by 'd''s. */
> -                free(i->ofpacts);
> -                i->ofpacts = xmemdup(d->ofpacts, d->ofpacts_len);
> -                i->ofpacts_len = d->ofpacts_len;
> -            }
> -
> -        }
> -    }
> -
> -    /* Iterate through the desired flows and add those that aren't found
> -     * in the installed flow table. */
> -    struct ovn_flow *d;
> -    HMAP_FOR_EACH (d, match_hmap_node, &flow_table->match_flow_table) {
> -        i = ovn_flow_lookup(&installed_flows, d, false);
> -        if (!i) {
> -            /* Send flow_mod to add flow. */
> -            struct ofputil_flow_mod fm = {
> -                .match = d->match,
> -                .priority = d->priority,
> -                .table_id = d->table_id,
> -                .ofpacts = d->ofpacts,
> -                .ofpacts_len = d->ofpacts_len,
> -                .new_cookie = htonll(d->cookie),
> -                .command = OFPFC_ADD,
> -            };
> -            add_flow_mod(&fm, &msgs);
> -            ovn_flow_log(d, "adding installed");
> -
> -            /* Copy 'd' from 'flow_table' to installed_flows. */
> -            struct ovn_flow *new_node = ofctrl_dup_flow(d);
> -            hmap_insert(&installed_flows, &new_node->match_hmap_node,
> -                        new_node->match_hmap_node.hash);
> -        }
> -    }
> -
> -    /* Iterate through the installed groups from previous runs. If they
> -     * are not needed delete them. */
> -    struct ovn_extend_table_info *installed, *next_group;
> -    EXTEND_TABLE_FOR_EACH_INSTALLED (installed, next_group, groups) {
> -        /* Delete the group. */
> -        struct ofputil_group_mod gm;
> -        enum ofputil_protocol usable_protocols;
> -        char *group_string = xasprintf("group_id=%"PRIu32"",
> -                                       installed->table_id);
> -        char *error = parse_ofp_group_mod_str(&gm, OFPGC11_DELETE,
> -                                              group_string, NULL, NULL,
> -                                              &usable_protocols);
> -        if (!error) {
> -            add_group_mod(&gm, &msgs);
> -        } else {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_ERR_RL(&rl, "Error deleting group %d: %s",
> -                        installed->table_id, error);
> -            free(error);
> -        }
> -        free(group_string);
> -        ofputil_uninit_group_mod(&gm);
> -        ovn_extend_table_remove_existing(groups, installed);
> -    }
> -
> -    /* Sync the contents of groups->desired to groups->existing. */
> -    ovn_extend_table_sync(groups);
> -
> -    /* Iterate through the installed meters from previous runs. If they
> -     * are not needed delete them. */
> -    struct ovn_extend_table_info *m_installed, *next_meter;
> -    EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_meter, meters) {
> -        /* Delete the meter. */
> -        struct ofputil_meter_mod mm = {
> -            .command = OFPMC13_DELETE,
> -            .meter = { .meter_id = m_installed->table_id },
> -        };
> -        add_meter_mod(&mm, &msgs);
> -
> -        ovn_extend_table_remove_existing(meters, m_installed);
> -    }
> -
> -    /* Sync the contents of meters->desired to meters->existing. */
> -    ovn_extend_table_sync(meters);
> -
> -    if (!ovs_list_is_empty(&msgs)) {
> -        /* Add a barrier to the list of messages. */
> -        struct ofpbuf *barrier =
> ofputil_encode_barrier_request(OFP13_VERSION);
> -        const struct ofp_header *oh = barrier->data;
> -        ovs_be32 xid_ = oh->xid;
> -        ovs_list_push_back(&msgs, &barrier->list_node);
> -
> -        /* Queue the messages. */
> -        struct ofpbuf *msg;
> -        LIST_FOR_EACH_POP (msg, list_node, &msgs) {
> -            queue_msg(msg);
> -        }
> -
> -        /* Store the barrier's xid with any newly sent ct flushes. */
> -        SHASH_FOR_EACH(iter, pending_ct_zones) {
> -            struct ct_zone_pending_entry *ctzpe = iter->data;
> -            if (ctzpe->state == CT_ZONE_OF_SENT && !ctzpe->of_xid) {
> -                ctzpe->of_xid = xid_;
> -            }
> -        }
> -
> -        /* Track the flow update. */
> -        struct ofctrl_flow_update *fup, *prev;
> -        LIST_FOR_EACH_REVERSE_SAFE (fup, prev, list_node, &flow_updates) {
> -            if (nb_cfg < fup->nb_cfg) {
> -                /* This ofctrl_flow_update is for a configuration later
> than
> -                 * 'nb_cfg'.  This should not normally happen, because it
> means
> -                 * that 'nb_cfg' in the SB_Global table of the southbound
> -                 * database decreased, and it should normally be
> monotonically
> -                 * increasing. */
> -                VLOG_WARN("nb_cfg regressed from %"PRId64" to %"PRId64,
> -                          fup->nb_cfg, nb_cfg);
> -                ovs_list_remove(&fup->list_node);
> -                free(fup);
> -            } else if (nb_cfg == fup->nb_cfg) {
> -                /* This ofctrl_flow_update is for the same configuration
> as
> -                 * 'nb_cfg'.  Probably, some change to the physical
> topology
> -                 * means that we had to revise the OpenFlow flow table
> even
> -                 * though the logical topology did not change.  Update
> fp->xid,
> -                 * so that we don't send a notification that we're
> up-to-date
> -                 * until we're really caught up. */
> -                VLOG_DBG("advanced xid target for nb_cfg=%"PRId64,
> nb_cfg);
> -                fup->xid = xid_;
> -                goto done;
> -            } else {
> -                break;
> -            }
> -        }
> -
> -        /* Add a flow update. */
> -        fup = xmalloc(sizeof *fup);
> -        ovs_list_push_back(&flow_updates, &fup->list_node);
> -        fup->xid = xid_;
> -        fup->nb_cfg = nb_cfg;
> -    done:;
> -    } else if (!ovs_list_is_empty(&flow_updates)) {
> -        /* Getting up-to-date with 'nb_cfg' didn't require any extra flow
> table
> -         * changes, so whenever we get up-to-date with the most recent
> flow
> -         * table update, we're also up-to-date with 'nb_cfg'. */
> -        struct ofctrl_flow_update *fup =
> ofctrl_flow_update_from_list_node(
> -            ovs_list_back(&flow_updates));
> -        fup->nb_cfg = nb_cfg;
> -    } else {
> -        /* We were completely up-to-date before and still are. */
> -        cur_cfg = nb_cfg;
> -    }
> -}
> -
> -/* Looks up the logical port with the name 'port_name' in 'br_int_'.  If
> - * found, returns true and sets '*portp' to the OpenFlow port number
> - * assigned to the port.  Otherwise, returns false. */
> -static bool
> -ofctrl_lookup_port(const void *br_int_, const char *port_name,
> -                   unsigned int *portp)
> -{
> -    const struct ovsrec_bridge *br_int = br_int_;
> -
> -    for (int i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> -        for (int j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec =
> port_rec->interfaces[j];
> -            const char *iface_id = smap_get(&iface_rec->external_ids,
> -                                            "iface-id");
> -
> -            if (iface_id && !strcmp(iface_id, port_name)) {
> -                if (!iface_rec->n_ofport) {
> -                    continue;
> -                }
> -
> -                int64_t ofport = iface_rec->ofport[0];
> -                if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) {
> -                    continue;
> -                }
> -                *portp = ofport;
> -                return true;
> -            }
> -        }
> -    }
> -
> -    return false;
> -}
> -
> -/* Generates a packet described by 'flow_s' in the syntax of an OVN
> - * logical expression and injects it into 'br_int'.  The flow
> - * description must contain an ingress logical port that is present on
> - * 'br_int'.
> - *
> - * Returns NULL if successful, otherwise an error message that the caller
> - * must free(). */
> -char *
> -ofctrl_inject_pkt(const struct ovsrec_bridge *br_int, const char *flow_s,
> -                  const struct shash *addr_sets,
> -                  const struct shash *port_groups)
> -{
> -    int version = rconn_get_version(swconn);
> -    if (version < 0) {
> -        return xstrdup("OpenFlow channel not ready.");
> -    }
> -
> -    struct flow uflow;
> -    char *error = expr_parse_microflow(flow_s, &symtab, addr_sets,
> -                                       port_groups, ofctrl_lookup_port,
> -                                       br_int, &uflow);
> -    if (error) {
> -        return error;
> -    }
> -
> -    /* The physical OpenFlow port was stored in the logical ingress
> -     * port, so put it in the correct location for a flow structure. */
> -    uflow.in_port.ofp_port = u16_to_ofp(uflow.regs[MFF_LOG_INPORT -
> MFF_REG0]);
> -    uflow.regs[MFF_LOG_INPORT - MFF_REG0] = 0;
> -
> -    if (!uflow.in_port.ofp_port) {
> -        return xstrdup("ingress port not found on hypervisor.");
> -    }
> -
> -    uint64_t packet_stub[128 / 8];
> -    struct dp_packet packet;
> -    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
> -    flow_compose(&packet, &uflow, NULL, 64);
> -
> -    uint64_t ofpacts_stub[1024 / 8];
> -    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
> -    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
> -    resubmit->in_port = OFPP_IN_PORT;
> -    resubmit->table_id = 0;
> -
> -    struct ofputil_packet_out po = {
> -        .packet = dp_packet_data(&packet),
> -        .packet_len = dp_packet_size(&packet),
> -        .buffer_id = UINT32_MAX,
> -        .ofpacts = ofpacts.data,
> -        .ofpacts_len = ofpacts.size,
> -    };
> -    match_set_in_port(&po.flow_metadata, uflow.in_port.ofp_port);
> -    enum ofputil_protocol proto =
> ofputil_protocol_from_ofp_version(version);
> -    queue_msg(ofputil_encode_packet_out(&po, proto));
> -    dp_packet_uninit(&packet);
> -    ofpbuf_uninit(&ofpacts);
> -
> -    return NULL;
> -}
> -
> -bool
> -ofctrl_is_connected(void)
> -{
> -    return rconn_is_connected(swconn);
> -}
> -
> -void
> -ofctrl_set_probe_interval(int probe_interval)
> -{
> -    if (swconn) {
> -        rconn_set_probe_interval(swconn, probe_interval);
> -    }
> -}
> diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
> deleted file mode 100644
> index ed8918aae..000000000
> --- a/ovn/controller/ofctrl.h
> +++ /dev/null
> @@ -1,87 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -
> -#ifndef OFCTRL_H
> -#define OFCTRL_H 1
> -
> -#include <stdint.h>
> -
> -#include "openvswitch/meta-flow.h"
> -#include "ovsdb-idl.h"
> -#include "hindex.h"
> -
> -struct ovn_extend_table;
> -struct hmap;
> -struct match;
> -struct ofpbuf;
> -struct ovsrec_bridge;
> -struct sbrec_meter_table;
> -struct shash;
> -
> -struct ovn_desired_flow_table {
> -    /* Hash map flow table using flow match conditions as hash key.*/
> -    struct hmap match_flow_table;
> -
> -    /* SB uuid index for the nodes in match_flow_table.*/
> -    struct hindex uuid_flow_table;
> -};
> -
> -/* Interface for OVN main loop. */
> -void ofctrl_init(struct ovn_extend_table *group_table,
> -                 struct ovn_extend_table *meter_table,
> -                 int inactivity_probe_interval);
> -void ofctrl_run(const struct ovsrec_bridge *br_int,
> -                struct shash *pending_ct_zones);
> -enum mf_field_id ofctrl_get_mf_field_id(void);
> -void ofctrl_put(struct ovn_desired_flow_table *,
> -                struct shash *pending_ct_zones,
> -                const struct sbrec_meter_table *,
> -                int64_t nb_cfg,
> -                bool flow_changed);
> -void ofctrl_wait(void);
> -void ofctrl_destroy(void);
> -int64_t ofctrl_get_cur_cfg(void);
> -
> -struct ovn_flow *ofctrl_dup_flow(struct ovn_flow *source);
> -
> -void ofctrl_ct_flush_zone(uint16_t zone_id);
> -
> -char *ofctrl_inject_pkt(const struct ovsrec_bridge *br_int,
> -                        const char *flow_s, const struct shash *addr_sets,
> -                        const struct shash *port_groups);
> -
> -/* Flow table interfaces to the rest of ovn-controller. */
> -void ofctrl_add_flow(struct ovn_desired_flow_table *, uint8_t table_id,
> -                     uint16_t priority, uint64_t cookie,
> -                     const struct match *, const struct ofpbuf *ofpacts,
> -                     const struct uuid *);
> -
> -void ofctrl_remove_flows(struct ovn_desired_flow_table *, const struct
> uuid *);
> -
> -void ovn_desired_flow_table_init(struct ovn_desired_flow_table *);
> -void ovn_desired_flow_table_clear(struct ovn_desired_flow_table *);
> -void ovn_desired_flow_table_destroy(struct ovn_desired_flow_table *);
> -
> -void ofctrl_check_and_add_flow(struct ovn_desired_flow_table *,
> -                               uint8_t table_id, uint16_t priority,
> -                               uint64_t cookie, const struct match *,
> -                               const struct ofpbuf *ofpacts,
> -                               const struct uuid *, bool
> log_duplicate_flow);
> -
> -bool ofctrl_is_connected(void);
> -void ofctrl_set_probe_interval(int probe_interval);
> -
> -#endif /* ovn/ofctrl.h */
> diff --git a/ovn/controller/ovn-controller.8.xml
> b/ovn/controller/ovn-controller.8.xml
> deleted file mode 100644
> index 780625ff8..000000000
> --- a/ovn/controller/ovn-controller.8.xml
> +++ /dev/null
> @@ -1,456 +0,0 @@
> -<?xml version="1.0" encoding="utf-8"?>
> -<manpage program="ovn-controller" section="8" title="ovn-controller">
> -    <h1>Name</h1>
> -    <p>ovn-controller -- Open Virtual Network local controller</p>
> -
> -    <h1>Synopsis</h1>
> -    <p><code>ovn-controller</code> [<var>options</var>]
> [<var>ovs-database</var>]</p>
> -
> -    <h1>Description</h1>
> -    <p>
> -      <code>ovn-controller</code> is the local controller daemon for
> -      OVN, the Open Virtual Network.  It connects up to the OVN
> -      Southbound database (see <code>ovn-sb</code>(5)) over the OVSDB
> -      protocol, and down to the Open vSwitch database (see
> -      <code>ovs-vswitchd.conf.db</code>(5)) over the OVSDB protocol and
> -      to <code>ovs-vswitchd</code>(8) via OpenFlow.  Each hypervisor and
> -      software gateway in an OVN deployment runs its own independent
> -      copy of <code>ovn-controller</code>; thus,
> -      <code>ovn-controller</code>'s downward connections are
> -      machine-local and do not run over a physical network.
> -    </p>
> -
> -    <h1>ACL Logging</h1>
> -    <p>
> -      ACL log messages are logged through <code>ovn-controller</code>'s
> -      logging mechanism.  ACL log entries have the module
> -      <code>acl_log</code> at log level <code>info</code>.  Configuring
> -      logging is described below in the <code>Logging Options</code>
> -      section.
> -    </p>
> -
> -    <h1>Options</h1>
> -
> -    <h2>Daemon Options</h2>
> -    <xi:include href="lib/daemon.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -
> -    <h2>Logging Options</h2>
> -    <xi:include href="lib/vlog.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -
> -    <h2>PKI Options</h2>
> -    <p>
> -      PKI configuration is required in order to use SSL for the
> connections to
> -      the Northbound and Southbound databases.
> -    </p>
> -    <xi:include href="lib/ssl.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -    <xi:include href="lib/ssl-bootstrap.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -    <xi:include href="lib/ssl-peer-ca-cert.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -
> -    <h2>Other Options</h2>
> -
> -    <xi:include href="lib/common.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> -
> -
> -    <h1>Configuration</h1>
> -    <p>
> -      <code>ovn-controller</code> retrieves most of its configuration
> -      information from the local Open vSwitch's ovsdb-server instance.
> -      The default location is <code>db.sock</code> in the local Open
> -      vSwitch's "run" directory.  It may be overridden by specifying the
> -      <var>ovs-database</var> argument as an OVSDB active or passive
> -      connection method, as described in <code>ovsdb</code>(7).
> -    </p>
> -
> -    <p>
> -      <code>ovn-controller</code> assumes it gets configuration
> -      information from the following keys in the <code>Open_vSwitch</code>
> -      table of the local OVS instance:
> -    </p>
> -    <dl>
> -      <dt><code>external_ids:system-id</code></dt>
> -      <dd>The chassis name to use in the Chassis table.</dd>
> -
> -      <dt><code>external_ids:hostname</code></dt>
> -      <dd>The hostname to use in the Chassis table.</dd>
> -
> -      <dt><code>external_ids:ovn-bridge</code></dt>
> -      <dd>
> -        The integration bridge to which logical ports are attached.  The
> -        default is <code>br-int</code>.  If this bridge does not exist
> when
> -        ovn-controller starts, it will be created automatically with the
> -        default configuration suggested in
> <code>ovn-architecture</code>(7).
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-bridge-datapath-type</code></dt>
> -      <dd>
> -        This configuration is optional. If set, then the datapath type of
> -        the integration bridge will be set to the configured value. If
> this
> -        option is not set, then <code>ovn-controller</code> will not
> modify
> -        the existing <code>datapath-type</code> of the integration bridge.
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-remote</code></dt>
> -      <dd>
> -        <p>
> -          The OVN database that this system should connect to for its
> -          configuration, in one of the same forms documented above for the
> -          <var>ovs-database</var>.
> -        </p>
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-remote-probe-interval</code></dt>
> -      <dd>
> -        <p>
> -          The inactivity probe interval of the connection to the OVN
> database,
> -          in milliseconds.
> -          If the value is zero, it disables the connection keepalive
> feature.
> -        </p>
> -
> -        <p>
> -          If the value is nonzero, then it will be forced to a value of
> -          at least 1000 ms.
> -        </p>
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-openflow-probe-interval</code></dt>
> -      <dd>
> -        <p>
> -          The inactivity probe interval of the OpenFlow connection to the
> -          OpenvSwitch integration bridge, in seconds.
> -          If the value is zero, it disables the connection keepalive
> feature.
> -        </p>
> -
> -        <p>
> -          If the value is nonzero, then it will be forced to a value of
> -          at least 5s.
> -        </p>
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-encap-type</code></dt>
> -      <dd>
> -        <p>
> -          The encapsulation type that a chassis should use to connect to
> -          this node.  Multiple encapsulation types may be specified with
> -          a comma-separated list.  Each listed encapsulation type will
> -          be paired with <code>ovn-encap-ip</code>.
> -        </p>
> -
> -        <p>
> -          Supported tunnel types for connecting hypervisors
> -          are <code>geneve</code> and <code>stt</code>.  Gateways may
> -          use <code>geneve</code>, <code>vxlan</code>, or
> -          <code>stt</code>.
> -        </p>
> -
> -        <p>
> -          Due to the limited amount of metadata in <code>vxlan</code>,
> -          the capabilities and performance of connected gateways will be
> -          reduced versus other tunnel formats.
> -        </p>
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-encap-ip</code></dt>
> -      <dd>
> -        The IP address that a chassis should use to connect to this node
> -        using encapsulation types specified by
> -        <code>external_ids:ovn-encap-type</code>.
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-bridge-mappings</code></dt>
> -      <dd>
> -        A list of key-value pairs that map a physical network name to a
> local
> -        ovs bridge that provides connectivity to that network.  An example
> -        value mapping two physical network names to two ovs bridges would
> be:
> -        <code>physnet1:br-eth0,physnet2:br-eth1</code>.
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-encap-csum</code></dt>
> -      <dd>
> -        <code>ovn-encap-csum</code> indicates that encapsulation
> checksums can
> -        be transmitted and received with reasonable performance. It is a
> hint
> -        to senders transmitting data to this chassis that they should use
> -        checksums to protect OVN metadata. Set to <code>true</code> to
> enable
> -        or <code>false</code> to disable. Depending on the capabilities
> of the
> -        network interface card, enabling encapsulation checksum may incur
> -        performance loss. In such cases, encapsulation checksums can be
> disabled.
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-cms-options</code></dt>
> -      <dd>
> -        A list of options that will be consumed by the CMS Plugin and
> which
> -        specific to this particular chassis. An example would be:
> -        <code>cms_option1,cms_option2:foo</code>.
> -      </dd>
> -
> -      <dt><code>external_ids:ovn-transport-zones</code></dt>
> -      <dd>
> -        <p>
> -          The transport zone(s) that this chassis belongs to. Transport
> -          zones is a way to group different chassis so that tunnels are
> only
> -          formed between members of the same group(s). Multiple transport
> -          zones may be specified with a comma-separated list. For example:
> -          tz1,tz2,tz3.
> -        </p>
> -        <p>
> -          If not set, the Chassis will be considered part of a default
> -          transport zone.
> -        </p>
> -      </dd>
> -      <dt><code>external_ids:ovn-chassis-mac-mappings</code></dt>
> -      <dd>
> -        A list of key-value pairs that map a chassis specific mac to
> -        a physical network name. An example
> -        value mapping two chassis macs to two physical network names
> would be:
> -
> <code>physnet1:aa:bb:cc:dd:ee:ff,physnet2:a1:b2:c3:d4:e5:f6</code>.
> -        These are the macs that ovn-controller will replace a router port
> -        mac with, if packet is going from a distributed router port on
> -        vlan type logical switch.
> -      </dd>
> -    </dl>
> -
> -    <p>
> -      <code>ovn-controller</code> reads the following values from the
> -      <code>Open_vSwitch</code> database of the local OVS instance:
> -    </p>
> -
> -    <dl>
> -      <dt><code>datapath-type</code> from <ref table="Bridge"
> db="Open_vSwitch"/> table</dt>
> -      <dd>
> -        This value is read from local OVS integration bridge row of
> -        <ref table="Bridge" db="Open_vSwitch"/> table and populated in
> -        <ref key="datapath-type" table="Chassis" column="external_ids"
> -        db="OVN_Southbound"/> of the <ref table="Chassis"
> db="OVN_Southbound"/>
> -        table in the OVN_Southbound database.
> -      </dd>
> -
> -      <dt><code>iface-types</code> from <ref table="Open_vSwitch"
> db="Open_vSwitch"/> table</dt>
> -      <dd>
> -        This value is populated in <ref key="iface-types" table="Chassis"
> -        column="external_ids" db="OVN_Southbound"/> of the
> -        <ref table="Chassis" db="OVN_Southbound"/> table in the
> OVN_Southbound
> -        database.
> -      </dd>
> -
> -      <dt><code>private_key</code>, <code>certificate</code>,
> -          <code>ca_cert</code>, and <code>bootstrap_ca_cert</code>
> -          from <ref table="SSL" db="Open_vSwitch"/> table</dt>
> -      <dd>
> -        These values provide the SSL configuration used for connecting
> -        to the OVN southbound database server when an SSL connection type
> -        is configured via <code>external_ids:ovn-remote</code>.  Note that
> -        this SSL configuration can also be provided via command-line
> options,
> -        the configuration in the database takes precedence if both are
> present.
> -      </dd>
> -    </dl>
> -
> -    <h1>Open vSwitch Database Usage</h1>
> -
> -    <p>
> -      <code>ovn-controller</code> uses a number of
> <code>external_ids</code>
> -      keys in the Open vSwitch database to keep track of ports and
> interfaces.
> -      For proper operation, users should not change or clear these keys:
> -    </p>
> -
> -    <dl>
> -      <dt>
> -        <code>external_ids:ovn-chassis-id</code> in the <code>Port</code>
> table
> -      </dt>
> -      <dd>
> -        The presence of this key identifies a tunnel port within the
> -        integration bridge as one created by <code>ovn-controller</code>
> to
> -        reach a remote chassis.  Its value is the chassis ID of the remote
> -        chassis.
> -      </dd>
> -
> -      <dt>
> -        <code>external_ids:ct-zone-*</code> in the <code>Bridge</code>
> table
> -      </dt>
> -      <dd>
> -        Logical ports and gateway routers are assigned a connection
> -        tracking zone by <code>ovn-controller</code> for stateful
> -        services.  To keep state across restarts of
> -        <code>ovn-controller</code>, these keys are stored in the
> -        integration bridge's Bridge table.  The name contains a prefix
> -        of <code>ct-zone-</code> followed by the name of the logical
> -        port or gateway router's zone key.  The value for this key
> -        identifies the zone used for this port.
> -      </dd>
> -
> -      <dt>
> -        <code>external_ids:ovn-localnet-port</code> in the
> <code>Port</code>
> -        table
> -      </dt>
> -      <dd>
> -        <p>
> -          The presence of this key identifies a patch port as one created
> by
> -          <code>ovn-controller</code> to connect the integration bridge
> and
> -          another bridge to implement a <code>localnet</code> logical
> port.
> -          Its value is the name of the logical port with <code>type</code>
> -          set to <code>localnet</code> that the port implements. See
> -          <code>external_ids:ovn-bridge-mappings</code>, above, for more
> -          information.
> -        </p>
> -
> -        <p>
> -          Each <code>localnet</code> logical port is implemented as a
> pair of
> -          patch ports, one in the integration bridge, one in a different
> -          bridge, with the same
> <code>external_ids:ovn-localnet-port</code>
> -          value.
> -        </p>
> -      </dd>
> -
> -      <dt>
> -        <code>external_ids:ovn-l2gateway-port</code> in the
> <code>Port</code>
> -        table
> -      </dt>
> -      <dd>
> -        <p>
> -          The presence of this key identifies a patch port as one created
> by
> -          <code>ovn-controller</code> to connect the integration bridge
> and
> -          another bridge to implement a <code>l2gateway</code> logical
> port.
> -          Its value is the name of the logical port with <code>type</code>
> -          set to <code>l2gateway</code> that the port implements. See
> -          <code>external_ids:ovn-bridge-mappings</code>, above, for more
> -          information.
> -        </p>
> -
> -        <p>
> -          Each <code>l2gateway</code> logical port is implemented as a
> pair
> -          of patch ports, one in the integration bridge, one in a
> different
> -          bridge, with the same
> <code>external_ids:ovn-l2gateway-port</code>
> -          value.
> -        </p>
> -      </dd>
> -
> -      <dt>
> -        <code>external-ids:ovn-l3gateway-port</code> in the
> <code>Port</code>
> -        table
> -      </dt>
> -
> -      <dd>
> -        <p>
> -          This key identifies a patch port as one created by
> -          <code>ovn-controller</code> to implement a
> <code>l3gateway</code>
> -          logical port. Its value is the name of the logical port with
> type
> -          set to <code>l3gateway</code>. This patch port is similar to
> -          the OVN logical patch port, except that <code>l3gateway</code>
> -          port can only be bound to a paticular chassis.
> -        </p>
> -      </dd>
> -
> -      <dt>
> -        <code>external-ids:ovn-logical-patch-port</code> in the
> -        <code>Port</code> table
> -      </dt>
> -
> -      <dd>
> -        <p>
> -          This key identifies a patch port as one created by
> -          <code>ovn-controller</code> to implement an OVN logical patch
> port
> -          within the integration bridge.  Its value is the name of the OVN
> -          logical patch port that it implements.
> -        </p>
> -      </dd>
> -    </dl>
> -
> -    <h1>OVN Southbound Database Usage</h1>
> -
> -    <p>
> -      <code>ovn-controller</code> reads from much of the
> -      <code>OVN_Southbound</code> database to guide its operation.
> -      <code>ovn-controller</code> also writes to the following tables:
> -    </p>
> -
> -    <dl>
> -      <dt><code>Chassis</code></dt>
> -      <dd>
> -        Upon startup, <code>ovn-controller</code> creates a row in this
> table
> -        to represent its own chassis.  Upon graceful termination, e.g.
> with
> -        <code>ovs-appctl -t ovn-controller exit</code> (but not
> -        <code>SIGTERM</code>), <code>ovn-controller</code> removes its
> row.
> -      </dd>
> -
> -      <dt><code>Encap</code></dt>
> -      <dd>
> -        Upon startup, <code>ovn-controller</code> creates a row or rows
> in this
> -        table that represent the tunnel encapsulations by which its
> chassis can
> -        be reached, and points its <code>Chassis</code> row to them.  Upon
> -        graceful termination, <code>ovn-controller</code> removes these
> rows.
> -      </dd>
> -
> -      <dt><code>Port_Binding</code></dt>
> -      <dd>
> -        At runtime, <code>ovn-controller</code> sets the
> <code>chassis</code>
> -        columns of ports that are resident on its chassis to point to its
> -        <code>Chassis</code> row, and, conversely, clears the
> -        <code>chassis</code> column of ports that point to its
> -        <code>Chassis</code> row but are no longer resident on its
> chassis.
> -        The <code>chassis</code> column has a weak reference type, so when
> -        <code>ovn-controller</code> gracefully exits and removes its
> -        <code>Chassis</code> row, the database server automatically
> clears any
> -        remaining references to that row.
> -      </dd>
> -
> -      <dt><code>MAC_Binding</code></dt>
> -      <dd>
> -        At runtime, <code>ovn-controller</code> updates the
> -        <code>MAC_Binding</code> table as instructed by
> <code>put_arp</code>
> -        and <code>put_nd</code> logical actions.  These changes persist
> beyond
> -        the lifetime of <code>ovn-controller</code>.
> -      </dd>
> -    </dl>
> -
> -    <h1>Runtime Management Commands</h1>
> -    <p>
> -      <code>ovs-appctl</code> can send commands to a running
> -      <code>ovn-controller</code> process.  The currently supported
> -      commands are described below.
> -      <dl>
> -      <dt><code>exit</code></dt>
> -      <dd>
> -        Causes <code>ovn-controller</code> to gracefully terminate.
> -      </dd>
> -
> -      <dt><code>ct-zone-list</code></dt>
> -      <dd>
> -        Lists each local logical port and its connection tracking zone.
> -      </dd>
> -
> -      <dt><code>meter-table-list</code></dt>
> -      <dd>
> -        Lists each meter table entry and its local meter id.
> -      </dd>
> -
> -      <dt><code>group-table-list</code></dt>
> -      <dd>
> -        Lists each group table entry and its local group id.
> -      </dd>
> -
> -      <dt><code>inject-pkt</code> <var>microflow</var></dt>
> -      <dd>
> -      <p>
> -        Injects <var>microflow</var> into the connected Open vSwitch
> -        instance.  <var>microflow</var> must contain an ingress logical
> -        port (<code>inport</code> argument) that is present on the Open
> -        vSwitch instance.
> -      </p>
> -
> -      <p>
> -        The <var>microflow</var> argument describes the packet whose
> -        forwarding is to be simulated, in the syntax of an OVN logical
> -        expression, as described in <code>ovn-sb</code>(5), to express
> -        constraints.  The parser understands prerequisites; for example,
> -        if the expression refers to <code>ip4.src</code>, there is no
> -        need to explicitly state <code>ip4</code> or <code>eth.type ==
> -        0x800</code>.
> -      </p>
> -      </dd>
> -
> -      <dt><code>connection-status</code></dt>
> -      <dd>
> -        Show OVN SBDB connection status for the chassis.
> -      </dd>
> -      </dl>
> -    </p>
> -
> -</manpage>
> diff --git a/ovn/controller/ovn-controller.c
> b/ovn/controller/ovn-controller.c
> deleted file mode 100644
> index cf6c8ae79..000000000
> --- a/ovn/controller/ovn-controller.c
> +++ /dev/null
> @@ -1,2366 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "ovn-controller.h"
> -
> -#include <errno.h>
> -#include <getopt.h>
> -#include <signal.h>
> -#include <stdlib.h>
> -#include <string.h>
> -
> -#include "bfd.h"
> -#include "binding.h"
> -#include "chassis.h"
> -#include "command-line.h"
> -#include "compiler.h"
> -#include "daemon.h"
> -#include "dirs.h"
> -#include "openvswitch/dynamic-string.h"
> -#include "encaps.h"
> -#include "fatal-signal.h"
> -#include "ip-mcast.h"
> -#include "openvswitch/hmap.h"
> -#include "lflow.h"
> -#include "lib/vswitch-idl.h"
> -#include "lport.h"
> -#include "ofctrl.h"
> -#include "openvswitch/vconn.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn/actions.h"
> -#include "ovn/lib/chassis-index.h"
> -#include "ovn/lib/extend-table.h"
> -#include "ovn/lib/ip-mcast-index.h"
> -#include "ovn/lib/mcast-group-index.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn/lib/ovn-util.h"
> -#include "patch.h"
> -#include "physical.h"
> -#include "pinctrl.h"
> -#include "openvswitch/poll-loop.h"
> -#include "lib/bitmap.h"
> -#include "lib/hash.h"
> -#include "smap.h"
> -#include "sset.h"
> -#include "stream-ssl.h"
> -#include "stream.h"
> -#include "unixctl.h"
> -#include "util.h"
> -#include "timeval.h"
> -#include "timer.h"
> -#include "stopwatch.h"
> -#include "ovn/lib/inc-proc-eng.h"
> -
> -VLOG_DEFINE_THIS_MODULE(main);
> -
> -static unixctl_cb_func ovn_controller_exit;
> -static unixctl_cb_func ct_zone_list;
> -static unixctl_cb_func meter_table_list;
> -static unixctl_cb_func group_table_list;
> -static unixctl_cb_func inject_pkt;
> -static unixctl_cb_func ovn_controller_conn_show;
> -
> -#define DEFAULT_BRIDGE_NAME "br-int"
> -#define DEFAULT_PROBE_INTERVAL_MSEC 5000
> -#define OFCTRL_DEFAULT_PROBE_INTERVAL_SEC 5
> -
> -#define CONTROLLER_LOOP_STOPWATCH_NAME "ovn-controller-flow-generation"
> -
> -static char *parse_options(int argc, char *argv[]);
> -OVS_NO_RETURN static void usage(void);
> -
> -/* Pending packet to be injected into connected OVS. */
> -struct pending_pkt {
> -    /* Setting 'conn' indicates that a request is pending. */
> -    struct unixctl_conn *conn;
> -    char *flow_s;
> -};
> -
> -struct local_datapath *
> -get_local_datapath(const struct hmap *local_datapaths, uint32_t
> tunnel_key)
> -{
> -    struct hmap_node *node = hmap_first_with_hash(local_datapaths,
> tunnel_key);
> -    return (node
> -            ? CONTAINER_OF(node, struct local_datapath, hmap_node)
> -            : NULL);
> -}
> -
> -uint32_t
> -get_tunnel_type(const char *name)
> -{
> -    if (!strcmp(name, "geneve")) {
> -        return GENEVE;
> -    } else if (!strcmp(name, "stt")) {
> -        return STT;
> -    } else if (!strcmp(name, "vxlan")) {
> -        return VXLAN;
> -    }
> -
> -    return 0;
> -}
> -
> -const struct ovsrec_bridge *
> -get_bridge(const struct ovsrec_bridge_table *bridge_table, const char
> *br_name)
> -{
> -    const struct ovsrec_bridge *br;
> -    OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) {
> -        if (!strcmp(br->name, br_name)) {
> -            return br;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static void
> -update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
> -                   const struct sbrec_chassis *chassis,
> -                   const struct sset *local_ifaces,
> -                   struct hmap *local_datapaths)
> -{
> -    /* Monitor Port_Bindings rows for local interfaces and local
> datapaths.
> -     *
> -     * Monitor Logical_Flow, MAC_Binding, Multicast_Group, and DNS tables
> for
> -     * local datapaths.
> -     *
> -     * Monitor Controller_Event rows for local chassis.
> -     *
> -     * Monitor IP_Multicast for local datapaths.
> -     *
> -     * Monitor IGMP_Groups for local chassis.
> -     *
> -     * We always monitor patch ports because they allow us to see the
> linkages
> -     * between related logical datapaths.  That way, when we know that we
> have
> -     * a VIF on a particular logical switch, we immediately know to
> monitor all
> -     * the connected logical routers and logical switches. */
> -    struct ovsdb_idl_condition pb = OVSDB_IDL_CONDITION_INIT(&pb);
> -    struct ovsdb_idl_condition lf = OVSDB_IDL_CONDITION_INIT(&lf);
> -    struct ovsdb_idl_condition mb = OVSDB_IDL_CONDITION_INIT(&mb);
> -    struct ovsdb_idl_condition mg = OVSDB_IDL_CONDITION_INIT(&mg);
> -    struct ovsdb_idl_condition dns = OVSDB_IDL_CONDITION_INIT(&dns);
> -    struct ovsdb_idl_condition ce =  OVSDB_IDL_CONDITION_INIT(&ce);
> -    struct ovsdb_idl_condition ip_mcast =
> OVSDB_IDL_CONDITION_INIT(&ip_mcast);
> -    struct ovsdb_idl_condition igmp = OVSDB_IDL_CONDITION_INIT(&igmp);
> -    sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "patch");
> -    /* XXX: We can optimize this, if we find a way to only monitor
> -     * ports that have a Gateway_Chassis that point's to our own
> -     * chassis */
> -    sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ,
> "chassisredirect");
> -    sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "external");
> -    if (chassis) {
> -        /* This should be mostly redundant with the other clauses for port
> -         * bindings, but it allows us to catch any ports that are
> assigned to
> -         * us but should not be.  That way, we can clear their chassis
> -         * assignments. */
> -        sbrec_port_binding_add_clause_chassis(&pb, OVSDB_F_EQ,
> -                                              &chassis->header_.uuid);
> -
> -        /* Ensure that we find out about l2gateway and l3gateway ports
> that
> -         * should be present on this chassis.  Otherwise, we might never
> find
> -         * out about those ports, if their datapaths don't otherwise have
> a VIF
> -         * in this chassis. */
> -        const char *id = chassis->name;
> -        const struct smap l2 = SMAP_CONST1(&l2, "l2gateway-chassis", id);
> -        sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES, &l2);
> -        const struct smap l3 = SMAP_CONST1(&l3, "l3gateway-chassis", id);
> -        sbrec_port_binding_add_clause_options(&pb, OVSDB_F_INCLUDES, &l3);
> -
> -        sbrec_controller_event_add_clause_chassis(&ce, OVSDB_F_EQ,
> -                                                  &chassis->header_.uuid);
> -        sbrec_igmp_group_add_clause_chassis(&igmp, OVSDB_F_EQ,
> -                                            &chassis->header_.uuid);
> -    }
> -    if (local_ifaces) {
> -        const char *name;
> -        SSET_FOR_EACH (name, local_ifaces) {
> -            sbrec_port_binding_add_clause_logical_port(&pb, OVSDB_F_EQ,
> name);
> -            sbrec_port_binding_add_clause_parent_port(&pb, OVSDB_F_EQ,
> name);
> -        }
> -    }
> -    if (local_datapaths) {
> -        const struct local_datapath *ld;
> -        HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
> -            struct uuid *uuid = CONST_CAST(struct uuid *,
> -                                           &ld->datapath->header_.uuid);
> -            sbrec_port_binding_add_clause_datapath(&pb, OVSDB_F_EQ, uuid);
> -            sbrec_logical_flow_add_clause_logical_datapath(&lf,
> OVSDB_F_EQ,
> -                                                           uuid);
> -            sbrec_mac_binding_add_clause_datapath(&mb, OVSDB_F_EQ, uuid);
> -            sbrec_multicast_group_add_clause_datapath(&mg, OVSDB_F_EQ,
> uuid);
> -            sbrec_dns_add_clause_datapaths(&dns, OVSDB_F_INCLUDES, &uuid,
> 1);
> -            sbrec_ip_multicast_add_clause_datapath(&ip_mcast, OVSDB_F_EQ,
> -                                                   uuid);
> -        }
> -    }
> -    sbrec_port_binding_set_condition(ovnsb_idl, &pb);
> -    sbrec_logical_flow_set_condition(ovnsb_idl, &lf);
> -    sbrec_mac_binding_set_condition(ovnsb_idl, &mb);
> -    sbrec_multicast_group_set_condition(ovnsb_idl, &mg);
> -    sbrec_dns_set_condition(ovnsb_idl, &dns);
> -    sbrec_controller_event_set_condition(ovnsb_idl, &ce);
> -    sbrec_ip_multicast_set_condition(ovnsb_idl, &ip_mcast);
> -    sbrec_igmp_group_set_condition(ovnsb_idl, &igmp);
> -    ovsdb_idl_condition_destroy(&pb);
> -    ovsdb_idl_condition_destroy(&lf);
> -    ovsdb_idl_condition_destroy(&mb);
> -    ovsdb_idl_condition_destroy(&mg);
> -    ovsdb_idl_condition_destroy(&dns);
> -    ovsdb_idl_condition_destroy(&ce);
> -    ovsdb_idl_condition_destroy(&ip_mcast);
> -    ovsdb_idl_condition_destroy(&igmp);
> -}
> -
> -static const char *
> -br_int_name(const struct ovsrec_open_vswitch *cfg)
> -{
> -    return smap_get_def(&cfg->external_ids, "ovn-bridge",
> DEFAULT_BRIDGE_NAME);
> -}
> -
> -static const struct ovsrec_bridge *
> -create_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
> -              const struct ovsrec_open_vswitch_table *ovs_table)
> -{
> -    if (!ovs_idl_txn) {
> -        return NULL;
> -    }
> -
> -    const struct ovsrec_open_vswitch *cfg;
> -    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> -    if (!cfg) {
> -        return NULL;
> -    }
> -    const char *bridge_name = br_int_name(cfg);
> -
> -    ovsdb_idl_txn_add_comment(ovs_idl_txn,
> -            "ovn-controller: creating integration bridge '%s'",
> bridge_name);
> -
> -    struct ovsrec_interface *iface;
> -    iface = ovsrec_interface_insert(ovs_idl_txn);
> -    ovsrec_interface_set_name(iface, bridge_name);
> -    ovsrec_interface_set_type(iface, "internal");
> -
> -    struct ovsrec_port *port;
> -    port = ovsrec_port_insert(ovs_idl_txn);
> -    ovsrec_port_set_name(port, bridge_name);
> -    ovsrec_port_set_interfaces(port, &iface, 1);
> -
> -    struct ovsrec_bridge *bridge;
> -    bridge = ovsrec_bridge_insert(ovs_idl_txn);
> -    ovsrec_bridge_set_name(bridge, bridge_name);
> -    ovsrec_bridge_set_fail_mode(bridge, "secure");
> -    const struct smap oc = SMAP_CONST1(&oc, "disable-in-band", "true");
> -    ovsrec_bridge_set_other_config(bridge, &oc);
> -    ovsrec_bridge_set_ports(bridge, &port, 1);
> -
> -    struct ovsrec_bridge **bridges;
> -    size_t bytes = sizeof *bridges * cfg->n_bridges;
> -    bridges = xmalloc(bytes + sizeof *bridges);
> -    memcpy(bridges, cfg->bridges, bytes);
> -    bridges[cfg->n_bridges] = bridge;
> -    ovsrec_open_vswitch_verify_bridges(cfg);
> -    ovsrec_open_vswitch_set_bridges(cfg, bridges, cfg->n_bridges + 1);
> -    free(bridges);
> -
> -    return bridge;
> -}
> -
> -static const struct ovsrec_bridge *
> -get_br_int(const struct ovsrec_bridge_table *bridge_table,
> -           const struct ovsrec_open_vswitch_table *ovs_table)
> -{
> -    const struct ovsrec_open_vswitch *cfg;
> -    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> -    if (!cfg) {
> -        return NULL;
> -    }
> -
> -    return get_bridge(bridge_table, br_int_name(cfg));
> -}
> -
> -static const struct ovsrec_bridge *
> -process_br_int(struct ovsdb_idl_txn *ovs_idl_txn,
> -               const struct ovsrec_bridge_table *bridge_table,
> -               const struct ovsrec_open_vswitch_table *ovs_table)
> -{
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> -                                                    ovs_table);
> -    if (!br_int) {
> -        br_int = create_br_int(ovs_idl_txn, ovs_table);
> -    }
> -    if (br_int && ovs_idl_txn) {
> -        const struct ovsrec_open_vswitch *cfg;
> -        cfg = ovsrec_open_vswitch_table_first(ovs_table);
> -        ovs_assert(cfg);
> -        const char *datapath_type = smap_get(&cfg->external_ids,
> -                                             "ovn-bridge-datapath-type");
> -        /* Check for the datapath_type and set it only if it is defined in
> -         * cfg. */
> -        if (datapath_type && strcmp(br_int->datapath_type,
> datapath_type)) {
> -            ovsrec_bridge_set_datapath_type(br_int, datapath_type);
> -        }
> -    }
> -    return br_int;
> -}
> -
> -static const char *
> -get_ovs_chassis_id(const struct ovsrec_open_vswitch_table *ovs_table)
> -{
> -    const struct ovsrec_open_vswitch *cfg
> -        = ovsrec_open_vswitch_table_first(ovs_table);
> -    const char *chassis_id = cfg ? smap_get(&cfg->external_ids,
> "system-id")
> -                                 : NULL;
> -
> -    if (!chassis_id) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -        VLOG_WARN_RL(&rl, "'system-id' in Open_vSwitch database is
> missing.");
> -    }
> -
> -    return chassis_id;
> -}
> -
> -/* Iterate address sets in the southbound database.  Create and update the
> - * corresponding symtab entries as necessary. */
> -static void
> -addr_sets_init(const struct sbrec_address_set_table *address_set_table,
> -               struct shash *addr_sets)
> -{
> -    const struct sbrec_address_set *as;
> -    SBREC_ADDRESS_SET_TABLE_FOR_EACH (as, address_set_table) {
> -        expr_const_sets_add(addr_sets, as->name,
> -                            (const char *const *) as->addresses,
> -                            as->n_addresses, true);
> -    }
> -}
> -
> -static void
> -addr_sets_update(const struct sbrec_address_set_table *address_set_table,
> -                 struct shash *addr_sets, struct sset *new,
> -                 struct sset *deleted, struct sset *updated)
> -{
> -    const struct sbrec_address_set *as;
> -    SBREC_ADDRESS_SET_TABLE_FOR_EACH_TRACKED (as, address_set_table) {
> -        if (sbrec_address_set_is_deleted(as)) {
> -            expr_const_sets_remove(addr_sets, as->name);
> -            sset_add(deleted, as->name);
> -        } else {
> -            expr_const_sets_add(addr_sets, as->name,
> -                                (const char *const *) as->addresses,
> -                                as->n_addresses, true);
> -            if (sbrec_address_set_is_new(as)) {
> -                sset_add(new, as->name);
> -            } else {
> -                sset_add(updated, as->name);
> -            }
> -        }
> -    }
> -}
> -
> -/* Iterate port groups in the southbound database.  Create and update the
> - * corresponding symtab entries as necessary. */
> - static void
> -port_groups_init(const struct sbrec_port_group_table *port_group_table,
> -                 struct shash *port_groups)
> -{
> -    const struct sbrec_port_group *pg;
> -    SBREC_PORT_GROUP_TABLE_FOR_EACH (pg, port_group_table) {
> -        expr_const_sets_add(port_groups, pg->name,
> -                            (const char *const *) pg->ports,
> -                            pg->n_ports, false);
> -    }
> -}
> -
> -static void
> -port_groups_update(const struct sbrec_port_group_table *port_group_table,
> -                   struct shash *port_groups, struct sset *new,
> -                   struct sset *deleted, struct sset *updated)
> -{
> -    const struct sbrec_port_group *pg;
> -    SBREC_PORT_GROUP_TABLE_FOR_EACH_TRACKED (pg, port_group_table) {
> -        if (sbrec_port_group_is_deleted(pg)) {
> -            expr_const_sets_remove(port_groups, pg->name);
> -            sset_add(deleted, pg->name);
> -        } else {
> -            expr_const_sets_add(port_groups, pg->name,
> -                                (const char *const *) pg->ports,
> -                                pg->n_ports, false);
> -            if (sbrec_port_group_is_new(pg)) {
> -                sset_add(new, pg->name);
> -            } else {
> -                sset_add(updated, pg->name);
> -            }
> -        }
> -    }
> -}
> -
> -static void
> -update_ssl_config(const struct ovsrec_ssl_table *ssl_table)
> -{
> -    const struct ovsrec_ssl *ssl = ovsrec_ssl_table_first(ssl_table);
> -
> -    if (ssl) {
> -        stream_ssl_set_key_and_cert(ssl->private_key, ssl->certificate);
> -        stream_ssl_set_ca_cert_file(ssl->ca_cert, ssl->bootstrap_ca_cert);
> -    }
> -}
> -
> -static int
> -get_ofctrl_probe_interval(struct ovsdb_idl *ovs_idl)
> -{
> -    const struct ovsrec_open_vswitch *cfg =
> ovsrec_open_vswitch_first(ovs_idl);
> -    return smap_get_int(&cfg->external_ids,
> -                        "ovn-openflow-probe-interval",
> -                        OFCTRL_DEFAULT_PROBE_INTERVAL_SEC);
> -}
> -
> -/* Retrieves the pointer to the OVN Southbound database from 'ovs_idl' and
> - * updates 'sbdb_idl' with that pointer. */
> -static void
> -update_sb_db(struct ovsdb_idl *ovs_idl, struct ovsdb_idl *ovnsb_idl)
> -{
> -    const struct ovsrec_open_vswitch *cfg =
> ovsrec_open_vswitch_first(ovs_idl);
> -
> -    /* Set remote based on user configuration. */
> -    const char *remote = NULL;
> -    if (cfg) {
> -        remote = smap_get(&cfg->external_ids, "ovn-remote");
> -    }
> -    ovsdb_idl_set_remote(ovnsb_idl, remote, true);
> -
> -    /* Set probe interval, based on user configuration and the remote. */
> -    int default_interval = (remote &&
> !stream_or_pstream_needs_probes(remote)
> -                            ? 0 : DEFAULT_PROBE_INTERVAL_MSEC);
> -    int interval = smap_get_int(&cfg->external_ids,
> -                                "ovn-remote-probe-interval",
> default_interval);
> -    ovsdb_idl_set_probe_interval(ovnsb_idl, interval);
> -}
> -
> -static void
> -update_ct_zones(const struct sset *lports, const struct hmap
> *local_datapaths,
> -                struct simap *ct_zones, unsigned long *ct_zone_bitmap,
> -                struct shash *pending_ct_zones)
> -{
> -    struct simap_node *ct_zone, *ct_zone_next;
> -    int scan_start = 1;
> -    const char *user;
> -    struct sset all_users = SSET_INITIALIZER(&all_users);
> -
> -    SSET_FOR_EACH(user, lports) {
> -        sset_add(&all_users, user);
> -    }
> -
> -    /* Local patched datapath (gateway routers) need zones assigned. */
> -    const struct local_datapath *ld;
> -    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
> -        /* XXX Add method to limit zone assignment to logical router
> -         * datapaths with NAT */
> -        char *dnat = alloc_nat_zone_key(&ld->datapath->header_.uuid,
> "dnat");
> -        char *snat = alloc_nat_zone_key(&ld->datapath->header_.uuid,
> "snat");
> -        sset_add(&all_users, dnat);
> -        sset_add(&all_users, snat);
> -        free(dnat);
> -        free(snat);
> -    }
> -
> -    /* Delete zones that do not exist in above sset. */
> -    SIMAP_FOR_EACH_SAFE(ct_zone, ct_zone_next, ct_zones) {
> -        if (!sset_contains(&all_users, ct_zone->name)) {
> -            VLOG_DBG("removing ct zone %"PRId32" for '%s'",
> -                     ct_zone->data, ct_zone->name);
> -
> -            struct ct_zone_pending_entry *pending = xmalloc(sizeof
> *pending);
> -            pending->state = CT_ZONE_DB_QUEUED; /* Skip flushing zone. */
> -            pending->zone = ct_zone->data;
> -            pending->add = false;
> -            shash_add(pending_ct_zones, ct_zone->name, pending);
> -
> -            bitmap_set0(ct_zone_bitmap, ct_zone->data);
> -            simap_delete(ct_zones, ct_zone);
> -        }
> -    }
> -
> -    /* xxx This is wasteful to assign a zone to each port--even if no
> -     * xxx security policy is applied. */
> -
> -    /* Assign a unique zone id for each logical port and two zones
> -     * to a gateway router. */
> -    SSET_FOR_EACH(user, &all_users) {
> -        int zone;
> -
> -        if (simap_contains(ct_zones, user)) {
> -            continue;
> -        }
> -
> -        /* We assume that there are 64K zones and that we own them all. */
> -        zone = bitmap_scan(ct_zone_bitmap, 0, scan_start, MAX_CT_ZONES +
> 1);
> -        if (zone == MAX_CT_ZONES + 1) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -            VLOG_WARN_RL(&rl, "exhausted all ct zones");
> -            return;
> -        }
> -        scan_start = zone + 1;
> -
> -        VLOG_DBG("assigning ct zone %"PRId32" to '%s'", zone, user);
> -
> -        struct ct_zone_pending_entry *pending = xmalloc(sizeof *pending);
> -        pending->state = CT_ZONE_OF_QUEUED;
> -        pending->zone = zone;
> -        pending->add = true;
> -        shash_add(pending_ct_zones, user, pending);
> -
> -        bitmap_set1(ct_zone_bitmap, zone);
> -        simap_put(ct_zones, user, zone);
> -    }
> -
> -    sset_destroy(&all_users);
> -}
> -
> -static void
> -commit_ct_zones(const struct ovsrec_bridge *br_int,
> -                struct shash *pending_ct_zones)
> -{
> -    struct smap new_ids;
> -    smap_clone(&new_ids, &br_int->external_ids);
> -
> -    bool updated = false;
> -    struct shash_node *iter;
> -    SHASH_FOR_EACH(iter, pending_ct_zones) {
> -        struct ct_zone_pending_entry *ctzpe = iter->data;
> -
> -        /* The transaction is open, so any pending entries in the
> -         * CT_ZONE_DB_QUEUED must be sent and any in CT_ZONE_DB_QUEUED
> -         * need to be retried. */
> -        if (ctzpe->state != CT_ZONE_DB_QUEUED
> -            && ctzpe->state != CT_ZONE_DB_SENT) {
> -            continue;
> -        }
> -
> -        char *user_str = xasprintf("ct-zone-%s", iter->name);
> -        if (ctzpe->add) {
> -            char *zone_str = xasprintf("%"PRId32, ctzpe->zone);
> -            smap_replace(&new_ids, user_str, zone_str);
> -            free(zone_str);
> -        } else {
> -            smap_remove(&new_ids, user_str);
> -        }
> -        free(user_str);
> -
> -        ctzpe->state = CT_ZONE_DB_SENT;
> -        updated = true;
> -    }
> -
> -    if (updated) {
> -        ovsrec_bridge_verify_external_ids(br_int);
> -        ovsrec_bridge_set_external_ids(br_int, &new_ids);
> -    }
> -    smap_destroy(&new_ids);
> -}
> -
> -static void
> -restore_ct_zones(const struct ovsrec_bridge_table *bridge_table,
> -                 const struct ovsrec_open_vswitch_table *ovs_table,
> -                 struct simap *ct_zones, unsigned long *ct_zone_bitmap)
> -{
> -    const struct ovsrec_open_vswitch *cfg;
> -    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> -    if (!cfg) {
> -        return;
> -    }
> -
> -    const struct ovsrec_bridge *br_int;
> -    br_int = get_bridge(bridge_table, br_int_name(cfg));
> -    if (!br_int) {
> -        /* If the integration bridge hasn't been defined, assume that
> -         * any existing ct-zone definitions aren't valid. */
> -        return;
> -    }
> -
> -    struct smap_node *node;
> -    SMAP_FOR_EACH(node, &br_int->external_ids) {
> -        if (strncmp(node->key, "ct-zone-", 8)) {
> -            continue;
> -        }
> -
> -        const char *user = node->key + 8;
> -        int zone = atoi(node->value);
> -
> -        if (user[0] && zone) {
> -            VLOG_DBG("restoring ct zone %"PRId32" for '%s'", zone, user);
> -            bitmap_set1(ct_zone_bitmap, zone);
> -            simap_put(ct_zones, user, zone);
> -        }
> -    }
> -}
> -
> -static int64_t
> -get_nb_cfg(const struct sbrec_sb_global_table *sb_global_table)
> -{
> -    const struct sbrec_sb_global *sb
> -        = sbrec_sb_global_table_first(sb_global_table);
> -    return sb ? sb->nb_cfg : 0;
> -}
> -
> -static const char *
> -get_transport_zones(const struct ovsrec_open_vswitch_table *ovs_table)
> -{
> -    const struct ovsrec_open_vswitch *cfg
> -        = ovsrec_open_vswitch_table_first(ovs_table);
> -    return smap_get_def(&cfg->external_ids, "ovn-transport-zones", "");
> -}
> -
> -static void
> -ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> -{
> -    /* We do not monitor all tables by default, so modules must register
> -     * their interest explicitly. */
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_type);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_options);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_fail_mode);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_other_config);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_external_ids);
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_ssl);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_bootstrap_ca_cert);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_ca_cert);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_certificate);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_ssl_col_private_key);
> -    chassis_register_ovs_idl(ovs_idl);
> -    encaps_register_ovs_idl(ovs_idl);
> -    binding_register_ovs_idl(ovs_idl);
> -    bfd_register_ovs_idl(ovs_idl);
> -    physical_register_ovs_idl(ovs_idl);
> -}
> -
> -#define SB_NODES \
> -    SB_NODE(chassis, "chassis") \
> -    SB_NODE(encap, "encap") \
> -    SB_NODE(address_set, "address_set") \
> -    SB_NODE(port_group, "port_group") \
> -    SB_NODE(multicast_group, "multicast_group") \
> -    SB_NODE(datapath_binding, "datapath_binding") \
> -    SB_NODE(port_binding, "port_binding") \
> -    SB_NODE(mac_binding, "mac_binding") \
> -    SB_NODE(logical_flow, "logical_flow") \
> -    SB_NODE(dhcp_options, "dhcp_options") \
> -    SB_NODE(dhcpv6_options, "dhcpv6_options") \
> -    SB_NODE(dns, "dns")
> -
> -enum sb_engine_node {
> -#define SB_NODE(NAME, NAME_STR) SB_##NAME,
> -    SB_NODES
> -#undef SB_NODE
> -};
> -
> -#define SB_NODE(NAME, NAME_STR) ENGINE_FUNC_SB(NAME);
> -    SB_NODES
> -#undef SB_NODE
> -
> -#define OVS_NODES \
> -    OVS_NODE(open_vswitch, "open_vswitch") \
> -    OVS_NODE(bridge, "bridge") \
> -    OVS_NODE(port, "port") \
> -    OVS_NODE(qos, "qos")
> -
> -enum ovs_engine_node {
> -#define OVS_NODE(NAME, NAME_STR) OVS_##NAME,
> -    OVS_NODES
> -#undef OVS_NODE
> -};
> -
> -#define OVS_NODE(NAME, NAME_STR) ENGINE_FUNC_OVS(NAME);
> -    OVS_NODES
> -#undef OVS_NODE
> -
> -struct ed_type_ofctrl_is_connected {
> -    bool connected;
> -};
> -
> -static void
> -en_ofctrl_is_connected_init(struct engine_node *node)
> -{
> -    struct ed_type_ofctrl_is_connected *data =
> -        (struct ed_type_ofctrl_is_connected *)node->data;
> -    data->connected = false;
> -}
> -
> -static void
> -en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED)
> -{
> -}
> -
> -static void
> -en_ofctrl_is_connected_run(struct engine_node *node)
> -{
> -    struct ed_type_ofctrl_is_connected *data =
> -        (struct ed_type_ofctrl_is_connected *)node->data;
> -    if (data->connected != ofctrl_is_connected()) {
> -        data->connected = !data->connected;
> -        node->changed = true;
> -        return;
> -    }
> -    node->changed = false;
> -}
> -
> -struct ed_type_addr_sets {
> -    struct shash addr_sets;
> -    bool change_tracked;
> -    struct sset new;
> -    struct sset deleted;
> -    struct sset updated;
> -};
> -
> -static void
> -en_addr_sets_init(struct engine_node *node)
> -{
> -    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
> -    shash_init(&as->addr_sets);
> -    as->change_tracked = false;
> -    sset_init(&as->new);
> -    sset_init(&as->deleted);
> -    sset_init(&as->updated);
> -}
> -
> -static void
> -en_addr_sets_cleanup(struct engine_node *node)
> -{
> -    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
> -    expr_const_sets_destroy(&as->addr_sets);
> -    shash_destroy(&as->addr_sets);
> -    sset_destroy(&as->new);
> -    sset_destroy(&as->deleted);
> -    sset_destroy(&as->updated);
> -}
> -
> -static void
> -en_addr_sets_run(struct engine_node *node)
> -{
> -    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
> -
> -    sset_clear(&as->new);
> -    sset_clear(&as->deleted);
> -    sset_clear(&as->updated);
> -    expr_const_sets_destroy(&as->addr_sets);
> -
> -    struct sbrec_address_set_table *as_table =
> -        (struct sbrec_address_set_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_address_set", node));
> -
> -    addr_sets_init(as_table, &as->addr_sets);
> -
> -    as->change_tracked = false;
> -    node->changed = true;
> -}
> -
> -static bool
> -addr_sets_sb_address_set_handler(struct engine_node *node)
> -{
> -    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
> -
> -    sset_clear(&as->new);
> -    sset_clear(&as->deleted);
> -    sset_clear(&as->updated);
> -
> -    struct sbrec_address_set_table *as_table =
> -        (struct sbrec_address_set_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_address_set", node));
> -
> -    addr_sets_update(as_table, &as->addr_sets, &as->new,
> -                     &as->deleted, &as->updated);
> -
> -    node->changed = !sset_is_empty(&as->new) ||
> !sset_is_empty(&as->deleted)
> -                    || !sset_is_empty(&as->updated);
> -
> -    as->change_tracked = true;
> -    node->changed = true;
> -    return true;
> -}
> -
> -struct ed_type_port_groups{
> -    struct shash port_groups;
> -    bool change_tracked;
> -    struct sset new;
> -    struct sset deleted;
> -    struct sset updated;
> -};
> -
> -static void
> -en_port_groups_init(struct engine_node *node)
> -{
> -    struct ed_type_port_groups *pg = (struct ed_type_port_groups
> *)node->data;
> -    shash_init(&pg->port_groups);
> -    pg->change_tracked = false;
> -    sset_init(&pg->new);
> -    sset_init(&pg->deleted);
> -    sset_init(&pg->updated);
> -}
> -
> -static void
> -en_port_groups_cleanup(struct engine_node *node)
> -{
> -    struct ed_type_port_groups *pg = (struct ed_type_port_groups
> *)node->data;
> -    expr_const_sets_destroy(&pg->port_groups);
> -    shash_destroy(&pg->port_groups);
> -    sset_destroy(&pg->new);
> -    sset_destroy(&pg->deleted);
> -    sset_destroy(&pg->updated);
> -}
> -
> -static void
> -en_port_groups_run(struct engine_node *node)
> -{
> -    struct ed_type_port_groups *pg = (struct ed_type_port_groups
> *)node->data;
> -
> -    sset_clear(&pg->new);
> -    sset_clear(&pg->deleted);
> -    sset_clear(&pg->updated);
> -    expr_const_sets_destroy(&pg->port_groups);
> -
> -    struct sbrec_port_group_table *pg_table =
> -        (struct sbrec_port_group_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_port_group", node));
> -
> -    port_groups_init(pg_table, &pg->port_groups);
> -
> -    pg->change_tracked = false;
> -    node->changed = true;
> -}
> -
> -static bool
> -port_groups_sb_port_group_handler(struct engine_node *node)
> -{
> -    struct ed_type_port_groups *pg = (struct ed_type_port_groups
> *)node->data;
> -
> -    sset_clear(&pg->new);
> -    sset_clear(&pg->deleted);
> -    sset_clear(&pg->updated);
> -
> -    struct sbrec_port_group_table *pg_table =
> -        (struct sbrec_port_group_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_port_group", node));
> -
> -    port_groups_update(pg_table, &pg->port_groups, &pg->new,
> -                     &pg->deleted, &pg->updated);
> -
> -    node->changed = !sset_is_empty(&pg->new) ||
> !sset_is_empty(&pg->deleted)
> -                    || !sset_is_empty(&pg->updated);
> -
> -    pg->change_tracked = true;
> -    node->changed = true;
> -    return true;
> -}
> -
> -struct ed_type_runtime_data {
> -    /* Contains "struct local_datapath" nodes. */
> -    struct hmap local_datapaths;
> -
> -    /* Contains the name of each logical port resident on the local
> -     * hypervisor.  These logical ports include the VIFs (and their child
> -     * logical ports, if any) that belong to VMs running on the
> hypervisor,
> -     * l2gateway ports for which options:l2gateway-chassis designates the
> -     * local hypervisor, and localnet ports. */
> -    struct sset local_lports;
> -
> -    /* Contains the same ports as local_lports, but in the format:
> -     * <datapath-tunnel-key>_<port-tunnel-key> */
> -    struct sset local_lport_ids;
> -    struct sset active_tunnels;
> -
> -    /* connection tracking zones. */
> -    unsigned long ct_zone_bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)];
> -    struct shash pending_ct_zones;
> -    struct simap ct_zones;
> -};
> -
> -static void
> -en_runtime_data_init(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)node->data;
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    hmap_init(&data->local_datapaths);
> -    sset_init(&data->local_lports);
> -    sset_init(&data->local_lport_ids);
> -    sset_init(&data->active_tunnels);
> -    shash_init(&data->pending_ct_zones);
> -    simap_init(&data->ct_zones);
> -
> -    /* Initialize connection tracking zones. */
> -    memset(data->ct_zone_bitmap, 0, sizeof data->ct_zone_bitmap);
> -    bitmap_set1(data->ct_zone_bitmap, 0); /* Zone 0 is reserved. */
> -    restore_ct_zones(bridge_table, ovs_table,
> -                     &data->ct_zones, data->ct_zone_bitmap);
> -}
> -
> -static void
> -en_runtime_data_cleanup(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)node->data;
> -
> -    sset_destroy(&data->local_lports);
> -    sset_destroy(&data->local_lport_ids);
> -    sset_destroy(&data->active_tunnels);
> -    struct local_datapath *cur_node, *next_node;
> -    HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
> -                        &data->local_datapaths) {
> -        free(cur_node->peer_ports);
> -        hmap_remove(&data->local_datapaths, &cur_node->hmap_node);
> -        free(cur_node);
> -    }
> -    hmap_destroy(&data->local_datapaths);
> -
> -    simap_destroy(&data->ct_zones);
> -    shash_destroy(&data->pending_ct_zones);
> -}
> -
> -static void
> -en_runtime_data_run(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)node->data;
> -    struct hmap *local_datapaths = &data->local_datapaths;
> -    struct sset *local_lports = &data->local_lports;
> -    struct sset *local_lport_ids = &data->local_lport_ids;
> -    struct sset *active_tunnels = &data->active_tunnels;
> -    unsigned long *ct_zone_bitmap = data->ct_zone_bitmap;
> -    struct shash *pending_ct_zones = &data->pending_ct_zones;
> -    struct simap *ct_zones = &data->ct_zones;
> -
> -    static bool first_run = true;
> -    if (first_run) {
> -        /* don't cleanup since there is no data yet */
> -        first_run = false;
> -    } else {
> -        struct local_datapath *cur_node, *next_node;
> -        HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
> local_datapaths) {
> -            free(cur_node->peer_ports);
> -            hmap_remove(local_datapaths, &cur_node->hmap_node);
> -            free(cur_node);
> -        }
> -        hmap_clear(local_datapaths);
> -        sset_destroy(local_lports);
> -        sset_destroy(local_lport_ids);
> -        sset_destroy(active_tunnels);
> -        sset_init(local_lports);
> -        sset_init(local_lport_ids);
> -        sset_init(active_tunnels);
> -    }
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const char *chassis_id = get_ovs_chassis_id(ovs_table);
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -
> -    ovs_assert(br_int && chassis_id);
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -
> -    const struct sbrec_chassis *chassis
> -        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
> -    ovs_assert(chassis);
> -
> -    struct ed_type_ofctrl_is_connected *ed_ofctrl_is_connected =
> -        (struct ed_type_ofctrl_is_connected *)engine_get_input(
> -            "ofctrl_is_connected", node)->data;
> -    if (ed_ofctrl_is_connected->connected) {
> -        /* Calculate the active tunnels only if have an an active
> -         * OpenFlow connection to br-int.
> -         * If we don't have a connection to br-int, it could mean
> -         * ovs-vswitchd is down for some reason and the BFD status
> -         * in the Interface rows could be stale. So its better to
> -         * consider 'active_tunnels' set to be empty if it's not
> -         * connected. */
> -        bfd_calculate_active_tunnels(br_int, active_tunnels);
> -    }
> -
> -    struct ovsrec_port_table *port_table =
> -        (struct ovsrec_port_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_port", node));
> -
> -    struct ovsrec_qos_table *qos_table =
> -        (struct ovsrec_qos_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_qos", node));
> -
> -    struct sbrec_port_binding_table *pb_table =
> -        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_port_binding", node));
> -
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_datapath_binding", node),
> -                "key");
> -
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "name");
> -
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "datapath");
> -
> -    binding_run(engine_get_context()->ovnsb_idl_txn,
> -                engine_get_context()->ovs_idl_txn,
> -                sbrec_datapath_binding_by_key,
> -                sbrec_port_binding_by_datapath,
> -                sbrec_port_binding_by_name,
> -                port_table, qos_table, pb_table,
> -                br_int, chassis,
> -                active_tunnels, local_datapaths,
> -                local_lports, local_lport_ids);
> -
> -    update_ct_zones(local_lports, local_datapaths, ct_zones,
> -                    ct_zone_bitmap, pending_ct_zones);
> -
> -    node->changed = true;
> -}
> -
> -static bool
> -runtime_data_sb_port_binding_handler(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)node->data;
> -    struct sset *local_lports = &data->local_lports;
> -    struct sset *active_tunnels = &data->active_tunnels;
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const char *chassis_id = chassis_get_id();
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -
> -    ovs_assert(br_int && chassis_id);
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -
> -    const struct sbrec_chassis *chassis
> -        = chassis_lookup_by_name(sbrec_chassis_by_name, chassis_id);
> -    ovs_assert(chassis);
> -
> -    struct sbrec_port_binding_table *pb_table =
> -        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_port_binding", node));
> -
> -    bool changed = binding_evaluate_port_binding_changes(
> -        pb_table, br_int, chassis, active_tunnels, local_lports);
> -
> -    return !changed;
> -}
> -
> -struct ed_type_mff_ovn_geneve {
> -    enum mf_field_id mff_ovn_geneve;
> -};
> -
> -static void
> -en_mff_ovn_geneve_init(struct engine_node *node)
> -{
> -    struct ed_type_mff_ovn_geneve *data =
> -        (struct ed_type_mff_ovn_geneve *)node->data;
> -    data->mff_ovn_geneve = 0;
> -}
> -
> -static void
> -en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED)
> -{
> -}
> -
> -static void
> -en_mff_ovn_geneve_run(struct engine_node *node)
> -{
> -    struct ed_type_mff_ovn_geneve *data =
> -        (struct ed_type_mff_ovn_geneve *)node->data;
> -    enum mf_field_id mff_ovn_geneve = ofctrl_get_mf_field_id();
> -    if (data->mff_ovn_geneve != mff_ovn_geneve) {
> -        data->mff_ovn_geneve = mff_ovn_geneve;
> -        node->changed = true;
> -        return;
> -    }
> -    node->changed = false;
> -}
> -
> -struct ed_type_flow_output {
> -    /* desired flows */
> -    struct ovn_desired_flow_table flow_table;
> -    /* group ids for load balancing */
> -    struct ovn_extend_table group_table;
> -    /* meter ids for QoS */
> -    struct ovn_extend_table meter_table;
> -    /* conjunction id offset */
> -    uint32_t conj_id_ofs;
> -    /* lflow resource cross reference */
> -    struct lflow_resource_ref lflow_resource_ref;
> -};
> -
> -static void
> -en_flow_output_init(struct engine_node *node)
> -{
> -    struct ed_type_flow_output *data =
> -        (struct ed_type_flow_output *)node->data;
> -    ovn_desired_flow_table_init(&data->flow_table);
> -    ovn_extend_table_init(&data->group_table);
> -    ovn_extend_table_init(&data->meter_table);
> -    data->conj_id_ofs = 1;
> -    lflow_resource_init(&data->lflow_resource_ref);
> -}
> -
> -static void
> -en_flow_output_cleanup(struct engine_node *node)
> -{
> -    struct ed_type_flow_output *data =
> -        (struct ed_type_flow_output *)node->data;
> -    ovn_desired_flow_table_destroy(&data->flow_table);
> -    ovn_extend_table_destroy(&data->group_table);
> -    ovn_extend_table_destroy(&data->meter_table);
> -    lflow_resource_destroy(&data->lflow_resource_ref);
> -}
> -
> -static void
> -en_flow_output_run(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *rt_data =
> -        (struct ed_type_runtime_data *)engine_get_input(
> -            "runtime_data", node)->data;
> -    struct hmap *local_datapaths = &rt_data->local_datapaths;
> -    struct sset *local_lports = &rt_data->local_lports;
> -    struct sset *local_lport_ids = &rt_data->local_lport_ids;
> -    struct sset *active_tunnels = &rt_data->active_tunnels;
> -    struct simap *ct_zones = &rt_data->ct_zones;
> -
> -    struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
> -        (struct ed_type_mff_ovn_geneve *)engine_get_input(
> -            "mff_ovn_geneve", node)->data;
> -    enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -    const char *chassis_id = chassis_get_id();
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -    struct ed_type_addr_sets *as_data =
> -        (struct ed_type_addr_sets *)engine_get_input("addr_sets",
> node)->data;
> -    struct shash *addr_sets = &as_data->addr_sets;
> -
> -    struct ed_type_port_groups *pg_data =
> -        (struct ed_type_port_groups *)engine_get_input(
> -            "port_groups", node)->data;
> -    struct shash *port_groups = &pg_data->port_groups;
> -
> -    const struct sbrec_chassis *chassis = NULL;
> -    if (chassis_id) {
> -        chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
> chassis_id);
> -    }
> -
> -    ovs_assert(br_int && chassis);
> -
> -    struct ed_type_flow_output *fo =
> -        (struct ed_type_flow_output *)node->data;
> -    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
> -    struct ovn_extend_table *group_table = &fo->group_table;
> -    struct ovn_extend_table *meter_table = &fo->meter_table;
> -    uint32_t *conj_id_ofs = &fo->conj_id_ofs;
> -    struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
> -
> -    static bool first_run = true;
> -    if (first_run) {
> -        first_run = false;
> -    } else {
> -        ovn_desired_flow_table_clear(flow_table);
> -        ovn_extend_table_clear(group_table, false /* desired */);
> -        ovn_extend_table_clear(meter_table, false /* desired */);
> -        lflow_resource_clear(lfrr);
> -    }
> -
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_multicast_group", node),
> -                "name_datapath");
> -
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "name");
> -
> -    struct sbrec_dhcp_options_table *dhcp_table =
> -        (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_dhcp_options", node));
> -
> -    struct sbrec_dhcpv6_options_table *dhcpv6_table =
> -        (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_dhcpv6_options", node));
> -
> -    struct sbrec_logical_flow_table *logical_flow_table =
> -        (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_logical_flow", node));
> -
> -    struct sbrec_mac_binding_table *mac_binding_table =
> -        (struct sbrec_mac_binding_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_mac_binding", node));
> -
> -    *conj_id_ofs = 1;
> -    lflow_run(sbrec_multicast_group_by_name_datapath,
> -              sbrec_port_binding_by_name,
> -              dhcp_table, dhcpv6_table,
> -              logical_flow_table,
> -              mac_binding_table,
> -              chassis, local_datapaths, addr_sets,
> -              port_groups, active_tunnels, local_lport_ids,
> -              flow_table, group_table, meter_table, lfrr,
> -              conj_id_ofs);
> -
> -    struct sbrec_multicast_group_table *multicast_group_table =
> -        (struct sbrec_multicast_group_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_multicast_group", node));
> -
> -    struct sbrec_port_binding_table *port_binding_table =
> -        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_port_binding", node));
> -
> -    physical_run(sbrec_port_binding_by_name,
> -                 multicast_group_table,
> -                 port_binding_table,
> -                 mff_ovn_geneve,
> -                 br_int, chassis, ct_zones,
> -                 local_datapaths, local_lports,
> -                 active_tunnels,
> -                 flow_table);
> -
> -    node->changed = true;
> -}
> -
> -static bool
> -flow_output_sb_logical_flow_handler(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)engine_get_input(
> -                "runtime_data", node)->data;
> -    struct hmap *local_datapaths = &data->local_datapaths;
> -    struct sset *local_lport_ids = &data->local_lport_ids;
> -    struct sset *active_tunnels = &data->active_tunnels;
> -    struct ed_type_addr_sets *as_data =
> -        (struct ed_type_addr_sets *)engine_get_input("addr_sets",
> node)->data;
> -    struct shash *addr_sets = &as_data->addr_sets;
> -
> -    struct ed_type_port_groups *pg_data =
> -        (struct ed_type_port_groups *)engine_get_input(
> -            "port_groups", node)->data;
> -    struct shash *port_groups = &pg_data->port_groups;
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -    const char *chassis_id = chassis_get_id();
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -
> -    const struct sbrec_chassis *chassis = NULL;
> -    if (chassis_id) {
> -        chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
> chassis_id);
> -    }
> -
> -    ovs_assert(br_int && chassis);
> -
> -    struct ed_type_flow_output *fo =
> -        (struct ed_type_flow_output *)node->data;
> -    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
> -    struct ovn_extend_table *group_table = &fo->group_table;
> -    struct ovn_extend_table *meter_table = &fo->meter_table;
> -    uint32_t *conj_id_ofs = &fo->conj_id_ofs;
> -    struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
> -
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_multicast_group", node),
> -                "name_datapath");
> -
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "name");
> -
> -    struct sbrec_dhcp_options_table *dhcp_table =
> -        (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_dhcp_options", node));
> -
> -    struct sbrec_dhcpv6_options_table *dhcpv6_table =
> -        (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_dhcpv6_options", node));
> -
> -    struct sbrec_logical_flow_table *logical_flow_table =
> -        (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_logical_flow", node));
> -
> -    bool handled = lflow_handle_changed_flows(
> -              sbrec_multicast_group_by_name_datapath,
> -              sbrec_port_binding_by_name,
> -              dhcp_table, dhcpv6_table,
> -              logical_flow_table,
> -              local_datapaths, chassis, addr_sets,
> -              port_groups, active_tunnels, local_lport_ids,
> -              flow_table, group_table, meter_table, lfrr,
> -              conj_id_ofs);
> -
> -    node->changed = true;
> -    return handled;
> -}
> -
> -static bool
> -flow_output_sb_mac_binding_handler(struct engine_node *node)
> -{
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "name");
> -
> -    struct sbrec_mac_binding_table *mac_binding_table =
> -        (struct sbrec_mac_binding_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_mac_binding", node));
> -
> -    struct ed_type_flow_output *fo =
> -        (struct ed_type_flow_output *)node->data;
> -    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
> -
> -    lflow_handle_changed_neighbors(sbrec_port_binding_by_name,
> -            mac_binding_table, flow_table);
> -
> -    node->changed = true;
> -    return true;
> -}
> -
> -static bool
> -flow_output_sb_port_binding_handler(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)engine_get_input(
> -                "runtime_data", node)->data;
> -    struct hmap *local_datapaths = &data->local_datapaths;
> -    struct sset *active_tunnels = &data->active_tunnels;
> -    struct simap *ct_zones = &data->ct_zones;
> -
> -    struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
> -        (struct ed_type_mff_ovn_geneve *)engine_get_input(
> -            "mff_ovn_geneve", node)->data;
> -    enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -    const char *chassis_id = chassis_get_id();
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -    const struct sbrec_chassis *chassis = NULL;
> -    if (chassis_id) {
> -        chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
> chassis_id);
> -    }
> -    ovs_assert(br_int && chassis);
> -
> -    struct ed_type_flow_output *fo =
> -        (struct ed_type_flow_output *)node->data;
> -    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
> -
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "name");
> -
> -    struct sbrec_port_binding_table *port_binding_table =
> -        (struct sbrec_port_binding_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_port_binding", node));
> -
> -    /* XXX: now we handle port-binding changes for physical flow
> processing
> -     * only, but port-binding change can have impact to logical flow
> -     * processing, too, in below circumstances:
> -     *
> -     *  - When a port-binding for a lport is inserted/deleted but the
> lflow
> -     *    using that lport doesn't change.
> -     *
> -     *    This can happen only when the lport name is used by ACL match
> -     *    condition, which is specified by user. Even in that case, if
> the port
> -     *    is actually bound on the current chassis it will trigger
> recompute on
> -     *    that chassis since ovs interface would be updated. So the only
> -     *    situation this would have real impact is when user defines an
> ACL
> -     *    that includes lport that is not on current chassis, and there
> is a
> -     *    port-binding creation/deletion related to that lport.e.g.: an
> ACL is
> -     *    defined:
> -     *
> -     *    to-lport 1000 'outport=="A" && inport=="B"' allow-related
> -     *
> -     *    If "A" is on current chassis, but "B" is lport that hasn't been
> -     *    created yet. When a lport "B" is created and bound on another
> -     *    chassis, the ACL will not take effect on the current chassis
> until a
> -     *    recompute is triggered later. This case doesn't seem to be a
> problem
> -     *    for real world use cases because usually lport is created before
> -     *    being referenced by name in ACLs.
> -     *
> -     *  - When is_chassis_resident(<lport>) is used in lflow. In this
> case the
> -     *    port binding is not a regular VIF. It can be either "patch" or
> -     *    "external", with ha-chassis-group assigned.  In current
> -     *    "runtime_data" handling, port-binding changes for these types
> always
> -     *    trigger recomputing. So it is fine even if we do not handle it
> here.
> -     *    (due to the ovsdb tracking support for referenced table changes,
> -     *    ha-chassis-group changes will appear as port-binding change).
> -     *
> -     *  - When a mac-binding doesn't change but the port-binding related
> to
> -     *    that mac-binding is deleted. In this case the neighbor flow
> generated
> -     *    for the mac-binding should be deleted. This would not cause any
> real
> -     *    issue for now, since the port-binding related to mac-binding is
> -     *    always logical router port, and any change to logical router
> port
> -     *    would just trigger recompute.
> -     *
> -     * Although there is no correctness issue so far (except the unusual
> ACL
> -     * use case, which doesn't seem to be a real problem), it might be
> better
> -     * to handle this more gracefully, without the need to consider these
> -     * tricky scenarios.  One approach is to maintain a mapping between
> lport
> -     * names and the lflows that uses them, and reprocess the related
> lflows
> -     * when related port-bindings change.
> -     */
> -    physical_handle_port_binding_changes(
> -            sbrec_port_binding_by_name,
> -            port_binding_table, mff_ovn_geneve,
> -            chassis, ct_zones, local_datapaths,
> -            active_tunnels, flow_table);
> -
> -    node->changed = true;
> -    return true;
> -}
> -
> -static bool
> -flow_output_sb_multicast_group_handler(struct engine_node *node)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)engine_get_input(
> -                "runtime_data", node)->data;
> -    struct hmap *local_datapaths = &data->local_datapaths;
> -    struct simap *ct_zones = &data->ct_zones;
> -
> -    struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
> -        (struct ed_type_mff_ovn_geneve *)engine_get_input(
> -            "mff_ovn_geneve", node)->data;
> -    enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -    const char *chassis_id = chassis_get_id();
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -    const struct sbrec_chassis *chassis = NULL;
> -    if (chassis_id) {
> -        chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
> chassis_id);
> -    }
> -    ovs_assert(br_int && chassis);
> -
> -    struct ed_type_flow_output *fo =
> -        (struct ed_type_flow_output *)node->data;
> -    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
> -
> -    struct sbrec_multicast_group_table *multicast_group_table =
> -        (struct sbrec_multicast_group_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_multicast_group", node));
> -
> -    physical_handle_mc_group_changes(multicast_group_table,
> -            mff_ovn_geneve, chassis, ct_zones, local_datapaths,
> -            flow_table);
> -
> -    node->changed = true;
> -    return true;
> -
> -}
> -
> -static bool
> -_flow_output_resource_ref_handler(struct engine_node *node,
> -                                 enum ref_type ref_type)
> -{
> -    struct ed_type_runtime_data *data =
> -        (struct ed_type_runtime_data *)engine_get_input(
> -                "runtime_data", node)->data;
> -    struct hmap *local_datapaths = &data->local_datapaths;
> -    struct sset *local_lport_ids = &data->local_lport_ids;
> -    struct sset *active_tunnels = &data->active_tunnels;
> -
> -    struct ed_type_addr_sets *as_data =
> -        (struct ed_type_addr_sets *)engine_get_input("addr_sets",
> node)->data;
> -    struct shash *addr_sets = &as_data->addr_sets;
> -
> -    struct ed_type_port_groups *pg_data =
> -        (struct ed_type_port_groups *)engine_get_input(
> -            "port_groups", node)->data;
> -    struct shash *port_groups = &pg_data->port_groups;
> -
> -    struct ovsrec_open_vswitch_table *ovs_table =
> -        (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_open_vswitch", node));
> -    struct ovsrec_bridge_table *bridge_table =
> -        (struct ovsrec_bridge_table *)EN_OVSDB_GET(
> -            engine_get_input("OVS_bridge", node));
> -    const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> ovs_table);
> -    const char *chassis_id = chassis_get_id();
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_chassis", node),
> -                "name");
> -    const struct sbrec_chassis *chassis = NULL;
> -    if (chassis_id) {
> -        chassis = chassis_lookup_by_name(sbrec_chassis_by_name,
> chassis_id);
> -    }
> -
> -    ovs_assert(br_int && chassis);
> -
> -    struct ed_type_flow_output *fo =
> -        (struct ed_type_flow_output *)node->data;
> -    struct ovn_desired_flow_table *flow_table = &fo->flow_table;
> -    struct ovn_extend_table *group_table = &fo->group_table;
> -    struct ovn_extend_table *meter_table = &fo->meter_table;
> -    uint32_t *conj_id_ofs = &fo->conj_id_ofs;
> -    struct lflow_resource_ref *lfrr = &fo->lflow_resource_ref;
> -
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_multicast_group", node),
> -                "name_datapath");
> -
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name =
> -        engine_ovsdb_node_get_index(
> -                engine_get_input("SB_port_binding", node),
> -                "name");
> -
> -    struct sbrec_dhcp_options_table *dhcp_table =
> -        (struct sbrec_dhcp_options_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_dhcp_options", node));
> -
> -    struct sbrec_dhcpv6_options_table *dhcpv6_table =
> -        (struct sbrec_dhcpv6_options_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_dhcpv6_options", node));
> -
> -    struct sbrec_logical_flow_table *logical_flow_table =
> -        (struct sbrec_logical_flow_table *)EN_OVSDB_GET(
> -            engine_get_input("SB_logical_flow", node));
> -
> -    bool changed;
> -    const char *ref_name;
> -    struct sset *new, *updated, *deleted;
> -
> -    switch (ref_type) {
> -        case REF_TYPE_ADDRSET:
> -            /* XXX: The change_tracked check may be added to inc-proc
> -             * framework. */
> -            if (!as_data->change_tracked) {
> -                return false;
> -            }
> -            new = &as_data->new;
> -            updated = &as_data->updated;
> -            deleted = &as_data->deleted;
> -            break;
> -        case REF_TYPE_PORTGROUP:
> -            if (!pg_data->change_tracked) {
> -                return false;
> -            }
> -            new = &pg_data->new;
> -            updated = &pg_data->updated;
> -            deleted = &pg_data->deleted;
> -            break;
> -        default:
> -            OVS_NOT_REACHED();
> -    }
> -
> -
> -    SSET_FOR_EACH (ref_name, deleted) {
> -        if (!lflow_handle_changed_ref(ref_type, ref_name,
> -                    sbrec_multicast_group_by_name_datapath,
> -                    sbrec_port_binding_by_name,dhcp_table,
> -                    dhcpv6_table, logical_flow_table,
> -                    local_datapaths, chassis, addr_sets,
> -                    port_groups, active_tunnels, local_lport_ids,
> -                    flow_table, group_table, meter_table, lfrr,
> -                    conj_id_ofs, &changed)) {
> -            return false;
> -        }
> -        node->changed = changed || node->changed;
> -    }
> -    SSET_FOR_EACH (ref_name, updated) {
> -        if (!lflow_handle_changed_ref(ref_type, ref_name,
> -                    sbrec_multicast_group_by_name_datapath,
> -                    sbrec_port_binding_by_name,dhcp_table,
> -                    dhcpv6_table, logical_flow_table,
> -                    local_datapaths, chassis, addr_sets,
> -                    port_groups, active_tunnels, local_lport_ids,
> -                    flow_table, group_table, meter_table, lfrr,
> -                    conj_id_ofs, &changed)) {
> -            return false;
> -        }
> -        node->changed = changed || node->changed;
> -    }
> -    SSET_FOR_EACH (ref_name, new) {
> -        if (!lflow_handle_changed_ref(ref_type, ref_name,
> -                    sbrec_multicast_group_by_name_datapath,
> -                    sbrec_port_binding_by_name,dhcp_table,
> -                    dhcpv6_table, logical_flow_table,
> -                    local_datapaths, chassis, addr_sets,
> -                    port_groups, active_tunnels, local_lport_ids,
> -                    flow_table, group_table, meter_table, lfrr,
> -                    conj_id_ofs, &changed)) {
> -            return false;
> -        }
> -        node->changed = changed || node->changed;
> -    }
> -
> -    return true;
> -}
> -
> -static bool
> -flow_output_addr_sets_handler(struct engine_node *node)
> -{
> -    return _flow_output_resource_ref_handler(node, REF_TYPE_ADDRSET);
> -}
> -
> -static bool
> -flow_output_port_groups_handler(struct engine_node *node)
> -{
> -    return _flow_output_resource_ref_handler(node, REF_TYPE_PORTGROUP);
> -}
> -
> -struct ovn_controller_exit_args {
> -    bool *exiting;
> -    bool *restart;
> -};
> -
> -int
> -main(int argc, char *argv[])
> -{
> -    struct unixctl_server *unixctl;
> -    bool exiting;
> -    bool restart;
> -    struct ovn_controller_exit_args exit_args = {&exiting, &restart};
> -    int retval;
> -
> -    ovs_cmdl_proctitle_init(argc, argv);
> -    set_program_name(argv[0]);
> -    service_start(&argc, &argv);
> -    char *ovs_remote = parse_options(argc, argv);
> -    fatal_ignore_sigpipe();
> -
> -    daemonize_start(false);
> -
> -    retval = unixctl_server_create(NULL, &unixctl);
> -    if (retval) {
> -        exit(EXIT_FAILURE);
> -    }
> -    unixctl_command_register("exit", "", 0, 1, ovn_controller_exit,
> -                             &exit_args);
> -
> -    daemonize_complete();
> -
> -    pinctrl_init();
> -    lflow_init();
> -
> -    /* Connect to OVS OVSDB instance. */
> -    struct ovsdb_idl_loop ovs_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
> -        ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true));
> -    ctrl_register_ovs_idl(ovs_idl_loop.idl);
> -    ovsdb_idl_get_initial_snapshot(ovs_idl_loop.idl);
> -
> -    /* Configure OVN SB database. */
> -    struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
> -        ovsdb_idl_create_unconnected(&sbrec_idl_class, true));
> -    ovsdb_idl_set_leader_only(ovnsb_idl_loop.idl, false);
> -
> -    unixctl_command_register("connection-status", "", 0, 0,
> -                             ovn_controller_conn_show,
> ovnsb_idl_loop.idl);
> -
> -    struct ovsdb_idl_index *sbrec_chassis_by_name
> -        = chassis_index_create(ovnsb_idl_loop.idl);
> -    struct ovsdb_idl_index *sbrec_multicast_group_by_name_datapath
> -        = mcast_group_index_create(ovnsb_idl_loop.idl);
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name
> -        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> -                                  &sbrec_port_binding_col_logical_port);
> -    struct ovsdb_idl_index *sbrec_port_binding_by_key
> -        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
> -                                  &sbrec_port_binding_col_tunnel_key,
> -                                  &sbrec_port_binding_col_datapath);
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath
> -        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> -                                  &sbrec_port_binding_col_datapath);
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key
> -        = ovsdb_idl_index_create1(ovnsb_idl_loop.idl,
> -                                  &sbrec_datapath_binding_col_tunnel_key);
> -    struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip
> -        = ovsdb_idl_index_create2(ovnsb_idl_loop.idl,
> -                                  &sbrec_mac_binding_col_logical_port,
> -                                  &sbrec_mac_binding_col_ip);
> -    struct ovsdb_idl_index *sbrec_ip_multicast
> -        = ip_mcast_index_create(ovnsb_idl_loop.idl);
> -    struct ovsdb_idl_index *sbrec_igmp_group
> -        = igmp_group_index_create(ovnsb_idl_loop.idl);
> -
> -    ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);
> -    ovsdb_idl_omit_alert(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
> -
> -    /* Omit the external_ids column of all the tables except for -
> -     *  - DNS. pinctrl.c uses the external_ids column of DNS,
> -     *    which it shouldn't. This should be removed.
> -     *
> -     *  - Chassis - chassis.c copies the chassis configuration from
> -     *              local open_vswitch table to the external_ids of
> -     *              chassis.
> -     *
> -     *  - Datapath_binding - lflow.c is using this to check if the
> datapath
> -     *                       is switch or not. This should be removed.
> -     * */
> -
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_sb_global_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl,
> &sbrec_logical_flow_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl,
> &sbrec_port_binding_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl,
> &sbrec_connection_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_ssl_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl,
> -                   &sbrec_gateway_chassis_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl,
> &sbrec_ha_chassis_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl,
> -                   &sbrec_ha_chassis_group_col_external_ids);
> -
> -    update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL);
> -
> -    stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
> -
> -    /* Define inc-proc-engine nodes. */
> -    struct ed_type_runtime_data ed_runtime_data;
> -    struct ed_type_mff_ovn_geneve ed_mff_ovn_geneve;
> -    struct ed_type_ofctrl_is_connected ed_ofctrl_is_connected;
> -    struct ed_type_flow_output ed_flow_output;
> -    struct ed_type_addr_sets ed_addr_sets;
> -    struct ed_type_port_groups ed_port_groups;
> -
> -    ENGINE_NODE(runtime_data, "runtime_data");
> -    ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
> -    ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
> -    ENGINE_NODE(flow_output, "flow_output");
> -    ENGINE_NODE(addr_sets, "addr_sets");
> -    ENGINE_NODE(port_groups, "port_groups");
> -
> -#define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR);
> -    SB_NODES
> -#undef SB_NODE
> -
> -#define OVS_NODE(NAME, NAME_STR) ENGINE_NODE_OVS(NAME, NAME_STR);
> -    OVS_NODES
> -#undef OVS_NODE
> -
> -    engine_ovsdb_node_add_index(&en_sb_chassis, "name",
> sbrec_chassis_by_name);
> -    engine_ovsdb_node_add_index(&en_sb_multicast_group, "name_datapath",
> -                                sbrec_multicast_group_by_name_datapath);
> -    engine_ovsdb_node_add_index(&en_sb_port_binding, "name",
> -                                sbrec_port_binding_by_name);
> -    engine_ovsdb_node_add_index(&en_sb_port_binding, "key",
> -                                sbrec_port_binding_by_key);
> -    engine_ovsdb_node_add_index(&en_sb_port_binding, "datapath",
> -                                sbrec_port_binding_by_datapath);
> -    engine_ovsdb_node_add_index(&en_sb_datapath_binding, "key",
> -                                sbrec_datapath_binding_by_key);
> -
> -    /* Add dependencies between inc-proc-engine nodes. */
> -
> -    engine_add_input(&en_addr_sets, &en_sb_address_set,
> -                     addr_sets_sb_address_set_handler);
> -    engine_add_input(&en_port_groups, &en_sb_port_group,
> -                     port_groups_sb_port_group_handler);
> -
> -    engine_add_input(&en_flow_output, &en_addr_sets,
> -                     flow_output_addr_sets_handler);
> -    engine_add_input(&en_flow_output, &en_port_groups,
> -                     flow_output_port_groups_handler);
> -    engine_add_input(&en_flow_output, &en_runtime_data, NULL);
> -    engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
> -
> -    engine_add_input(&en_flow_output, &en_ovs_open_vswitch, NULL);
> -    engine_add_input(&en_flow_output, &en_ovs_bridge, NULL);
> -
> -    engine_add_input(&en_flow_output, &en_sb_chassis, NULL);
> -    engine_add_input(&en_flow_output, &en_sb_encap, NULL);
> -    engine_add_input(&en_flow_output, &en_sb_multicast_group,
> -                     flow_output_sb_multicast_group_handler);
> -    engine_add_input(&en_flow_output, &en_sb_port_binding,
> -                     flow_output_sb_port_binding_handler);
> -    engine_add_input(&en_flow_output, &en_sb_mac_binding,
> -                     flow_output_sb_mac_binding_handler);
> -    engine_add_input(&en_flow_output, &en_sb_logical_flow,
> -                     flow_output_sb_logical_flow_handler);
> -    engine_add_input(&en_flow_output, &en_sb_dhcp_options, NULL);
> -    engine_add_input(&en_flow_output, &en_sb_dhcpv6_options, NULL);
> -    engine_add_input(&en_flow_output, &en_sb_dns, NULL);
> -
> -    engine_add_input(&en_runtime_data, &en_ofctrl_is_connected, NULL);
> -
> -    engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL);
> -    engine_add_input(&en_runtime_data, &en_ovs_bridge, NULL);
> -    engine_add_input(&en_runtime_data, &en_ovs_port, NULL);
> -    engine_add_input(&en_runtime_data, &en_ovs_qos, NULL);
> -
> -    engine_add_input(&en_runtime_data, &en_sb_chassis, NULL);
> -    engine_add_input(&en_runtime_data, &en_sb_datapath_binding, NULL);
> -    engine_add_input(&en_runtime_data, &en_sb_port_binding,
> -                     runtime_data_sb_port_binding_handler);
> -
> -    engine_init(&en_flow_output);
> -
> -    ofctrl_init(&ed_flow_output.group_table,
> -                &ed_flow_output.meter_table,
> -                get_ofctrl_probe_interval(ovs_idl_loop.idl));
> -
> -    unixctl_command_register("group-table-list", "", 0, 0,
> -                             group_table_list,
> &ed_flow_output.group_table);
> -
> -    unixctl_command_register("meter-table-list", "", 0, 0,
> -                             meter_table_list,
> &ed_flow_output.meter_table);
> -
> -    unixctl_command_register("ct-zone-list", "", 0, 0,
> -                             ct_zone_list, &ed_runtime_data.ct_zones);
> -
> -    struct pending_pkt pending_pkt = { .conn = NULL };
> -    unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
> -                             &pending_pkt);
> -
> -    uint64_t engine_run_id = 0;
> -    uint64_t old_engine_run_id = 0;
> -
> -    unsigned int ovs_cond_seqno = UINT_MAX;
> -    unsigned int ovnsb_cond_seqno = UINT_MAX;
> -
> -    /* Main loop. */
> -    exiting = false;
> -    restart = false;
> -    while (!exiting) {
> -        update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl);
> -        update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
> -
> ofctrl_set_probe_interval(get_ofctrl_probe_interval(ovs_idl_loop.idl));
> -        old_engine_run_id = engine_run_id;
> -
> -        struct ovsdb_idl_txn *ovs_idl_txn =
> ovsdb_idl_loop_run(&ovs_idl_loop);
> -        unsigned int new_ovs_cond_seqno
> -            = ovsdb_idl_get_condition_seqno(ovs_idl_loop.idl);
> -        if (new_ovs_cond_seqno != ovs_cond_seqno) {
> -            if (!new_ovs_cond_seqno) {
> -                VLOG_INFO("OVS IDL reconnected, force recompute.");
> -                engine_set_force_recompute(true);
> -            }
> -            ovs_cond_seqno = new_ovs_cond_seqno;
> -        }
> -
> -        struct ovsdb_idl_txn *ovnsb_idl_txn
> -            = ovsdb_idl_loop_run(&ovnsb_idl_loop);
> -        unsigned int new_ovnsb_cond_seqno
> -            = ovsdb_idl_get_condition_seqno(ovnsb_idl_loop.idl);
> -        if (new_ovnsb_cond_seqno != ovnsb_cond_seqno) {
> -            if (!new_ovnsb_cond_seqno) {
> -                VLOG_INFO("OVNSB IDL reconnected, force recompute.");
> -                engine_set_force_recompute(true);
> -            }
> -            ovnsb_cond_seqno = new_ovnsb_cond_seqno;
> -        }
> -
> -        struct engine_context eng_ctx = {
> -            .ovs_idl_txn = ovs_idl_txn,
> -            .ovnsb_idl_txn = ovnsb_idl_txn
> -        };
> -
> -        engine_set_context(&eng_ctx);
> -
> -        if (ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl)) {
> -            /* Contains the transport zones that this Chassis belongs to
> */
> -            struct sset transport_zones =
> SSET_INITIALIZER(&transport_zones);
> -            sset_from_delimited_string(&transport_zones,
> -                get_transport_zones(ovsrec_open_vswitch_table_get(
> -                                    ovs_idl_loop.idl)), ",");
> -
> -            const struct ovsrec_bridge_table *bridge_table =
> -                ovsrec_bridge_table_get(ovs_idl_loop.idl);
> -            const struct ovsrec_open_vswitch_table *ovs_table =
> -                ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
> -            const struct sbrec_chassis_table *chassis_table =
> -                sbrec_chassis_table_get(ovnsb_idl_loop.idl);
> -            const struct ovsrec_bridge *br_int =
> -                process_br_int(ovs_idl_txn, bridge_table, ovs_table);
> -            const char *chassis_id = get_ovs_chassis_id(ovs_table);
> -            const struct sbrec_chassis *chassis = NULL;
> -            if (chassis_id) {
> -                chassis = chassis_run(ovnsb_idl_txn,
> sbrec_chassis_by_name,
> -                                      ovs_table, chassis_table,
> chassis_id,
> -                                      br_int, &transport_zones);
> -            }
> -
> -            if (br_int) {
> -                ofctrl_run(br_int, &ed_runtime_data.pending_ct_zones);
> -
> -                if (chassis) {
> -                    patch_run(ovs_idl_txn,
> -                              ovsrec_bridge_table_get(ovs_idl_loop.idl),
> -
> ovsrec_open_vswitch_table_get(ovs_idl_loop.idl),
> -                              ovsrec_port_table_get(ovs_idl_loop.idl),
> -
> sbrec_port_binding_table_get(ovnsb_idl_loop.idl),
> -                              br_int, chassis);
> -                    encaps_run(ovs_idl_txn,
> -                               bridge_table, br_int,
> -
>  sbrec_chassis_table_get(ovnsb_idl_loop.idl),
> -                               chassis_id,
> -                               sbrec_sb_global_first(ovnsb_idl_loop.idl),
> -                               &transport_zones);
> -
> -                    stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME,
> -                                    time_msec());
> -                    if (ovnsb_idl_txn) {
> -                        engine_run(&en_flow_output, ++engine_run_id);
> -                    }
> -                    stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
> -                                   time_msec());
> -                    if (ovs_idl_txn) {
> -                        commit_ct_zones(br_int,
> -
> &ed_runtime_data.pending_ct_zones);
> -
> bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl),
> -                                br_int, chassis,
> -                                sbrec_ha_chassis_group_table_get(
> -                                    ovnsb_idl_loop.idl),
> -
> sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
> -                    }
> -                    ofctrl_put(&ed_flow_output.flow_table,
> -                               &ed_runtime_data.pending_ct_zones,
> -                               sbrec_meter_table_get(ovnsb_idl_loop.idl),
> -                               get_nb_cfg(sbrec_sb_global_table_get(
> -                                              ovnsb_idl_loop.idl)),
> -                               en_flow_output.changed);
> -                    pinctrl_run(ovnsb_idl_txn,
> -                                sbrec_datapath_binding_by_key,
> -                                sbrec_port_binding_by_datapath,
> -                                sbrec_port_binding_by_key,
> -                                sbrec_port_binding_by_name,
> -                                sbrec_mac_binding_by_lport_ip,
> -                                sbrec_igmp_group,
> -                                sbrec_ip_multicast,
> -                                sbrec_dns_table_get(ovnsb_idl_loop.idl),
> -                                sbrec_controller_event_table_get(
> -                                    ovnsb_idl_loop.idl),
> -                                br_int, chassis,
> -                                &ed_runtime_data.local_datapaths,
> -                                &ed_runtime_data.active_tunnels);
> -
> -                    if (en_runtime_data.changed) {
> -                        update_sb_monitors(ovnsb_idl_loop.idl, chassis,
> -                                           &ed_runtime_data.local_lports,
> -
>  &ed_runtime_data.local_datapaths);
> -                    }
> -                }
> -
> -            }
> -            if (old_engine_run_id == engine_run_id) {
> -                if (engine_need_run(&en_flow_output)) {
> -                    VLOG_DBG("engine did not run, force recompute next
> time: "
> -                             "br_int %p, chassis %p", br_int, chassis);
> -                    engine_set_force_recompute(true);
> -                    poll_immediate_wake();
> -                } else {
> -                    VLOG_DBG("engine did not run, and it was not needed"
> -                             " either: br_int %p, chassis %p",
> -                             br_int, chassis);
> -                }
> -            } else {
> -                engine_set_force_recompute(false);
> -            }
> -
> -            if (ovnsb_idl_txn && chassis) {
> -                int64_t cur_cfg = ofctrl_get_cur_cfg();
> -                if (cur_cfg && cur_cfg != chassis->nb_cfg) {
> -                    sbrec_chassis_set_nb_cfg(chassis, cur_cfg);
> -                }
> -            }
> -
> -
> -            if (pending_pkt.conn) {
> -                if (br_int && chassis) {
> -                    char *error = ofctrl_inject_pkt(br_int,
> pending_pkt.flow_s,
> -                        &ed_addr_sets.addr_sets,
> &ed_port_groups.port_groups);
> -                    if (error) {
> -                        unixctl_command_reply_error(pending_pkt.conn,
> error);
> -                        free(error);
> -                    } else {
> -                        VLOG_DBG("Pending_pkt conn but br_int %p or
> chassis "
> -                                 "%p not ready. run-id: %"PRIu64, br_int,
> -                                 chassis, engine_run_id);
> -                        unixctl_command_reply_error(pending_pkt.conn,
> -                            "ovn-controller not ready.");
> -                    }
> -                }
> -                pending_pkt.conn = NULL;
> -                free(pending_pkt.flow_s);
> -            }
> -
> -            sset_destroy(&transport_zones);
> -
> -            if (br_int) {
> -                ofctrl_wait();
> -                pinctrl_wait(ovnsb_idl_txn);
> -            }
> -        }
> -
> -        unixctl_server_run(unixctl);
> -
> -        unixctl_server_wait(unixctl);
> -        if (exiting || pending_pkt.conn) {
> -            poll_immediate_wake();
> -        }
> -
> -        if (!ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop)) {
> -            VLOG_INFO("OVNSB commit failed, force recompute next time.");
> -            engine_set_force_recompute(true);
> -        }
> -
> -        if (ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop) == 1) {
> -            struct shash_node *iter, *iter_next;
> -            SHASH_FOR_EACH_SAFE (iter, iter_next,
> -                                 &ed_runtime_data.pending_ct_zones) {
> -                struct ct_zone_pending_entry *ctzpe = iter->data;
> -                if (ctzpe->state == CT_ZONE_DB_SENT) {
> -                    shash_delete(&ed_runtime_data.pending_ct_zones, iter);
> -                    free(ctzpe);
> -                }
> -            }
> -        }
> -
> -        ovsdb_idl_track_clear(ovnsb_idl_loop.idl);
> -        ovsdb_idl_track_clear(ovs_idl_loop.idl);
> -        poll_block();
> -        if (should_service_stop()) {
> -            exiting = true;
> -        }
> -    }
> -
> -    engine_set_context(NULL);
> -    engine_cleanup(&en_flow_output);
> -
> -    /* It's time to exit.  Clean up the databases if we are not
> restarting */
> -    if (!restart) {
> -        bool done = !ovsdb_idl_has_ever_connected(ovnsb_idl_loop.idl);
> -        while (!done) {
> -            update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl);
> -            update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
> -
> -            struct ovsdb_idl_txn *ovs_idl_txn
> -                = ovsdb_idl_loop_run(&ovs_idl_loop);
> -            struct ovsdb_idl_txn *ovnsb_idl_txn
> -                = ovsdb_idl_loop_run(&ovnsb_idl_loop);
> -
> -            const struct ovsrec_bridge_table *bridge_table
> -                = ovsrec_bridge_table_get(ovs_idl_loop.idl);
> -            const struct ovsrec_open_vswitch_table *ovs_table
> -                = ovsrec_open_vswitch_table_get(ovs_idl_loop.idl);
> -
> -            const struct sbrec_port_binding_table *port_binding_table
> -                = sbrec_port_binding_table_get(ovnsb_idl_loop.idl);
> -
> -            const struct ovsrec_bridge *br_int = get_br_int(bridge_table,
> -                                                            ovs_table);
> -            const char *chassis_id = chassis_get_id();
> -            const struct sbrec_chassis *chassis
> -                = (chassis_id
> -                   ? chassis_lookup_by_name(sbrec_chassis_by_name,
> chassis_id)
> -                   : NULL);
> -
> -            /* Run all of the cleanup functions, even if one of them
> returns
> -             * false. We're done if all of them return true. */
> -            done = binding_cleanup(ovnsb_idl_txn, port_binding_table,
> chassis);
> -            done = chassis_cleanup(ovnsb_idl_txn, chassis) && done;
> -            done = encaps_cleanup(ovs_idl_txn, br_int) && done;
> -            done = igmp_group_cleanup(ovnsb_idl_txn, sbrec_igmp_group) &&
> done;
> -            if (done) {
> -                poll_immediate_wake();
> -            }
> -
> -            ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
> -            ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
> -            poll_block();
> -        }
> -    }
> -
> -    unixctl_server_destroy(unixctl);
> -    lflow_destroy();
> -    ofctrl_destroy();
> -    pinctrl_destroy();
> -
> -    ovsdb_idl_loop_destroy(&ovs_idl_loop);
> -    ovsdb_idl_loop_destroy(&ovnsb_idl_loop);
> -
> -    free(ovs_remote);
> -    service_stop();
> -
> -    exit(retval);
> -}
> -
> -static char *
> -parse_options(int argc, char *argv[])
> -{
> -    enum {
> -        OPT_PEER_CA_CERT = UCHAR_MAX + 1,
> -        OPT_BOOTSTRAP_CA_CERT,
> -        VLOG_OPTION_ENUMS,
> -        DAEMON_OPTION_ENUMS,
> -        SSL_OPTION_ENUMS,
> -    };
> -
> -    static struct option long_options[] = {
> -        {"help", no_argument, NULL, 'h'},
> -        {"version", no_argument, NULL, 'V'},
> -        VLOG_LONG_OPTIONS,
> -        DAEMON_LONG_OPTIONS,
> -        STREAM_SSL_LONG_OPTIONS,
> -        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
> -        {"bootstrap-ca-cert", required_argument, NULL,
> OPT_BOOTSTRAP_CA_CERT},
> -        {NULL, 0, NULL, 0}
> -    };
> -    char *short_options =
> ovs_cmdl_long_options_to_short_options(long_options);
> -
> -    for (;;) {
> -        int c;
> -
> -        c = getopt_long(argc, argv, short_options, long_options, NULL);
> -        if (c == -1) {
> -            break;
> -        }
> -
> -        switch (c) {
> -        case 'h':
> -            usage();
> -
> -        case 'V':
> -            ovs_print_version(OFP13_VERSION, OFP13_VERSION);
> -            exit(EXIT_SUCCESS);
> -
> -        VLOG_OPTION_HANDLERS
> -        DAEMON_OPTION_HANDLERS
> -        STREAM_SSL_OPTION_HANDLERS
> -
> -        case OPT_PEER_CA_CERT:
> -            stream_ssl_set_peer_ca_cert_file(optarg);
> -            break;
> -
> -        case OPT_BOOTSTRAP_CA_CERT:
> -            stream_ssl_set_ca_cert_file(optarg, true);
> -            break;
> -
> -        case '?':
> -            exit(EXIT_FAILURE);
> -
> -        default:
> -            abort();
> -        }
> -    }
> -    free(short_options);
> -
> -    argc -= optind;
> -    argv += optind;
> -
> -    char *ovs_remote;
> -    if (argc == 0) {
> -        ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
> -    } else if (argc == 1) {
> -        ovs_remote = xstrdup(argv[0]);
> -    } else {
> -        VLOG_FATAL("exactly zero or one non-option argument required; "
> -                   "use --help for usage");
> -    }
> -    return ovs_remote;
> -}
> -
> -static void
> -usage(void)
> -{
> -    printf("%s: OVN controller\n"
> -           "usage %s [OPTIONS] [OVS-DATABASE]\n"
> -           "where OVS-DATABASE is a socket on which the OVS OVSDB server
> is listening.\n",
> -               program_name, program_name);
> -    stream_usage("OVS-DATABASE", true, false, true);
> -    daemon_usage();
> -    vlog_usage();
> -    printf("\nOther options:\n"
> -           "  -h, --help              display this help message\n"
> -           "  -V, --version           display version information\n");
> -    exit(EXIT_SUCCESS);
> -}
> -
> -static void
> -ovn_controller_exit(struct unixctl_conn *conn, int argc,
> -             const char *argv[], void *exit_args_)
> -{
> -    struct ovn_controller_exit_args *exit_args = exit_args_;
> -    *exit_args->exiting = true;
> -    *exit_args->restart = argc == 2 && !strcmp(argv[1], "--restart");
> -    unixctl_command_reply(conn, NULL);
> -}
> -
> -static void
> -ct_zone_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -             const char *argv[] OVS_UNUSED, void *ct_zones_)
> -{
> -    struct simap *ct_zones = ct_zones_;
> -    struct ds ds = DS_EMPTY_INITIALIZER;
> -    struct simap_node *zone;
> -
> -    SIMAP_FOR_EACH(zone, ct_zones) {
> -        ds_put_format(&ds, "%s %d\n", zone->name, zone->data);
> -    }
> -
> -    unixctl_command_reply(conn, ds_cstr(&ds));
> -    ds_destroy(&ds);
> -}
> -
> -static void
> -meter_table_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -                 const char *argv[] OVS_UNUSED, void *meter_table_)
> -{
> -    struct ovn_extend_table *meter_table = meter_table_;
> -    struct ds ds = DS_EMPTY_INITIALIZER;
> -    struct simap meters = SIMAP_INITIALIZER(&meters);
> -
> -    struct ovn_extend_table_info *m_installed, *next_meter;
> -    EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_meter,
> meter_table) {
> -        simap_put(&meters, m_installed->name, m_installed->table_id);
> -    }
> -
> -    const struct simap_node **nodes = simap_sort(&meters);
> -    size_t n_nodes = simap_count(&meters);
> -    for (size_t i = 0; i < n_nodes; i++) {
> -        const struct simap_node *node = nodes[i];
> -        ds_put_format(&ds, "%s: %d\n", node->name, node->data);
> -    }
> -
> -    free(nodes);
> -    simap_destroy(&meters);
> -
> -    unixctl_command_reply(conn, ds_cstr(&ds));
> -    ds_destroy(&ds);
> -}
> -
> -static void
> -group_table_list(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -                 const char *argv[] OVS_UNUSED, void *group_table_)
> -{
> -    struct ovn_extend_table *group_table = group_table_;
> -    struct ds ds = DS_EMPTY_INITIALIZER;
> -    struct simap groups = SIMAP_INITIALIZER(&groups);
> -
> -    struct ovn_extend_table_info *m_installed, *next_group;
> -    EXTEND_TABLE_FOR_EACH_INSTALLED (m_installed, next_group,
> group_table) {
> -        simap_put(&groups, m_installed->name, m_installed->table_id);
> -    }
> -
> -    const struct simap_node **nodes = simap_sort(&groups);
> -    size_t n_nodes = simap_count(&groups);
> -    for (size_t i = 0; i < n_nodes; i++) {
> -        const struct simap_node *node = nodes[i];
> -        ds_put_format(&ds, "%s: %d\n", node->name, node->data);
> -    }
> -
> -    free(nodes);
> -    simap_destroy(&groups);
> -
> -    unixctl_command_reply(conn, ds_cstr(&ds));
> -    ds_destroy(&ds);
> -}
> -
> -static void
> -inject_pkt(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -           const char *argv[], void *pending_pkt_)
> -{
> -    struct pending_pkt *pending_pkt = pending_pkt_;
> -
> -    if (pending_pkt->conn) {
> -        unixctl_command_reply_error(conn, "already pending packet
> injection");
> -        return;
> -    }
> -    pending_pkt->conn = conn;
> -    pending_pkt->flow_s = xstrdup(argv[1]);
> -}
> -
> -static void
> -ovn_controller_conn_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
> -                         const char *argv[] OVS_UNUSED, void *idl_)
> -{
> -    const char *result = "not connected";
> -    const struct ovsdb_idl *idl = idl_;
> -
> -    if (ovsdb_idl_is_connected(idl)) {
> -       result = "connected";
> -    }
> -    unixctl_command_reply(conn, result);
> -}
> diff --git a/ovn/controller/ovn-controller.h
> b/ovn/controller/ovn-controller.h
> deleted file mode 100644
> index 078c9eabe..000000000
> --- a/ovn/controller/ovn-controller.h
> +++ /dev/null
> @@ -1,85 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -
> -#ifndef OVN_CONTROLLER_H
> -#define OVN_CONTROLLER_H 1
> -
> -#include "simap.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -
> -struct ovsrec_bridge_table;
> -
> -/* Linux supports a maximum of 64K zones, which seems like a fine
> default. */
> -#define MAX_CT_ZONES 65535
> -
> -/* States to move through when a new conntrack zone has been allocated. */
> -enum ct_zone_pending_state {
> -    CT_ZONE_OF_QUEUED,    /* Waiting to send conntrack flush command. */
> -    CT_ZONE_OF_SENT,      /* Sent and waiting for confirmation on flush.
> */
> -    CT_ZONE_DB_QUEUED,    /* Waiting for DB transaction to open. */
> -    CT_ZONE_DB_SENT,      /* Sent and waiting for confirmation from DB. */
> -};
> -
> -struct ct_zone_pending_entry {
> -    int zone;
> -    bool add;             /* Is the entry being added? */
> -    ovs_be32 of_xid;      /* Transaction id for barrier. */
> -    enum ct_zone_pending_state state;
> -};
> -
> -/* A logical datapath that has some relevance to this hypervisor.  A
> logical
> - * datapath D is relevant to hypervisor H if:
> - *
> - *     - Some VIF or l2gateway or l3gateway port in D is located on H.
> - *
> - *     - D is reachable over a series of hops across patch ports,
> starting from
> - *       a datapath relevant to H.
> - *
> - * The 'hmap_node''s hash value is 'datapath->tunnel_key'. */
> -struct local_datapath {
> -    struct hmap_node hmap_node;
> -    const struct sbrec_datapath_binding *datapath;
> -
> -    /* The localnet port in this datapath, if any (at most one is
> allowed). */
> -    const struct sbrec_port_binding *localnet_port;
> -
> -    /* True if this datapath contains an l3gateway port located on this
> -     * hypervisor. */
> -    bool has_local_l3gateway;
> -
> -    const struct sbrec_port_binding **peer_ports;
> -    size_t n_peer_ports;
> -};
> -
> -struct local_datapath *get_local_datapath(const struct hmap *,
> -                                          uint32_t tunnel_key);
> -
> -const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *,
> -                                       const char *br_name);
> -
> -struct sbrec_encap *preferred_encap(const struct sbrec_chassis *);
> -
> -/* Must be a bit-field ordered from most-preferred (higher number) to
> - * least-preferred (lower number). */
> -enum chassis_tunnel_type {
> -    GENEVE = 1 << 2,
> -    STT    = 1 << 1,
> -    VXLAN  = 1 << 0
> -};
> -
> -uint32_t get_tunnel_type(const char *name);
> -
> -#endif /* ovn/ovn-controller.h */
> diff --git a/ovn/controller/patch.c b/ovn/controller/patch.c
> deleted file mode 100644
> index a6770c6d5..000000000
> --- a/ovn/controller/patch.c
> +++ /dev/null
> @@ -1,273 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "patch.h"
> -
> -#include "hash.h"
> -#include "lflow.h"
> -#include "lib/vswitch-idl.h"
> -#include "lport.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/vlog.h"
> -#include "ovn-controller.h"
> -
> -VLOG_DEFINE_THIS_MODULE(patch);
> -
> -static char *
> -patch_port_name(const char *src, const char *dst)
> -{
> -    return xasprintf("patch-%s-to-%s", src, dst);
> -}
> -
> -/* Return true if 'port' is a patch port with the specified 'peer'. */
> -static bool
> -match_patch_port(const struct ovsrec_port *port, const char *peer)
> -{
> -    for (size_t i = 0; i < port->n_interfaces; i++) {
> -        struct ovsrec_interface *iface = port->interfaces[i];
> -        if (strcmp(iface->type, "patch")) {
> -            continue;
> -        }
> -        const char *iface_peer = smap_get(&iface->options, "peer");
> -        if (iface_peer && !strcmp(iface_peer, peer)) {
> -            return true;
> -        }
> -    }
> -    return false;
> -}
> -
> -/* Creates a patch port in bridge 'src' named 'src_name', whose peer is
> - * 'dst_name' in bridge 'dst'.  Initializes the patch port's
> external-ids:'key'
> - * to 'key'.
> - *
> - * If such a patch port already exists, removes it from 'existing_ports'.
> */
> -static void
> -create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn,
> -                  const char *key, const char *value,
> -                  const struct ovsrec_bridge *src, const char *src_name,
> -                  const struct ovsrec_bridge *dst, const char *dst_name,
> -                  struct shash *existing_ports)
> -{
> -    for (size_t i = 0; i < src->n_ports; i++) {
> -        if (match_patch_port(src->ports[i], dst_name)) {
> -            /* Patch port already exists on 'src'. */
> -            shash_find_and_delete(existing_ports, src->ports[i]->name);
> -            return;
> -        }
> -    }
> -
> -    ovsdb_idl_txn_add_comment(ovs_idl_txn,
> -            "ovn-controller: creating patch port '%s' from '%s' to '%s'",
> -            src_name, src->name, dst->name);
> -
> -    struct ovsrec_interface *iface;
> -    iface = ovsrec_interface_insert(ovs_idl_txn);
> -    ovsrec_interface_set_name(iface, src_name);
> -    ovsrec_interface_set_type(iface, "patch");
> -    const struct smap options = SMAP_CONST1(&options, "peer", dst_name);
> -    ovsrec_interface_set_options(iface, &options);
> -
> -    struct ovsrec_port *port;
> -    port = ovsrec_port_insert(ovs_idl_txn);
> -    ovsrec_port_set_name(port, src_name);
> -    ovsrec_port_set_interfaces(port, &iface, 1);
> -    const struct smap ids = SMAP_CONST1(&ids, key, value);
> -    ovsrec_port_set_external_ids(port, &ids);
> -
> -    struct ovsrec_port **ports;
> -    ports = xmalloc(sizeof *ports * (src->n_ports + 1));
> -    memcpy(ports, src->ports, sizeof *ports * src->n_ports);
> -    ports[src->n_ports] = port;
> -    ovsrec_bridge_verify_ports(src);
> -    ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
> -
> -    free(ports);
> -}
> -
> -static void
> -remove_port(const struct ovsrec_bridge_table *bridge_table,
> -            const struct ovsrec_port *port)
> -{
> -    const struct ovsrec_bridge *bridge;
> -
> -    /* We know the port we want to delete, but we have to find the bridge
> its
> -     * on to do so.  Note this only runs on a config change that should be
> -     * pretty rare. */
> -    OVSREC_BRIDGE_TABLE_FOR_EACH (bridge, bridge_table) {
> -        size_t i;
> -        for (i = 0; i < bridge->n_ports; i++) {
> -            if (bridge->ports[i] != port) {
> -                continue;
> -            }
> -            struct ovsrec_port **new_ports;
> -            new_ports = xmemdup(bridge->ports,
> -                    sizeof *new_ports * (bridge->n_ports - 1));
> -            if (i != bridge->n_ports - 1) {
> -                /* Removed port was not last */
> -                new_ports[i] = bridge->ports[bridge->n_ports - 1];
> -            }
> -            ovsrec_bridge_verify_ports(bridge);
> -            ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports -
> 1);
> -            free(new_ports);
> -            ovsrec_port_delete(port);
> -            return;
> -        }
> -    }
> -}
> -
> -/* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch
> ports for
> - * the local bridge mappings.  Removes any patch ports for bridge
> mappings that
> - * already existed from 'existing_ports'. */
> -static void
> -add_bridge_mappings(struct ovsdb_idl_txn *ovs_idl_txn,
> -                    const struct ovsrec_bridge_table *bridge_table,
> -                    const struct ovsrec_open_vswitch_table *ovs_table,
> -                    const struct sbrec_port_binding_table
> *port_binding_table,
> -                    const struct ovsrec_bridge *br_int,
> -                    struct shash *existing_ports,
> -                    const struct sbrec_chassis *chassis)
> -{
> -    /* Get ovn-bridge-mappings. */
> -    const char *mappings_cfg = "";
> -    const struct ovsrec_open_vswitch *cfg;
> -    cfg = ovsrec_open_vswitch_table_first(ovs_table);
> -    if (cfg) {
> -        mappings_cfg = smap_get(&cfg->external_ids,
> "ovn-bridge-mappings");
> -        if (!mappings_cfg || !mappings_cfg[0]) {
> -            return;
> -        }
> -    }
> -
> -    /* Parse bridge mappings. */
> -    struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
> -    char *cur, *next, *start;
> -    next = start = xstrdup(mappings_cfg);
> -    while ((cur = strsep(&next, ",")) && *cur) {
> -        char *network, *bridge = cur;
> -        const struct ovsrec_bridge *ovs_bridge;
> -
> -        network = strsep(&bridge, ":");
> -        if (!bridge || !*network || !*bridge) {
> -            VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
> -                    mappings_cfg);
> -            break;
> -        }
> -
> -        ovs_bridge = get_bridge(bridge_table, bridge);
> -        if (!ovs_bridge) {
> -            VLOG_WARN("Bridge '%s' not found for network '%s'",
> -                    bridge, network);
> -            continue;
> -        }
> -
> -        shash_add(&bridge_mappings, network, ovs_bridge);
> -    }
> -    free(start);
> -
> -    const struct sbrec_port_binding *binding;
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
> -        const char *patch_port_id;
> -        if (!strcmp(binding->type, "localnet")) {
> -            patch_port_id = "ovn-localnet-port";
> -        } else if (!strcmp(binding->type, "l2gateway")) {
> -            if (!binding->chassis
> -                || strcmp(chassis->name, binding->chassis->name)) {
> -                /* This L2 gateway port is not bound to this chassis,
> -                 * so we should not create any patch ports for it. */
> -                continue;
> -            }
> -            patch_port_id = "ovn-l2gateway-port";
> -        } else {
> -            /* not a localnet or L2 gateway port. */
> -            continue;
> -        }
> -
> -        const char *network = smap_get(&binding->options, "network_name");
> -        if (!network) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_ERR_RL(&rl, "%s port '%s' has no network name.",
> -                         binding->type, binding->logical_port);
> -            continue;
> -        }
> -        struct ovsrec_bridge *br_ln = shash_find_data(&bridge_mappings,
> network);
> -        if (!br_ln) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_ERR_RL(&rl, "bridge not found for %s port '%s' "
> -                    "with network name '%s'",
> -                    binding->type, binding->logical_port, network);
> -            continue;
> -        }
> -
> -        char *name1 = patch_port_name(br_int->name,
> binding->logical_port);
> -        char *name2 = patch_port_name(binding->logical_port,
> br_int->name);
> -        create_patch_port(ovs_idl_txn, patch_port_id,
> binding->logical_port,
> -                          br_int, name1, br_ln, name2, existing_ports);
> -        create_patch_port(ovs_idl_txn, patch_port_id,
> binding->logical_port,
> -                          br_ln, name2, br_int, name1, existing_ports);
> -        free(name1);
> -        free(name2);
> -    }
> -
> -    shash_destroy(&bridge_mappings);
> -}
> -
> -void
> -patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -          const struct ovsrec_bridge_table *bridge_table,
> -          const struct ovsrec_open_vswitch_table *ovs_table,
> -          const struct ovsrec_port_table *port_table,
> -          const struct sbrec_port_binding_table *port_binding_table,
> -          const struct ovsrec_bridge *br_int,
> -          const struct sbrec_chassis *chassis)
> -{
> -    if (!ovs_idl_txn) {
> -        return;
> -    }
> -
> -    /* Figure out what patch ports already exist.
> -     *
> -     * ovn-controller does not create or use ports of type
> "ovn-l3gateway-port"
> -     * or "ovn-logical-patch-port", but older version did.  We still
> recognize
> -     * them here, so that we delete them at the end of this function, to
> avoid
> -     * leaving useless ports on upgrade. */
> -    struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
> -    const struct ovsrec_port *port;
> -    OVSREC_PORT_TABLE_FOR_EACH (port, port_table) {
> -        if (smap_get(&port->external_ids, "ovn-localnet-port")
> -            || smap_get(&port->external_ids, "ovn-l2gateway-port")
> -            || smap_get(&port->external_ids, "ovn-l3gateway-port")
> -            || smap_get(&port->external_ids, "ovn-logical-patch-port")) {
> -            shash_add(&existing_ports, port->name, port);
> -        }
> -    }
> -
> -    /* Create in the database any patch ports that should exist.  Remove
> from
> -     * 'existing_ports' any patch ports that do exist in the database and
> -     * should be there. */
> -    add_bridge_mappings(ovs_idl_txn, bridge_table, ovs_table,
> -                        port_binding_table, br_int, &existing_ports,
> chassis);
> -
> -    /* Now 'existing_ports' only still contains patch ports that exist in
> the
> -     * database but shouldn't.  Delete them from the database. */
> -    struct shash_node *port_node, *port_next_node;
> -    SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
> -        port = port_node->data;
> -        shash_delete(&existing_ports, port_node);
> -        remove_port(bridge_table, port);
> -    }
> -    shash_destroy(&existing_ports);
> -}
> diff --git a/ovn/controller/patch.h b/ovn/controller/patch.h
> deleted file mode 100644
> index dd052cfd8..000000000
> --- a/ovn/controller/patch.h
> +++ /dev/null
> @@ -1,42 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_PATCH_H
> -#define OVN_PATCH_H 1
> -
> -/* Patch Ports
> - * ===========
> - *
> - * This module adds and removes patch ports between the integration
> bridge and
> - * physical bridges, as directed by other-config:ovn-bridge-mappings. */
> -
> -struct hmap;
> -struct ovsdb_idl_txn;
> -struct ovsrec_bridge;
> -struct ovsrec_bridge_table;
> -struct ovsrec_open_vswitch_table;
> -struct ovsrec_port_table;
> -struct sbrec_port_binding_table;
> -struct sbrec_chassis;
> -
> -void patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
> -               const struct ovsrec_bridge_table *,
> -               const struct ovsrec_open_vswitch_table *,
> -               const struct ovsrec_port_table *,
> -               const struct sbrec_port_binding_table *,
> -               const struct ovsrec_bridge *br_int,
> -               const struct sbrec_chassis *);
> -
> -#endif /* ovn/patch.h */
> diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
> deleted file mode 100644
> index 316d3738c..000000000
> --- a/ovn/controller/physical.c
> +++ /dev/null
> @@ -1,1459 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -#include "binding.h"
> -#include "byte-order.h"
> -#include "encaps.h"
> -#include "flow.h"
> -#include "ha-chassis.h"
> -#include "lflow.h"
> -#include "lport.h"
> -#include "chassis.h"
> -#include "lib/bundle.h"
> -#include "openvswitch/poll-loop.h"
> -#include "lib/uuid.h"
> -#include "ofctrl.h"
> -#include "openvswitch/list.h"
> -#include "openvswitch/hmap.h"
> -#include "openvswitch/match.h"
> -#include "openvswitch/ofp-actions.h"
> -#include "openvswitch/ofpbuf.h"
> -#include "openvswitch/vlog.h"
> -#include "openvswitch/ofp-parse.h"
> -#include "ovn-controller.h"
> -#include "ovn/lib/chassis-index.h"
> -#include "ovn/lib/ovn-sb-idl.h"
> -#include "ovn/lib/ovn-util.h"
> -#include "physical.h"
> -#include "openvswitch/shash.h"
> -#include "simap.h"
> -#include "smap.h"
> -#include "sset.h"
> -#include "util.h"
> -#include "vswitch-idl.h"
> -
> -VLOG_DEFINE_THIS_MODULE(physical);
> -
> -/* UUID to identify OF flows not associated with ovsdb rows. */
> -static struct uuid *hc_uuid = NULL;
> -
> -void
> -physical_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> -{
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
> -    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> -
> -    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_name);
> -    ovsdb_idl_track_add_column(ovs_idl, &ovsrec_interface_col_ofport);
> -    ovsdb_idl_track_add_column(ovs_idl,
> &ovsrec_interface_col_external_ids);
> -}
> -
> -static struct simap localvif_to_ofport =
> -    SIMAP_INITIALIZER(&localvif_to_ofport);
> -static struct hmap tunnels = HMAP_INITIALIZER(&tunnels);
> -
> -/* Maps from a chassis to the OpenFlow port number of the tunnel that can
> be
> - * used to reach that chassis. */
> -struct chassis_tunnel {
> -    struct hmap_node hmap_node;
> -    char *chassis_id;
> -    ofp_port_t ofport;
> -    enum chassis_tunnel_type type;
> -};
> -
> -/*
> - * This function looks up the list of tunnel ports (provided by
> - * ovn-chassis-id ports) and returns the tunnel for the given chassid-id
> and
> - * encap-ip. The ovn-chassis-id is formed using the chassis-id and
> encap-ip.
> - * The list is hashed using the chassis-id. If the encap-ip is not
> specified,
> - * it means we'll just return a tunnel for that chassis-id, i.e. we just
> check
> - * for chassis-id and if there is a match, we'll return the tunnel.
> - * If encap-ip is also provided we use both chassis-id and encap-ip to do
> - * a more specific lookup.
> - */
> -static struct chassis_tunnel *
> -chassis_tunnel_find(const char *chassis_id, char *encap_ip)
> -{
> -    /*
> -     * If the specific encap_ip is given, look for the chassisid_ip entry,
> -     * else return the 1st found entry for the chassis.
> -     */
> -    struct chassis_tunnel *tun = NULL;
> -    HMAP_FOR_EACH_WITH_HASH (tun, hmap_node, hash_string(chassis_id, 0),
> -                             &tunnels) {
> -        if (encaps_tunnel_id_match(tun->chassis_id, chassis_id,
> encap_ip)) {
> -            return tun;
> -        }
> -    }
> -    return NULL;
> -}
> -
> -static void
> -put_load(uint64_t value, enum mf_field_id dst, int ofs, int n_bits,
> -         struct ofpbuf *ofpacts)
> -{
> -    struct ofpact_set_field *sf = ofpact_put_set_field(ofpacts,
> -                                                       mf_from_id(dst),
> NULL,
> -                                                       NULL);
> -    ovs_be64 n_value = htonll(value);
> -    bitwise_copy(&n_value, 8, 0, sf->value, sf->field->n_bytes, ofs,
> n_bits);
> -    bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs,
> n_bits);
> -}
> -
> -static void
> -put_move(enum mf_field_id src, int src_ofs,
> -         enum mf_field_id dst, int dst_ofs,
> -         int n_bits,
> -         struct ofpbuf *ofpacts)
> -{
> -    struct ofpact_reg_move *move = ofpact_put_REG_MOVE(ofpacts);
> -    move->src.field = mf_from_id(src);
> -    move->src.ofs = src_ofs;
> -    move->src.n_bits = n_bits;
> -    move->dst.field = mf_from_id(dst);
> -    move->dst.ofs = dst_ofs;
> -    move->dst.n_bits = n_bits;
> -}
> -
> -static void
> -put_resubmit(uint8_t table_id, struct ofpbuf *ofpacts)
> -{
> -    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(ofpacts);
> -    resubmit->in_port = OFPP_IN_PORT;
> -    resubmit->table_id = table_id;
> -}
> -
> -/*
> - * For a port binding, get the corresponding ovn-chassis-id tunnel port
> - * from the associated encap.
> - */
> -static struct chassis_tunnel *
> -get_port_binding_tun(const struct sbrec_port_binding *binding)
> -{
> -    struct sbrec_encap *encap = binding->encap;
> -    struct sbrec_chassis *chassis = binding->chassis;
> -    struct chassis_tunnel *tun = NULL;
> -
> -    if (encap) {
> -        tun = chassis_tunnel_find(chassis->name, encap->ip);
> -    }
> -    if (!tun) {
> -        tun = chassis_tunnel_find(chassis->name, NULL);
> -    }
> -    return tun;
> -}
> -
> -static void
> -put_encapsulation(enum mf_field_id mff_ovn_geneve,
> -                  const struct chassis_tunnel *tun,
> -                  const struct sbrec_datapath_binding *datapath,
> -                  uint16_t outport, struct ofpbuf *ofpacts)
> -{
> -    if (tun->type == GENEVE) {
> -        put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts);
> -        put_load(outport, mff_ovn_geneve, 0, 32, ofpacts);
> -        put_move(MFF_LOG_INPORT, 0, mff_ovn_geneve, 16, 15, ofpacts);
> -    } else if (tun->type == STT) {
> -        put_load(datapath->tunnel_key | ((uint64_t) outport << 24),
> -                 MFF_TUN_ID, 0, 64, ofpacts);
> -        put_move(MFF_LOG_INPORT, 0, MFF_TUN_ID, 40, 15, ofpacts);
> -    } else if (tun->type == VXLAN) {
> -        put_load(datapath->tunnel_key, MFF_TUN_ID, 0, 24, ofpacts);
> -    } else {
> -        OVS_NOT_REACHED();
> -    }
> -}
> -
> -static void
> -put_stack(enum mf_field_id field, struct ofpact_stack *stack)
> -{
> -    stack->subfield.field = mf_from_id(field);
> -    stack->subfield.ofs = 0;
> -    stack->subfield.n_bits = stack->subfield.field->n_bits;
> -}
> -
> -static const struct sbrec_port_binding *
> -get_localnet_port(const struct hmap *local_datapaths, int64_t tunnel_key)
> -{
> -    const struct local_datapath *ld = get_local_datapath(local_datapaths,
> -                                                         tunnel_key);
> -    return ld ? ld->localnet_port : NULL;
> -}
> -
> -/* Datapath zone IDs for connection tracking and NAT */
> -struct zone_ids {
> -    int ct;                     /* MFF_LOG_CT_ZONE. */
> -    int dnat;                   /* MFF_LOG_DNAT_ZONE. */
> -    int snat;                   /* MFF_LOG_SNAT_ZONE. */
> -};
> -
> -static struct zone_ids
> -get_zone_ids(const struct sbrec_port_binding *binding,
> -             const struct simap *ct_zones)
> -{
> -    struct zone_ids zone_ids;
> -
> -    zone_ids.ct = simap_get(ct_zones, binding->logical_port);
> -
> -    const struct uuid *key = &binding->datapath->header_.uuid;
> -
> -    char *dnat = alloc_nat_zone_key(key, "dnat");
> -    zone_ids.dnat = simap_get(ct_zones, dnat);
> -    free(dnat);
> -
> -    char *snat = alloc_nat_zone_key(key, "snat");
> -    zone_ids.snat = simap_get(ct_zones, snat);
> -    free(snat);
> -
> -    return zone_ids;
> -}
> -
> -static void
> -put_replace_router_port_mac_flows(const struct
> -                                  sbrec_port_binding *localnet_port,
> -                                  const struct sbrec_chassis *chassis,
> -                                  const struct hmap *local_datapaths,
> -                                  struct ofpbuf *ofpacts_p,
> -                                  ofp_port_t ofport,
> -                                  struct ovn_desired_flow_table
> *flow_table)
> -{
> -    struct local_datapath *ld = get_local_datapath(local_datapaths,
> -
>  localnet_port->datapath->
> -                                                   tunnel_key);
> -    ovs_assert(ld);
> -
> -    uint32_t dp_key = localnet_port->datapath->tunnel_key;
> -    uint32_t port_key = localnet_port->tunnel_key;
> -    int tag = localnet_port->tag ? *localnet_port->tag : 0;
> -    const char *network = smap_get(&localnet_port->options,
> "network_name");
> -    struct eth_addr chassis_mac;
> -
> -    if (!network) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        VLOG_WARN_RL(&rl, "Physical network not configured for datapath:"
> -                     "%"PRId64" with localnet port",
> -                     localnet_port->datapath->tunnel_key);
> -        return;
> -    }
> -
> -    /* Get chassis mac */
> -    if (!chassis_get_mac(chassis, network, &chassis_mac)) {
> -        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -        /* Keeping the log level low for backward compatibility.
> -         * Chassis mac is a new configuration.
> -         */
> -        VLOG_DBG_RL(&rl, "Could not get chassis mac for network: %s",
> network);
> -        return;
> -    }
> -
> -    for (int i = 0; i < ld->n_peer_ports; i++) {
> -        const struct sbrec_port_binding *rport_binding =
> ld->peer_ports[i];
> -        struct eth_addr router_port_mac;
> -        struct match match;
> -        struct ofpact_mac *replace_mac;
> -
> -        /* Table 65, priority 150.
> -         * =======================
> -         *
> -         * Implements output to localnet port.
> -         * a. Flow replaces ingress router port mac with a chassis mac.
> -         * b. Flow appends the vlan id localnet port is configured with.
> -         */
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -
> -        ovs_assert(rport_binding->n_mac == 1);
> -        char *err_str = str_to_mac(rport_binding->mac[0],
> &router_port_mac);
> -        if (err_str) {
> -            /* Parsing of mac failed. */
> -            VLOG_WARN("Parsing or router port mac failed for router port:
> %s, "
> -                      "with error: %s", rport_binding->logical_port,
> err_str);
> -            free(err_str);
> -            return;
> -        }
> -
> -        /* Replace Router mac flow */
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -        match_set_dl_src(&match, router_port_mac);
> -
> -        replace_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
> -        replace_mac->mac = chassis_mac;
> -
> -        if (tag) {
> -            struct ofpact_vlan_vid *vlan_vid;
> -            vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p);
> -            vlan_vid->vlan_vid = tag;
> -            vlan_vid->push_vlan_if_needed = true;
> -        }
> -
> -        ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
> -
> -        ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 150, 0,
> -                        &match, ofpacts_p, &localnet_port->header_.uuid);
> -    }
> -}
> -
> -static void
> -put_local_common_flows(uint32_t dp_key, uint32_t port_key,
> -                       uint32_t parent_port_key,
> -                       const struct zone_ids *zone_ids,
> -                       struct ofpbuf *ofpacts_p,
> -                       struct ovn_desired_flow_table *flow_table)
> -{
> -    struct match match;
> -
> -    /* Table 33, priority 100.
> -     * =======================
> -     *
> -     * Implements output to local hypervisor.  Each flow matches a
> -     * logical output port on the local hypervisor, and resubmits to
> -     * table 34.
> -     */
> -
> -    match_init_catchall(&match);
> -    ofpbuf_clear(ofpacts_p);
> -
> -    /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */
> -    match_set_metadata(&match, htonll(dp_key));
> -    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -
> -    if (zone_ids) {
> -        if (zone_ids->ct) {
> -            put_load(zone_ids->ct, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
> -        }
> -        if (zone_ids->dnat) {
> -            put_load(zone_ids->dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
> -        }
> -        if (zone_ids->snat) {
> -            put_load(zone_ids->snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
> -        }
> -    }
> -
> -    /* Resubmit to table 34. */
> -    put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
> -    ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
> -                    &match, ofpacts_p, hc_uuid);
> -
> -    /* Table 34, Priority 100.
> -     * =======================
> -     *
> -     * Drop packets whose logical inport and outport are the same
> -     * and the MLF_ALLOW_LOOPBACK flag is not set. */
> -    match_init_catchall(&match);
> -    ofpbuf_clear(ofpacts_p);
> -    match_set_metadata(&match, htonll(dp_key));
> -    match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
> -                         0, MLF_ALLOW_LOOPBACK);
> -    match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0, port_key);
> -    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -    ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 100, 0,
> -                    &match, ofpacts_p, hc_uuid);
> -
> -    /* Table 64, Priority 100.
> -     * =======================
> -     *
> -     * If the packet is supposed to hair-pin because the
> -     *   - "loopback" flag is set
> -     *   - or if the destination is a nested container
> -     *   - or if "nested_container" flag is set and the destination is the
> -     *     parent port,
> -     * temporarily set the in_port to zero, resubmit to
> -     * table 65 for logical-to-physical translation, then restore
> -     * the port number.
> -     *
> -     * If 'parent_port_key' is set, then the 'port_key' represents a
> nested
> -     * container. */
> -
> -    bool nested_container = parent_port_key ? true: false;
> -    match_init_catchall(&match);
> -    ofpbuf_clear(ofpacts_p);
> -    match_set_metadata(&match, htonll(dp_key));
> -    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -    if (!nested_container) {
> -        match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
> -                             MLF_ALLOW_LOOPBACK, MLF_ALLOW_LOOPBACK);
> -    }
> -
> -    put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p));
> -    put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
> -    put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
> -    put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
> -    ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
> -                    &match, ofpacts_p, hc_uuid);
> -
> -    if (nested_container) {
> -        /* It's a nested container and when the packet from the nested
> -         * container is to be sent to the parent port, "nested_container"
> -         * flag will be set. We need to temporarily set the in_port to
> zero
> -         * as mentioned in the comment above.
> -         *
> -         * If a parent port has multiple child ports, then this if
> condition
> -         * will be hit multiple times, but we want to add only one flow.
> -         * ofctrl_add_flow() logs a warning message for duplicate flows.
> -         * So use the function 'ofctrl_check_and_add_flow' which doesn't
> -         * log a warning.
> -         *
> -         * Other option is to add this flow for all the ports which are
> not
> -         * nested containers. In which case we will add this flow for all
> the
> -         * ports even if they don't have any child ports which is
> -         * unnecessary.
> -         */
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0,
> parent_port_key);
> -        match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
> -                             MLF_NESTED_CONTAINER, MLF_NESTED_CONTAINER);
> -
> -        put_stack(MFF_IN_PORT, ofpact_put_STACK_PUSH(ofpacts_p));
> -        put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
> -        put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
> -        put_stack(MFF_IN_PORT, ofpact_put_STACK_POP(ofpacts_p));
> -        ofctrl_check_and_add_flow(flow_table, OFTABLE_SAVE_INPORT, 100, 0,
> -                                  &match, ofpacts_p, hc_uuid, false);
> -    }
> -}
> -
> -static void
> -load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
> -                              const struct zone_ids *zone_ids,
> -                              struct ofpbuf *ofpacts_p)
> -{
> -    if (zone_ids) {
> -        if (zone_ids->ct) {
> -            put_load(zone_ids->ct, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
> -        }
> -        if (zone_ids->dnat) {
> -            put_load(zone_ids->dnat, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
> -        }
> -        if (zone_ids->snat) {
> -            put_load(zone_ids->snat, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
> -        }
> -    }
> -
> -    /* Set MFF_LOG_DATAPATH and MFF_LOG_INPORT. */
> -    uint32_t dp_key = binding->datapath->tunnel_key;
> -    uint32_t port_key = binding->tunnel_key;
> -    put_load(dp_key, MFF_LOG_DATAPATH, 0, 64, ofpacts_p);
> -    put_load(port_key, MFF_LOG_INPORT, 0, 32, ofpacts_p);
> -}
> -
> -static void
> -consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                      enum mf_field_id mff_ovn_geneve,
> -                      const struct simap *ct_zones,
> -                      const struct sset *active_tunnels,
> -                      const struct hmap *local_datapaths,
> -                      const struct sbrec_port_binding *binding,
> -                      const struct sbrec_chassis *chassis,
> -                      struct ovn_desired_flow_table *flow_table,
> -                      struct ofpbuf *ofpacts_p)
> -{
> -    uint32_t dp_key = binding->datapath->tunnel_key;
> -    uint32_t port_key = binding->tunnel_key;
> -    if (!get_local_datapath(local_datapaths, dp_key)) {
> -        return;
> -    }
> -
> -    struct match match;
> -    if (!strcmp(binding->type, "patch")
> -        || (!strcmp(binding->type, "l3gateway")
> -            && binding->chassis == chassis)) {
> -        const char *peer_name = smap_get(&binding->options, "peer");
> -        if (!peer_name) {
> -            return;
> -        }
> -
> -        const struct sbrec_port_binding *peer = lport_lookup_by_name(
> -            sbrec_port_binding_by_name, peer_name);
> -        if (!peer || strcmp(peer->type, binding->type)) {
> -            return;
> -        }
> -        const char *peer_peer_name = smap_get(&peer->options, "peer");
> -        if (!peer_peer_name || strcmp(peer_peer_name,
> binding->logical_port)) {
> -            return;
> -        }
> -
> -        struct zone_ids binding_zones = get_zone_ids(binding, ct_zones);
> -        put_local_common_flows(dp_key, port_key, 0, &binding_zones,
> -                               ofpacts_p, flow_table);
> -
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -
> -        size_t clone_ofs = ofpacts_p->size;
> -        struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts_p);
> -        ofpact_put_CT_CLEAR(ofpacts_p);
> -        put_load(0, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
> -        put_load(0, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
> -        put_load(0, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
> -        struct zone_ids peer_zones = get_zone_ids(peer, ct_zones);
> -        load_logical_ingress_metadata(peer, &peer_zones, ofpacts_p);
> -        put_load(0, MFF_LOG_FLAGS, 0, 32, ofpacts_p);
> -        put_load(0, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
> -        for (int i = 0; i < MFF_N_LOG_REGS; i++) {
> -            put_load(0, MFF_LOG_REG0 + i, 0, 32, ofpacts_p);
> -        }
> -        put_load(0, MFF_IN_PORT, 0, 16, ofpacts_p);
> -        put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
> -        clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof *clone);
> -        ofpacts_p->header = clone;
> -        ofpact_finish_CLONE(ofpacts_p, &clone);
> -
> -        ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
> -                        &match, ofpacts_p, &binding->header_.uuid);
> -        return;
> -    }
> -
> -    struct ha_chassis_ordered *ha_ch_ordered
> -        = ha_chassis_get_ordered(binding->ha_chassis_group);
> -
> -    if (!strcmp(binding->type, "chassisredirect")
> -        && (binding->chassis == chassis
> -            || ha_chassis_group_is_active(binding->ha_chassis_group,
> -                                          active_tunnels, chassis))) {
> -
> -        /* Table 33, priority 100.
> -         * =======================
> -         *
> -         * Implements output to local hypervisor.  Each flow matches a
> -         * logical output port on the local hypervisor, and resubmits to
> -         * table 34.  For ports of type "chassisredirect", the logical
> -         * output port is changed from the "chassisredirect" port to the
> -         * underlying distributed port. */
> -
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -
> -        const char *distributed_port = smap_get_def(&binding->options,
> -                                                    "distributed-port",
> "");
> -        const struct sbrec_port_binding *distributed_binding
> -            = lport_lookup_by_name(sbrec_port_binding_by_name,
> -                                   distributed_port);
> -
> -        if (!distributed_binding) {
> -            /* Packet will be dropped. */
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -            VLOG_WARN_RL(&rl, "No port binding record for distributed "
> -                         "port %s referred by chassisredirect port %s",
> -                         distributed_port,
> -                         binding->logical_port);
> -        } else if (binding->datapath !=
> -                   distributed_binding->datapath) {
> -            /* Packet will be dropped. */
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
> -            VLOG_WARN_RL(&rl,
> -                         "chassisredirect port %s refers to "
> -                         "distributed port %s in wrong datapath",
> -                         binding->logical_port,
> -                         distributed_port);
> -        } else {
> -            put_load(distributed_binding->tunnel_key,
> -                     MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
> -
> -            struct zone_ids zone_ids = get_zone_ids(distributed_binding,
> -                                                    ct_zones);
> -            if (zone_ids.ct) {
> -                put_load(zone_ids.ct, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
> -            }
> -            if (zone_ids.dnat) {
> -                put_load(zone_ids.dnat, MFF_LOG_DNAT_ZONE, 0, 32,
> ofpacts_p);
> -            }
> -            if (zone_ids.snat) {
> -                put_load(zone_ids.snat, MFF_LOG_SNAT_ZONE, 0, 32,
> ofpacts_p);
> -            }
> -
> -            /* Resubmit to table 34. */
> -            put_resubmit(OFTABLE_CHECK_LOOPBACK, ofpacts_p);
> -        }
> -
> -        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
> -                        &match, ofpacts_p, &binding->header_.uuid);
> -
> -        goto out;
> -    }
> -
> -    /* Find the OpenFlow port for the logical port, as 'ofport'.  This is
> -     * one of:
> -     *
> -     *     - If the port is a VIF on the chassis we're managing, the
> -     *       OpenFlow port for the VIF.  'tun' will be NULL.
> -     *
> -     *       The same logic handles ports that OVN implements as Open
> vSwitch
> -     *       patch ports, that is, "localnet" and "l2gateway" ports.
> -     *
> -     *       For a container nested inside a VM and accessible via a VLAN,
> -     *       'tag' is the VLAN ID; otherwise 'tag' is 0.
> -     *
> -     *       For a localnet or l2gateway patch port, if a VLAN ID was
> -     *       configured, 'tag' is set to that VLAN ID; otherwise 'tag' is
> 0.
> -     *
> -     *     - If the port is on a remote chassis, the OpenFlow port for a
> -     *       tunnel to the VIF's remote chassis.  'tun' identifies that
> -     *       tunnel.
> -     */
> -
> -    int tag = 0;
> -    bool nested_container = false;
> -    const struct sbrec_port_binding *parent_port = NULL;
> -    ofp_port_t ofport;
> -    bool is_remote = false;
> -    if (binding->parent_port && *binding->parent_port) {
> -        if (!binding->tag) {
> -            goto out;
> -        }
> -        ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
> -                                      binding->parent_port));
> -        if (ofport) {
> -            tag = *binding->tag;
> -            nested_container = true;
> -            parent_port = lport_lookup_by_name(
> -                sbrec_port_binding_by_name, binding->parent_port);
> -        }
> -    } else {
> -        ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
> -                                      binding->logical_port));
> -        const char *requested_chassis = smap_get(&binding->options,
> -                                                 "requested-chassis");
> -        if (ofport && requested_chassis && requested_chassis[0] &&
> -            strcmp(requested_chassis, chassis->name) &&
> -            strcmp(requested_chassis, chassis->hostname)) {
> -            /* Even though there is an ofport for this port_binding, it is
> -             * requested on a different chassis. So ignore this ofport.
> -             */
> -            ofport = 0;
> -        }
> -
> -        if ((!strcmp(binding->type, "localnet")
> -            || !strcmp(binding->type, "l2gateway"))
> -            && ofport && binding->tag) {
> -            tag = *binding->tag;
> -        }
> -    }
> -
> -    bool is_ha_remote = false;
> -    const struct chassis_tunnel *tun = NULL;
> -    const struct sbrec_port_binding *localnet_port =
> -        get_localnet_port(local_datapaths, dp_key);
> -    if (!ofport) {
> -        /* It is remote port, may be reached by tunnel or localnet port */
> -        is_remote = true;
> -        if (localnet_port) {
> -            ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
> -                                          localnet_port->logical_port));
> -            if (!ofport) {
> -                goto out;
> -            }
> -        } else {
> -            if (!ha_ch_ordered || ha_ch_ordered->n_ha_ch < 2) {
> -                /* It's on a single remote chassis */
> -                if (!binding->chassis) {
> -                    goto out;
> -                }
> -                tun = chassis_tunnel_find(binding->chassis->name, NULL);
> -                if (!tun) {
> -                    goto out;
> -                }
> -                ofport = tun->ofport;
> -            } else {
> -                /* It's distributed across the chassis belonging to
> -                 * an HA chassis group. */
> -                is_ha_remote = true;
> -            }
> -        }
> -    }
> -
> -    if (!is_remote) {
> -        /* Packets that arrive from a vif can belong to a VM or
> -         * to a container located inside that VM. Packets that
> -         * arrive from containers have a tag (vlan) associated with them.
> -         */
> -
> -        struct zone_ids zone_ids = get_zone_ids(binding, ct_zones);
> -        uint32_t parent_port_key = parent_port ? parent_port->tunnel_key
> : 0;
> -        /* Pass the parent port tunnel key if the port is a nested
> -         * container. */
> -        put_local_common_flows(dp_key, port_key, parent_port_key,
> &zone_ids,
> -                               ofpacts_p, flow_table);
> -
> -        /* Table 0, Priority 150 and 100.
> -         * ==============================
> -         *
> -         * Priority 150 is for tagged traffic. This may be containers in a
> -         * VM or a VLAN on a local network. For such traffic, match on the
> -         * tags and then strip the tag.
> -         *
> -         * Priority 100 is for traffic belonging to VMs or untagged
> locally
> -         * connected networks.
> -         *
> -         * For both types of traffic: set MFF_LOG_INPORT to the logical
> -         * input port, MFF_LOG_DATAPATH to the logical datapath, and
> -         * resubmit into the logical ingress pipeline starting at table
> -         * 16. */
> -        ofpbuf_clear(ofpacts_p);
> -        match_init_catchall(&match);
> -        match_set_in_port(&match, ofport);
> -
> -        /* Match a VLAN tag and strip it, including stripping priority
> tags
> -         * (e.g. VLAN ID 0).  In the latter case we'll add a second flow
> -         * for frames that lack any 802.1Q header later. */
> -        if (tag || !strcmp(binding->type, "localnet")
> -            || !strcmp(binding->type, "l2gateway")) {
> -            match_set_dl_vlan(&match, htons(tag), 0);
> -            if (nested_container) {
> -                /* When a packet comes from a container sitting behind a
> -                 * parent_port, we should let it loopback to other
> containers
> -                 * or the parent_port itself. Indicate this by setting the
> -                 * MLF_NESTED_CONTAINER_BIT in MFF_LOG_FLAGS.*/
> -                put_load(1, MFF_LOG_FLAGS, MLF_NESTED_CONTAINER_BIT, 1,
> -                         ofpacts_p);
> -            }
> -            ofpact_put_STRIP_VLAN(ofpacts_p);
> -        }
> -
> -        /* Remember the size with just strip vlan added so far,
> -         * as we're going to remove this with ofpbuf_pull() later. */
> -        uint32_t ofpacts_orig_size = ofpacts_p->size;
> -
> -        load_logical_ingress_metadata(binding, &zone_ids, ofpacts_p);
> -
> -        /* Resubmit to first logical ingress pipeline table. */
> -        put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
> -        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
> -                        tag ? 150 : 100, 0, &match, ofpacts_p,
> -                        &binding->header_.uuid);
> -
> -        if (!tag && (!strcmp(binding->type, "localnet")
> -                     || !strcmp(binding->type, "l2gateway"))) {
> -
> -            /* Add a second flow for frames that lack any 802.1Q
> -             * header.  For these, drop the OFPACT_STRIP_VLAN
> -             * action. */
> -            ofpbuf_pull(ofpacts_p, ofpacts_orig_size);
> -            match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
> -            ofctrl_add_flow(flow_table, 0, 100, 0, &match, ofpacts_p,
> -                            &binding->header_.uuid);
> -        }
> -
> -        /* Table 65, Priority 100.
> -         * =======================
> -         *
> -         * Deliver the packet to the local vif. */
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -        if (tag) {
> -            /* For containers sitting behind a local vif, tag the packets
> -             * before delivering them. */
> -            struct ofpact_vlan_vid *vlan_vid;
> -            vlan_vid = ofpact_put_SET_VLAN_VID(ofpacts_p);
> -            vlan_vid->vlan_vid = tag;
> -            vlan_vid->push_vlan_if_needed = true;
> -        }
> -        ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
> -        if (tag) {
> -            /* Revert the tag added to the packets headed to containers
> -             * in the previous step. If we don't do this, the packets
> -             * that are to be broadcasted to a VM in the same logical
> -             * switch will also contain the tag. */
> -            ofpact_put_STRIP_VLAN(ofpacts_p);
> -        }
> -        ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
> -                        &match, ofpacts_p, &binding->header_.uuid);
> -
> -        if (!strcmp(binding->type, "localnet")) {
> -            put_replace_router_port_mac_flows(binding, chassis,
> -                                              local_datapaths, ofpacts_p,
> -                                              ofport, flow_table);
> -        }
> -
> -    } else if (!tun && !is_ha_remote) {
> -        /* Remote port connected by localnet port */
> -        /* Table 33, priority 100.
> -         * =======================
> -         *
> -         * Implements switching to localnet port. Each flow matches a
> -         * logical output port on remote hypervisor, switch the output
> port
> -         * to connected localnet port and resubmits to same table.
> -         */
> -
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -
> -        /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -
> -        put_load(localnet_port->tunnel_key, MFF_LOG_OUTPORT, 0, 32,
> ofpacts_p);
> -
> -        /* Resubmit to table 33. */
> -        put_resubmit(OFTABLE_LOCAL_OUTPUT, ofpacts_p);
> -        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
> -                        &match, ofpacts_p, &binding->header_.uuid);
> -    } else {
> -        /* Remote port connected by tunnel */
> -
> -        /* Table 32, priority 100.
> -         * =======================
> -         *
> -         * Handles traffic that needs to be sent to a remote hypervisor.
> Each
> -         * flow matches an output port that includes a logical port on a
> remote
> -         * hypervisor, and tunnels the packet to that hypervisor.
> -         */
> -        match_init_catchall(&match);
> -        ofpbuf_clear(ofpacts_p);
> -
> -        /* Match MFF_LOG_DATAPATH, MFF_LOG_OUTPORT. */
> -        match_set_metadata(&match, htonll(dp_key));
> -        match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
> -
> -        if (!is_ha_remote) {
> -            /* Setup encapsulation */
> -            const struct chassis_tunnel *rem_tun =
> -                get_port_binding_tun(binding);
> -            if (!rem_tun) {
> -                goto out;
> -            }
> -            put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
> -                              port_key, ofpacts_p);
> -            /* Output to tunnel. */
> -            ofpact_put_OUTPUT(ofpacts_p)->port = rem_tun->ofport;
> -        } else {
> -            /* Make sure all tunnel endpoints use the same encapsulation,
> -             * and set it up */
> -            for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
> -                const struct sbrec_chassis *ch =
> -                    ha_ch_ordered->ha_ch[i].chassis;
> -                if (!ch) {
> -                    continue;
> -                }
> -                if (!tun) {
> -                    tun = chassis_tunnel_find(ch->name, NULL);
> -                } else {
> -                    struct chassis_tunnel *chassis_tunnel =
> -                        chassis_tunnel_find(ch->name, NULL);
> -                    if (chassis_tunnel &&
> -                        tun->type != chassis_tunnel->type) {
> -                        static struct vlog_rate_limit rl =
> -                            VLOG_RATE_LIMIT_INIT(1, 1);
> -                        VLOG_ERR_RL(&rl, "Port %s has Gateway_Chassis "
> -                                            "with mixed encapsulations,
> only "
> -                                            "uniform encapsulations are "
> -                                            "supported.",
> -                                    binding->logical_port);
> -                        goto out;
> -                    }
> -                }
> -            }
> -            if (!tun) {
> -                static struct vlog_rate_limit rl =
> VLOG_RATE_LIMIT_INIT(1, 1);
> -                VLOG_ERR_RL(&rl, "No tunnel endpoint found for HA chassis
> in "
> -                                 "HA chassis group of port %s",
> -                            binding->logical_port);
> -                goto out;
> -            }
> -
> -            put_encapsulation(mff_ovn_geneve, tun, binding->datapath,
> -                              port_key, ofpacts_p);
> -
> -            /* Output to tunnels with active/backup */
> -            struct ofpact_bundle *bundle = ofpact_put_BUNDLE(ofpacts_p);
> -
> -            for (size_t i = 0; i < ha_ch_ordered->n_ha_ch; i++) {
> -                const struct sbrec_chassis *ch =
> -                    ha_ch_ordered->ha_ch[i].chassis;
> -                if (!ch) {
> -                    continue;
> -                }
> -                tun = chassis_tunnel_find(ch->name, NULL);
> -                if (!tun) {
> -                    continue;
> -                }
> -                if (bundle->n_slaves >= BUNDLE_MAX_SLAVES) {
> -                    static struct vlog_rate_limit rl =
> -                            VLOG_RATE_LIMIT_INIT(1, 1);
> -                    VLOG_WARN_RL(&rl, "Remote endpoints for port beyond "
> -                                        "BUNDLE_MAX_SLAVES");
> -                    break;
> -                }
> -                ofpbuf_put(ofpacts_p, &tun->ofport,
> -                            sizeof tun->ofport);
> -                bundle = ofpacts_p->header;
> -                bundle->n_slaves++;
> -            }
> -
> -            bundle->algorithm = NX_BD_ALG_ACTIVE_BACKUP;
> -            /* Although ACTIVE_BACKUP bundle algorithm seems to ignore
> -             * the next two fields, those are always set */
> -            bundle->basis = 0;
> -            bundle->fields = NX_HASH_FIELDS_ETH_SRC;
> -            ofpact_finish_BUNDLE(ofpacts_p, &bundle);
> -        }
> -        ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
> -                        &match, ofpacts_p, &binding->header_.uuid);
> -    }
> -out:
> -    if (ha_ch_ordered) {
> -        ha_chassis_destroy_ordered(ha_ch_ordered);
> -    }
> -}
> -
> -static void
> -consider_mc_group(enum mf_field_id mff_ovn_geneve,
> -                  const struct simap *ct_zones,
> -                  const struct hmap *local_datapaths,
> -                  const struct sbrec_chassis *chassis,
> -                  const struct sbrec_multicast_group *mc,
> -                  struct ovn_desired_flow_table *flow_table)
> -{
> -    uint32_t dp_key = mc->datapath->tunnel_key;
> -    if (!get_local_datapath(local_datapaths, dp_key)) {
> -        return;
> -    }
> -
> -    struct sset remote_chassis = SSET_INITIALIZER(&remote_chassis);
> -    struct match match;
> -
> -    match_init_catchall(&match);
> -    match_set_metadata(&match, htonll(dp_key));
> -    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, mc->tunnel_key);
> -
> -    /* Go through all of the ports in the multicast group:
> -     *
> -     *    - For remote ports, add the chassis to 'remote_chassis'.
> -     *
> -     *    - For local ports (other than logical patch ports), add actions
> -     *      to 'ofpacts' to set the output port and resubmit.
> -     *
> -     *    - For logical patch ports, add actions to 'remote_ofpacts'
> -     *      instead.  (If we put them in 'ofpacts', then the output
> -     *      would happen on every hypervisor in the multicast group,
> -     *      effectively duplicating the packet.)
> -     */
> -    struct ofpbuf ofpacts;
> -    ofpbuf_init(&ofpacts, 0);
> -    struct ofpbuf remote_ofpacts;
> -    ofpbuf_init(&remote_ofpacts, 0);
> -    for (size_t i = 0; i < mc->n_ports; i++) {
> -        struct sbrec_port_binding *port = mc->ports[i];
> -
> -        if (port->datapath != mc->datapath) {
> -            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
> -            VLOG_WARN_RL(&rl, UUID_FMT": multicast group contains ports "
> -                         "in wrong datapath",
> -                         UUID_ARGS(&mc->header_.uuid));
> -            continue;
> -        }
> -
> -        int zone_id = simap_get(ct_zones, port->logical_port);
> -        if (zone_id) {
> -            put_load(zone_id, MFF_LOG_CT_ZONE, 0, 32, &ofpacts);
> -        }
> -
> -        if (!strcmp(port->type, "patch")) {
> -            put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32,
> -                     &remote_ofpacts);
> -            put_resubmit(OFTABLE_CHECK_LOOPBACK, &remote_ofpacts);
> -        } else if (simap_contains(&localvif_to_ofport,
> -                           (port->parent_port && *port->parent_port)
> -                           ? port->parent_port : port->logical_port)
> -                   || (!strcmp(port->type, "l3gateway")
> -                       && port->chassis == chassis)) {
> -            put_load(port->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
> -            put_resubmit(OFTABLE_CHECK_LOOPBACK, &ofpacts);
> -        } else if (port->chassis && !get_localnet_port(local_datapaths,
> -                                         mc->datapath->tunnel_key)) {
> -            /* Add remote chassis only when localnet port not exist,
> -             * otherwise multicast will reach remote ports through
> localnet
> -             * port. */
> -            sset_add(&remote_chassis, port->chassis->name);
> -        }
> -    }
> -
> -    /* Table 33, priority 100.
> -     * =======================
> -     *
> -     * Handle output to the local logical ports in the multicast group, if
> -     * any. */
> -    bool local_ports = ofpacts.size > 0;
> -    if (local_ports) {
> -        /* Following delivery to local logical ports, restore the
> multicast
> -         * group as the logical output port. */
> -        put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
> -
> -        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
> -                        &match, &ofpacts, &mc->header_.uuid);
> -    }
> -
> -    /* Table 32, priority 100.
> -     * =======================
> -     *
> -     * Handle output to the remote chassis in the multicast group, if
> -     * any. */
> -    if (!sset_is_empty(&remote_chassis) || remote_ofpacts.size > 0) {
> -        if (remote_ofpacts.size > 0) {
> -            /* Following delivery to logical patch ports, restore the
> -             * multicast group as the logical output port. */
> -            put_load(mc->tunnel_key, MFF_LOG_OUTPORT, 0, 32,
> -                     &remote_ofpacts);
> -        }
> -
> -        const char *chassis_name;
> -        const struct chassis_tunnel *prev = NULL;
> -        SSET_FOR_EACH (chassis_name, &remote_chassis) {
> -            const struct chassis_tunnel *tun
> -                = chassis_tunnel_find(chassis_name, NULL);
> -            if (!tun) {
> -                continue;
> -            }
> -
> -            if (!prev || tun->type != prev->type) {
> -                put_encapsulation(mff_ovn_geneve, tun, mc->datapath,
> -                                  mc->tunnel_key, &remote_ofpacts);
> -                prev = tun;
> -            }
> -            ofpact_put_OUTPUT(&remote_ofpacts)->port = tun->ofport;
> -        }
> -
> -        if (remote_ofpacts.size) {
> -            if (local_ports) {
> -                put_resubmit(OFTABLE_LOCAL_OUTPUT, &remote_ofpacts);
> -            }
> -            ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
> -                            &match, &remote_ofpacts, &mc->header_.uuid);
> -        }
> -    }
> -    ofpbuf_uninit(&ofpacts);
> -    ofpbuf_uninit(&remote_ofpacts);
> -    sset_destroy(&remote_chassis);
> -}
> -
> -/* Replaces 'old' by 'new' (destroying 'new').  Returns true if 'old' and
> 'new'
> - * contained different data, false if they were the same. */
> -static bool
> -update_ofports(struct simap *old, struct simap *new)
> -{
> -    bool changed = !simap_equal(old, new);
> -    simap_swap(old, new);
> -    simap_destroy(new);
> -    return changed;
> -}
> -
> -void physical_handle_port_binding_changes(
> -        struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -        const struct sbrec_port_binding_table *pb_table,
> -        enum mf_field_id mff_ovn_geneve,
> -        const struct sbrec_chassis *chassis,
> -        const struct simap *ct_zones,
> -        struct hmap *local_datapaths,
> -        struct sset *active_tunnels,
> -        struct ovn_desired_flow_table *flow_table)
> -{
> -    const struct sbrec_port_binding *binding;
> -    struct ofpbuf ofpacts;
> -    ofpbuf_init(&ofpacts, 0);
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (binding, pb_table) {
> -        if (sbrec_port_binding_is_deleted(binding)) {
> -            ofctrl_remove_flows(flow_table, &binding->header_.uuid);
> -        } else {
> -            if (!sbrec_port_binding_is_new(binding)) {
> -                ofctrl_remove_flows(flow_table, &binding->header_.uuid);
> -            }
> -            consider_port_binding(sbrec_port_binding_by_name,
> -                                  mff_ovn_geneve, ct_zones,
> -                                  active_tunnels, local_datapaths,
> -                                  binding, chassis,
> -                                  flow_table, &ofpacts);
> -        }
> -    }
> -
> -}
> -
> -void
> -physical_handle_mc_group_changes(
> -        const struct sbrec_multicast_group_table *multicast_group_table,
> -        enum mf_field_id mff_ovn_geneve,
> -        const struct sbrec_chassis *chassis,
> -        const struct simap *ct_zones,
> -        const struct hmap *local_datapaths,
> -        struct ovn_desired_flow_table *flow_table)
> -{
> -    const struct sbrec_multicast_group *mc;
> -    SBREC_MULTICAST_GROUP_TABLE_FOR_EACH_TRACKED (mc,
> multicast_group_table) {
> -        if (sbrec_multicast_group_is_deleted(mc)) {
> -            ofctrl_remove_flows(flow_table, &mc->header_.uuid);
> -        } else {
> -            if (!sbrec_multicast_group_is_new(mc)) {
> -                ofctrl_remove_flows(flow_table, &mc->header_.uuid);
> -            }
> -            consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths,
> -                              chassis, mc, flow_table);
> -        }
> -    }
> -}
> -
> -void
> -physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -             const struct sbrec_multicast_group_table
> *multicast_group_table,
> -             const struct sbrec_port_binding_table *port_binding_table,
> -             enum mf_field_id mff_ovn_geneve,
> -             const struct ovsrec_bridge *br_int,
> -             const struct sbrec_chassis *chassis,
> -             const struct simap *ct_zones,
> -             const struct hmap *local_datapaths,
> -             const struct sset *local_lports,
> -             const struct sset *active_tunnels,
> -             struct ovn_desired_flow_table *flow_table)
> -{
> -    if (!hc_uuid) {
> -        hc_uuid = xmalloc(sizeof(struct uuid));
> -        uuid_generate(hc_uuid);
> -    }
> -
> -    /* This bool tracks physical mapping changes. */
> -    bool physical_map_changed = false;
> -
> -    struct simap new_localvif_to_ofport =
> -        SIMAP_INITIALIZER(&new_localvif_to_ofport);
> -    struct simap new_tunnel_to_ofport =
> -        SIMAP_INITIALIZER(&new_tunnel_to_ofport);
> -    for (int i = 0; i < br_int->n_ports; i++) {
> -        const struct ovsrec_port *port_rec = br_int->ports[i];
> -        if (!strcmp(port_rec->name, br_int->name)) {
> -            continue;
> -        }
> -
> -        const char *tunnel_id = smap_get(&port_rec->external_ids,
> -                                         "ovn-chassis-id");
> -        if (tunnel_id &&
> -                encaps_tunnel_id_match(tunnel_id, chassis->name, NULL)) {
> -            continue;
> -        }
> -
> -        const char *localnet = smap_get(&port_rec->external_ids,
> -                                        "ovn-localnet-port");
> -        const char *l2gateway = smap_get(&port_rec->external_ids,
> -                                        "ovn-l2gateway-port");
> -
> -        for (int j = 0; j < port_rec->n_interfaces; j++) {
> -            const struct ovsrec_interface *iface_rec =
> port_rec->interfaces[j];
> -
> -            /* Get OpenFlow port number. */
> -            if (!iface_rec->n_ofport) {
> -                continue;
> -            }
> -            int64_t ofport = iface_rec->ofport[0];
> -            if (ofport < 1 || ofport > ofp_to_u16(OFPP_MAX)) {
> -                continue;
> -            }
> -
> -            /* Record as patch to local net, logical patch port, chassis,
> or
> -             * local logical port. */
> -            bool is_patch = !strcmp(iface_rec->type, "patch");
> -            if (is_patch && localnet) {
> -                /* localnet patch ports can be handled just like VIFs. */
> -                simap_put(&new_localvif_to_ofport, localnet, ofport);
> -                break;
> -            } else if (is_patch && l2gateway) {
> -                /* L2 gateway patch ports can be handled just like VIFs.
> */
> -                simap_put(&new_localvif_to_ofport, l2gateway, ofport);
> -                break;
> -            } else if (tunnel_id) {
> -                enum chassis_tunnel_type tunnel_type;
> -                if (!strcmp(iface_rec->type, "geneve")) {
> -                    tunnel_type = GENEVE;
> -                    if (!mff_ovn_geneve) {
> -                        continue;
> -                    }
> -                } else if (!strcmp(iface_rec->type, "stt")) {
> -                    tunnel_type = STT;
> -                } else if (!strcmp(iface_rec->type, "vxlan")) {
> -                    tunnel_type = VXLAN;
> -                } else {
> -                    continue;
> -                }
> -
> -                simap_put(&new_tunnel_to_ofport, tunnel_id, ofport);
> -                /*
> -                 * We split the tunnel_id to get the chassis-id
> -                 * and hash the tunnel list on the chassis-id. The
> -                 * reason to use the chassis-id alone is because
> -                 * there might be cases (multicast, gateway chassis)
> -                 * where we need to tunnel to the chassis, but won't
> -                 * have the encap-ip specifically.
> -                 */
> -                char *hash_id = NULL;
> -                char *ip = NULL;
> -
> -                if (!encaps_tunnel_id_parse(tunnel_id, &hash_id, &ip)) {
> -                    continue;
> -                }
> -                struct chassis_tunnel *tun = chassis_tunnel_find(hash_id,
> ip);
> -                if (tun) {
> -                    /* If the tunnel's ofport has changed, update. */
> -                    if (tun->ofport != u16_to_ofp(ofport) ||
> -                        tun->type != tunnel_type) {
> -                        tun->ofport = u16_to_ofp(ofport);
> -                        tun->type = tunnel_type;
> -                        physical_map_changed = true;
> -                    }
> -                } else {
> -                    tun = xmalloc(sizeof *tun);
> -                    hmap_insert(&tunnels, &tun->hmap_node,
> -                                hash_string(hash_id, 0));
> -                    tun->chassis_id = xstrdup(tunnel_id);
> -                    tun->ofport = u16_to_ofp(ofport);
> -                    tun->type = tunnel_type;
> -                    physical_map_changed = true;
> -                }
> -                free(hash_id);
> -                free(ip);
> -                break;
> -            } else {
> -                const char *iface_id = smap_get(&iface_rec->external_ids,
> -                                                "iface-id");
> -                if (iface_id) {
> -                    simap_put(&new_localvif_to_ofport, iface_id, ofport);
> -                }
> -            }
> -        }
> -    }
> -
> -    /* Remove tunnels that are no longer here. */
> -    struct chassis_tunnel *tun, *tun_next;
> -    HMAP_FOR_EACH_SAFE (tun, tun_next, hmap_node, &tunnels) {
> -        if (!simap_find(&new_tunnel_to_ofport, tun->chassis_id)) {
> -            hmap_remove(&tunnels, &tun->hmap_node);
> -            physical_map_changed = true;
> -            free(tun->chassis_id);
> -            free(tun);
> -        }
> -    }
> -
> -    /* Capture changed or removed openflow ports. */
> -    physical_map_changed |= update_ofports(&localvif_to_ofport,
> -                                           &new_localvif_to_ofport);
> -    if (physical_map_changed) {
> -        /* Reprocess logical flow table immediately. */
> -        poll_immediate_wake();
> -    }
> -
> -    struct ofpbuf ofpacts;
> -    ofpbuf_init(&ofpacts, 0);
> -
> -    /* Set up flows in table 0 for physical-to-logical translation and in
> table
> -     * 64 for logical-to-physical translation. */
> -    const struct sbrec_port_binding *binding;
> -    SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
> -        consider_port_binding(sbrec_port_binding_by_name,
> -                              mff_ovn_geneve, ct_zones,
> -                              active_tunnels, local_datapaths,
> -                              binding, chassis,
> -                              flow_table, &ofpacts);
> -    }
> -
> -    /* Handle output to multicast groups, in tables 32 and 33. */
> -    const struct sbrec_multicast_group *mc;
> -    SBREC_MULTICAST_GROUP_TABLE_FOR_EACH (mc, multicast_group_table) {
> -        consider_mc_group(mff_ovn_geneve, ct_zones, local_datapaths,
> -                          chassis, mc, flow_table);
> -    }
> -
> -    /* Table 0, priority 100.
> -     * ======================
> -     *
> -     * Process packets that arrive from a remote hypervisor (by matching
> -     * on tunnel in_port). */
> -
> -    /* Add flows for Geneve and STT encapsulations.  These
> -     * encapsulations have metadata about the ingress and egress logical
> -     * ports.  We set MFF_LOG_DATAPATH, MFF_LOG_INPORT, and
> -     * MFF_LOG_OUTPORT from the tunnel key data, then resubmit to table
> -     * 33 to handle packets to the local hypervisor. */
> -    HMAP_FOR_EACH (tun, hmap_node, &tunnels) {
> -        struct match match = MATCH_CATCHALL_INITIALIZER;
> -        match_set_in_port(&match, tun->ofport);
> -
> -        ofpbuf_clear(&ofpacts);
> -        if (tun->type == GENEVE) {
> -            put_move(MFF_TUN_ID, 0,  MFF_LOG_DATAPATH, 0, 24, &ofpacts);
> -            put_move(mff_ovn_geneve, 16, MFF_LOG_INPORT, 0, 15,
> -                     &ofpacts);
> -            put_move(mff_ovn_geneve, 0, MFF_LOG_OUTPORT, 0, 16,
> -                     &ofpacts);
> -        } else if (tun->type == STT) {
> -            put_move(MFF_TUN_ID, 40, MFF_LOG_INPORT,   0, 15, &ofpacts);
> -            put_move(MFF_TUN_ID, 24, MFF_LOG_OUTPORT,  0, 16, &ofpacts);
> -            put_move(MFF_TUN_ID,  0, MFF_LOG_DATAPATH, 0, 24, &ofpacts);
> -        } else if (tun->type == VXLAN) {
> -            /* We'll handle VXLAN later. */
> -            continue;
> -        } else {
> -            OVS_NOT_REACHED();
> -        }
> -
> -        put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
> -
> -        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0, &match,
> -                        &ofpacts, hc_uuid);
> -    }
> -
> -    /* Add flows for VXLAN encapsulations.  Due to the limited amount of
> -     * metadata, we only support VXLAN for connections to gateways.  The
> -     * VNI is used to populate MFF_LOG_DATAPATH.  The gateway's logical
> -     * port is set to MFF_LOG_INPORT.  Then the packet is resubmitted to
> -     * table 16 to determine the logical egress port. */
> -    HMAP_FOR_EACH (tun, hmap_node, &tunnels) {
> -        if (tun->type != VXLAN) {
> -            continue;
> -        }
> -
> -        SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
> -            struct match match = MATCH_CATCHALL_INITIALIZER;
> -
> -            if (!binding->chassis ||
> -                !encaps_tunnel_id_match(tun->chassis_id,
> -                                        binding->chassis->name, NULL)) {
> -                continue;
> -            }
> -
> -            match_set_in_port(&match, tun->ofport);
> -            match_set_tun_id(&match,
> htonll(binding->datapath->tunnel_key));
> -
> -            ofpbuf_clear(&ofpacts);
> -            put_move(MFF_TUN_ID, 0,  MFF_LOG_DATAPATH, 0, 24, &ofpacts);
> -            put_load(binding->tunnel_key, MFF_LOG_INPORT, 0, 15,
> &ofpacts);
> -            /* For packets received from a vxlan tunnel, set a flag to
> that
> -             * effect. */
> -            put_load(1, MFF_LOG_FLAGS, MLF_RCV_FROM_VXLAN_BIT, 1,
> &ofpacts);
> -            put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, &ofpacts);
> -
> -            ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 100, 0,
> &match,
> -                            &ofpacts, hc_uuid);
> -        }
> -    }
> -
> -    /* Table 32, priority 150.
> -     * =======================
> -     *
> -     * Handles packets received from a VXLAN tunnel which get resubmitted
> to
> -     * OFTABLE_LOG_INGRESS_PIPELINE due to lack of needed metadata in
> VXLAN,
> -     * explicitly skip sending back out any tunnels and resubmit to table
> 33
> -     * for local delivery.
> -     */
> -    struct match match;
> -    match_init_catchall(&match);
> -    match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
> -                         MLF_RCV_FROM_VXLAN, MLF_RCV_FROM_VXLAN);
> -
> -    /* Resubmit to table 33. */
> -    ofpbuf_clear(&ofpacts);
> -    put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
> -    ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
> -                    &match, &ofpacts, hc_uuid);
> -
> -    /* Table 32, priority 150.
> -     * =======================
> -     *
> -     * Packets that should not be sent to other hypervisors.
> -     */
> -    match_init_catchall(&match);
> -    match_set_reg_masked(&match, MFF_LOG_FLAGS - MFF_REG0,
> -                         MLF_LOCAL_ONLY, MLF_LOCAL_ONLY);
> -    /* Resubmit to table 33. */
> -    ofpbuf_clear(&ofpacts);
> -    put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
> -    ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
> -                    &match, &ofpacts, hc_uuid);
> -
> -    /* Table 32, priority 150.
> -     * =======================
> -     *
> -     * Handles packets received from ports of type "localport".  These
> ports
> -     * are present on every hypervisor.  Traffic that originates at one
> should
> -     * never go over a tunnel to a remote hypervisor, so resubmit them to
> table
> -     * 33 for local delivery. */
> -    match_init_catchall(&match);
> -    ofpbuf_clear(&ofpacts);
> -    put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
> -    const char *localport;
> -    SSET_FOR_EACH (localport, local_lports) {
> -        /* Iterate over all local logical ports and insert a drop
> -         * rule with higher priority for every localport in this
> -         * datapath. */
> -        const struct sbrec_port_binding *pb = lport_lookup_by_name(
> -            sbrec_port_binding_by_name, localport);
> -        if (pb && !strcmp(pb->type, "localport")) {
> -            match_set_reg(&match, MFF_LOG_INPORT - MFF_REG0,
> pb->tunnel_key);
> -            match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
> -            ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 150, 0,
> -                            &match, &ofpacts, hc_uuid);
> -        }
> -    }
> -
> -    /* Table 32, Priority 0.
> -     * =======================
> -     *
> -     * Resubmit packets that are not directed at tunnels or part of a
> -     * multicast group to the local output table. */
> -    match_init_catchall(&match);
> -    ofpbuf_clear(&ofpacts);
> -    put_resubmit(OFTABLE_LOCAL_OUTPUT, &ofpacts);
> -    ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 0, 0, &match,
> &ofpacts,
> -                    hc_uuid);
> -
> -    /* Table 34, Priority 0.
> -     * =======================
> -     *
> -     * Resubmit packets that don't output to the ingress port (already
> checked
> -     * in table 33) to the logical egress pipeline, clearing the logical
> -     * registers (for consistent behavior with packets that get
> tunneled). */
> -    match_init_catchall(&match);
> -    ofpbuf_clear(&ofpacts);
> -    for (int i = 0; i < MFF_N_LOG_REGS; i++) {
> -        put_load(0, MFF_REG0 + i, 0, 32, &ofpacts);
> -    }
> -    put_resubmit(OFTABLE_LOG_EGRESS_PIPELINE, &ofpacts);
> -    ofctrl_add_flow(flow_table, OFTABLE_CHECK_LOOPBACK, 0, 0, &match,
> -                    &ofpacts, hc_uuid);
> -
> -    /* Table 64, Priority 0.
> -     * =======================
> -     *
> -     * Resubmit packets that do not have the MLF_ALLOW_LOOPBACK flag set
> -     * to table 65 for logical-to-physical translation. */
> -    match_init_catchall(&match);
> -    ofpbuf_clear(&ofpacts);
> -    put_resubmit(OFTABLE_LOG_TO_PHY, &ofpacts);
> -    ofctrl_add_flow(flow_table, OFTABLE_SAVE_INPORT, 0, 0, &match,
> &ofpacts,
> -                    hc_uuid);
> -
> -    ofpbuf_uninit(&ofpacts);
> -
> -    simap_destroy(&new_tunnel_to_ofport);
> -}
> diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
> deleted file mode 100644
> index c5544e8de..000000000
> --- a/ovn/controller/physical.h
> +++ /dev/null
> @@ -1,74 +0,0 @@
> -/* Copyright (c) 2015, 2016 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#ifndef OVN_PHYSICAL_H
> -#define OVN_PHYSICAL_H 1
> -
> -/* Logical/Physical Translation
> - * ============================
> - *
> - * This module implements physical-to-logical and logical-to-physical
> - * translation as separate OpenFlow tables that run before the ingress
> pipeline
> - * and after the egress pipeline, respectively, as well as to connect the
> - * two pipelines.
> - */
> -
> -#include "openvswitch/meta-flow.h"
> -
> -struct hmap;
> -struct ovsdb_idl_index;
> -struct ovsrec_bridge;
> -struct simap;
> -struct sbrec_multicast_group_table;
> -struct sbrec_port_binding_table;
> -struct sset;
> -
> -/* OVN Geneve option information.
> - *
> - * Keep these in sync with the documentation in ovn-architecture(7). */
> -#define OVN_GENEVE_CLASS 0x0102  /* Assigned Geneve class for OVN. */
> -#define OVN_GENEVE_TYPE 0x80     /* Critical option. */
> -#define OVN_GENEVE_LEN 4
> -
> -void physical_register_ovs_idl(struct ovsdb_idl *);
> -void physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                  const struct sbrec_multicast_group_table *,
> -                  const struct sbrec_port_binding_table *,
> -                  enum mf_field_id mff_ovn_geneve,
> -                  const struct ovsrec_bridge *br_int,
> -                  const struct sbrec_chassis *chassis,
> -                  const struct simap *ct_zones,
> -                  const struct hmap *local_datapaths,
> -                  const struct sset *local_lports,
> -                  const struct sset *active_tunnels,
> -                  struct ovn_desired_flow_table *);
> -void physical_handle_port_binding_changes(
> -        struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -        const struct sbrec_port_binding_table *,
> -        enum mf_field_id mff_ovn_geneve,
> -        const struct sbrec_chassis *,
> -        const struct simap *ct_zones,
> -        struct hmap *local_datapaths,
> -        struct sset *active_tunnels,
> -        struct ovn_desired_flow_table *);
> -
> -void physical_handle_mc_group_changes(
> -        const struct sbrec_multicast_group_table *,
> -        enum mf_field_id mff_ovn_geneve,
> -        const struct sbrec_chassis *,
> -        const struct simap *ct_zones,
> -        const struct hmap *local_datapaths,
> -        struct ovn_desired_flow_table *);
> -#endif /* ovn/physical.h */
> diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
> deleted file mode 100644
> index d857067a5..000000000
> --- a/ovn/controller/pinctrl.c
> +++ /dev/null
> @@ -1,4343 +0,0 @@
> -/* Copyright (c) 2015, 2016, 2017 Red Hat, Inc.
> - * Copyright (c) 2017 Nicira, Inc.
> - *
> - * Licensed under the Apache License, Version 2.0 (the "License");
> - * you may not use this file except in compliance with the License.
> - * You may obtain a copy of the License at:
> - *
> - *     http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing, software
> - * distributed under the License is distributed on an "AS IS" BASIS,
> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> - * See the License for the specific language governing permissions and
> - * limitations under the License.
> - */
> -
> -#include <config.h>
> -
> -#include "pinctrl.h"
> -
> -#include "coverage.h"
> -#include "csum.h"
> -#include "dirs.h"
> -#include "dp-packet.h"
> -#include "encaps.h"
> -#include "flow.h"
> -#include "ha-chassis.h"
> -#include "lport.h"
> -#include "nx-match.h"
> -#include "ovn-controller.h"
> -#include "latch.h"
> -#include "lib/packets.h"
> -#include "lib/sset.h"
> -#include "openvswitch/ofp-actions.h"
> -#include "openvswitch/ofp-msgs.h"
> -#include "openvswitch/ofp-packet.h"
> -#include "openvswitch/ofp-print.h"
> -#include "openvswitch/ofp-switch.h"
> -#include "openvswitch/ofp-util.h"
> -#include "openvswitch/vlog.h"
> -
> -#include "lib/dhcp.h"
> -#include "ovn-controller.h"
> -#include "ovn/actions.h"
> -#include "ovn/lex.h"
> -#include "ovn/lib/acl-log.h"
> -#include "ovn/lib/ip-mcast-index.h"
> -#include "ovn/lib/mcast-group-index.h"
> -#include "ovn/lib/ovn-l7.h"
> -#include "ovn/lib/ovn-util.h"
> -#include "ovn/logical-fields.h"
> -#include "openvswitch/poll-loop.h"
> -#include "openvswitch/rconn.h"
> -#include "socket-util.h"
> -#include "seq.h"
> -#include "timeval.h"
> -#include "vswitch-idl.h"
> -#include "lflow.h"
> -#include "ip-mcast.h"
> -
> -VLOG_DEFINE_THIS_MODULE(pinctrl);
> -
> -/* pinctrl module creates a thread - pinctrl_handler to handle
> - * the packet-ins from ovs-vswitchd. Some of the OVN actions
> - * are translated to OF 'controller' actions. See include/ovn/actions.h
> - * for more details.
> - *
> - * pinctrl_handler thread doesn't access the Southbound IDL object. But
> - * some of the OVN actions which gets translated to 'controller'
> - * OF action, require data from Southbound DB.  Below are the details
> - * on how these actions are implemented.
> - *
> - * pinctrl_run() function is called by ovn-controller main thread.
> - * A Mutex - 'pinctrl_mutex' is used between the pinctrl_handler() thread
> - * and pinctrl_run().
> - *
> - *   - dns_lookup -     In order to do a DNS lookup, this action needs
> - *                      to access the 'DNS' table. pinctrl_run() builds a
> - *                      local DNS cache - 'dns_cache'. See
> sync_dns_cache()
> - *                      for more details.
> - *                      The function 'pinctrl_handle_dns_lookup()' (which
> is
> - *                      called with in the pinctrl_handler thread) looks
> into
> - *                      the local DNS cache to resolve the DNS requests.
> - *
> - *   - put_arp/put_nd - These actions stores the IPv4/IPv6 and MAC
> addresses
> - *                      in the 'MAC_Binding' table.
> - *                      The function 'pinctrl_handle_put_mac_binding()'
> (which
> - *                      is called with in the pinctrl_handler thread),
> stores
> - *                      the IPv4/IPv6 and MAC addresses in the
> - *                      hmap - put_mac_bindings.
> - *
> - *                      pinctrl_run(), reads these mac bindings from the
> hmap
> - *                      'put_mac_bindings' and writes to the 'MAC_Binding'
> - *                      table in the Southbound DB.
> - *
> - *   - arp/nd_ns      - These actions generate an ARP/IPv6 Neighbor
> solicit
> - *                      requests. The original packets are buffered and
> - *                      injected back when put_arp/put_nd resolves
> - *                      corresponding ARP/IPv6 Neighbor solicit requests.
> - *                      When pinctrl_run(), writes the mac bindings from
> the
> - *                      'put_mac_bindings' hmap to the MAC_Binding table
> in
> - *                      SB DB, run_buffered_binding will add the buffered
> - *                      packets to buffered_mac_bindings and notify
> - *                      pinctrl_handler.
> - *
> - *                      The pinctrl_handler thread calls the function -
> - *                      send_mac_binding_buffered_pkts(), which uses
> - *                      the hmap - 'buffered_mac_bindings' and reinjects
> the
> - *                      buffered packets.
> - *
> - *    - igmp          - This action punts an IGMP packet to the controller
> - *                      which maintains multicast group information. The
> - *                      multicast groups (mcast_snoop_map) are synced to
> - *                      the 'IGMP_Group' table by ip_mcast_sync().
> - *                      ip_mcast_sync() also reads the 'IP_Multicast'
> - *                      (snooping and querier) configuration and builds a
> - *                      local configuration mcast_cfg_map.
> - *                      ip_mcast_snoop_run() which runs in the
> - *                      pinctrl_handler() thread configures the per
> datapath
> - *                      mcast_snoop_map entries according to
> mcast_cfg_map.
> - *
> - * pinctrl module also periodically sends IPv6 Router Solicitation
> requests
> - * and gARPs (for the router gateway IPs and configured NAT addresses).
> - *
> - * IPv6 RA handling - pinctrl_run() prepares the IPv6 RA information
> - *                    (see prepare_ipv6_ras()) in the shash 'ipv6_ras' by
> - *                    looking into the Southbound DB table - Port_Binding.
> - *
> - *                    pinctrl_handler thread sends the periodic IPv6 RAs
> using
> - *                    the shash - 'ipv6_ras'
> - *
> - * gARP handling    - pinctrl_run() prepares the gARP information
> - *                    (see send_garp_prepare()) in the shash
> 'send_garp_data'
> - *                    by looking into the Southbound DB table
> Port_Binding.
> - *
> - *                    pinctrl_handler() thread sends these gARPs using the
> - *                    shash 'send_garp_data'.
> - *
> - * IGMP Queries     - pinctrl_run() prepares the IGMP queries (at most one
> - *                    per local datapath) based on the mcast_snoop_map
> - *                    contents and stores them in mcast_query_list.
> - *
> - *                    pinctrl_handler thread sends the periodic IGMP
> queries
> - *                    by walking the mcast_query_list.
> - *
> - * Notification between pinctrl_handler() and pinctrl_run()
> - * -------------------------------------------------------
> - * 'struct seq' is used for notification between pinctrl_handler() thread
> - *  and pinctrl_run().
> - *  'pinctrl_handler_seq' is used by pinctrl_run() to
> - *  wake up pinctrl_handler thread from poll_block() if any changes
> happened
> - *  in 'send_garp_data', 'ipv6_ras' and 'buffered_mac_bindings'
> structures.
> - *
> - *  'pinctrl_main_seq' is used by pinctrl_handler() thread to wake up
> - *  the main thread from poll_block() when mac bindings/igmp groups need
> to
> - *  be updated in the Southboubd DB.
> - * */
> -
> -static struct ovs_mutex pinctrl_mutex = OVS_MUTEX_INITIALIZER;
> -static struct seq *pinctrl_handler_seq;
> -static struct seq *pinctrl_main_seq;
> -
> -static void *pinctrl_handler(void *arg);
> -
> -struct pinctrl {
> -    char *br_int_name;
> -    pthread_t pinctrl_thread;
> -    /* Latch to destroy the 'pinctrl_thread' */
> -    struct latch pinctrl_thread_exit;
> -};
> -
> -static struct pinctrl pinctrl;
> -
> -static void init_buffered_packets_map(void);
> -static void destroy_buffered_packets_map(void);
> -static void
> -run_buffered_binding(struct ovsdb_idl_index
> *sbrec_port_binding_by_datapath,
> -                     struct ovsdb_idl_index
> *sbrec_mac_binding_by_lport_ip,
> -                     const struct hmap *local_datapaths)
> -    OVS_REQUIRES(pinctrl_mutex);
> -
> -static void pinctrl_handle_put_mac_binding(const struct flow *md,
> -                                           const struct flow *headers,
> -                                           bool is_arp)
> -    OVS_REQUIRES(pinctrl_mutex);
> -static void init_put_mac_bindings(void);
> -static void destroy_put_mac_bindings(void);
> -static void run_put_mac_bindings(
> -    struct ovsdb_idl_txn *ovnsb_idl_txn,
> -    struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_key,
> -    struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip)
> -    OVS_REQUIRES(pinctrl_mutex);
> -static void wait_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn);
> -static void flush_put_mac_bindings(void);
> -static void send_mac_binding_buffered_pkts(struct rconn *swconn)
> -    OVS_REQUIRES(pinctrl_mutex);
> -
> -static void init_send_garps(void);
> -static void destroy_send_garps(void);
> -static void send_garp_wait(long long int send_garp_time);
> -static void send_garp_prepare(
> -    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
> -    struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -    const struct ovsrec_bridge *,
> -    const struct sbrec_chassis *,
> -    const struct hmap *local_datapaths,
> -    const struct sset *active_tunnels)
> -    OVS_REQUIRES(pinctrl_mutex);
> -static void send_garp_run(struct rc