diff --git a/SOURCES/0001-Add-DNSSL-support-to-OVN.patch b/SOURCES/0001-Add-DNSSL-support-to-OVN.patch
new file mode 100644
index 0000000..9b53940
--- /dev/null
+++ b/SOURCES/0001-Add-DNSSL-support-to-OVN.patch
@@ -0,0 +1,250 @@
+From 5a12a940f63a5909baee6d00ce9e1696a2e7c96c Mon Sep 17 00:00:00 2001
+Message-Id: <5a12a940f63a5909baee6d00ce9e1696a2e7c96c.1572427188.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 29 Oct 2019 13:18:34 +0100
+Subject: [PATCH ovn] Add DNSSL support to OVN
+
+Introduce the possibility to specify a DNSSL option to Router
+Advertisement packets. DNS Search list can be specified using
+'dnssl' tag in the ipv6_ra_configs column of logical router
+port table
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/pinctrl.c | 63 ++++++++++++++++++++++++++++++++++++++++++++
+ ovn/lib/ovn-l7.h         | 11 ++++++++
+ ovn/northd/ovn-northd.c  |  4 +++
+ ovn/ovn-nb.xml           |  5 ++++
+ tests/ovn.at         | 30 ++++++++++++++-------
+ 5 files changed, 103 insertions(+), 10 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2198,6 +2198,7 @@ struct ipv6_ra_config {
+     struct lport_addresses prefixes;
+     struct in6_addr rdnss;
+     bool has_rdnss;
++    struct ds dnssl;
+ };
+ 
+ struct ipv6_ra_state {
+@@ -2219,6 +2220,7 @@ ipv6_ra_config_delete(struct ipv6_ra_con
+ {
+     if (config) {
+         destroy_lport_addresses(&config->prefixes);
++        ds_destroy(&config->dnssl);
+         free(config);
+     }
+ }
+@@ -2257,6 +2259,7 @@ ipv6_ra_update_config(const struct sbrec
+             nd_ra_min_interval_default(config->max_interval));
+     config->mtu = smap_get_int(&pb->options, "ipv6_ra_mtu", ND_MTU_DEFAULT);
+     config->la_flags = IPV6_ND_RA_OPT_PREFIX_ON_LINK;
++    ds_init(&config->dnssl);
+ 
+     const char *address_mode = smap_get(&pb->options, "ipv6_ra_address_mode");
+     if (!address_mode) {
+@@ -2302,6 +2305,11 @@ ipv6_ra_update_config(const struct sbrec
+     }
+     config->has_rdnss = !!rdnss;
+ 
++    const char *dnssl = smap_get(&pb->options, "ipv6_ra_dnssl");
++    if (dnssl) {
++        ds_put_buffer(&config->dnssl, dnssl, strlen(dnssl));
++    }
++
+     return config;
+ 
+ fail:
+@@ -2358,6 +2366,57 @@ packet_put_ra_rdnss_opt(struct dp_packet
+                                                       prev_l4_size + len * 8));
+ }
+ 
++static void
++packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
++                        char *dnssl_list)
++{
++    size_t prev_l4_size = dp_packet_l4_size(b);
++    size_t size = sizeof(struct ovs_nd_dnssl);
++    struct ip6_hdr *nh = dp_packet_l3(b);
++    char *t0, *r0, dnssl[255] = {};
++    int i = 0;
++
++    /* Multiple DNS Search List must be 'comma' separated
++     * (e.g. "a.b.c, d.e.f"). Domain names must be encoded
++     * as described in Section 3.1 of RFC1035.
++     * (e.g if dns list is a.b.c,www.ovn.org, it will be encoded as:
++     * 01 61 01 62 01 63 00 03 77 77 77 03 6f 76 63 03 6f 72 67 00
++     */
++    for (t0 = strtok_r(dnssl_list, ",", &r0); t0;
++         t0 = strtok_r(NULL, ",", &r0)) {
++        char *t1, *r1;
++
++        size += strlen(t0) + 2;
++        if (size > sizeof(dnssl)) {
++            return;
++        }
++
++        for (t1 = strtok_r(t0, ".", &r1); t1;
++             t1 = strtok_r(NULL, ".", &r1)) {
++            dnssl[i++] = strlen(t1);
++            memcpy(&dnssl[i], t1, strlen(t1));
++            i += strlen(t1);
++        }
++        dnssl[i++] = 0;
++    }
++    size = ROUND_UP(size, 8);
++    nh->ip6_plen = htons(prev_l4_size + size);
++
++    struct ovs_nd_dnssl *nd_dnssl = dp_packet_put_uninit(b, sizeof *nd_dnssl);
++    nd_dnssl->type = ND_OPT_DNSSL;
++    nd_dnssl->len = size / 8;
++    nd_dnssl->reserved = 0;
++    put_16aligned_be32(&nd_dnssl->lifetime, lifetime);
++
++    dp_packet_put(b, dnssl, size - sizeof *nd_dnssl);
++
++    struct ovs_ra_msg *ra = dp_packet_l4(b);
++    ra->icmph.icmp6_cksum = 0;
++    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
++    ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra,
++                                                      prev_l4_size + size));
++}
++
+ /* Called with in the pinctrl_handler thread context. */
+ static long long int
+ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra)
+@@ -2386,6 +2445,10 @@ ipv6_ra_send(struct rconn *swconn, struc
+         packet_put_ra_rdnss_opt(&packet, 1, htonl(0xffffffff),
+                                 &ra->config->rdnss);
+     }
++    if (ra->config->dnssl.length) {
++        packet_put_ra_dnssl_opt(&packet, htonl(0xffffffff),
++                                ra->config->dnssl.string);
++    }
+ 
+     uint64_t ofpacts_stub[4096 / 8];
+     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -231,6 +231,17 @@ struct nd_rdnss_opt {
+ };
+ BUILD_ASSERT_DECL(ND_RDNSS_OPT_LEN == sizeof(struct nd_rdnss_opt));
+ 
++/* DNSSL option RFC 6106 */
++#define ND_OPT_DNSSL        31
++#define ND_DNSSL_OPT_LEN    8
++struct ovs_nd_dnssl {
++    u_int8_t type;  /* ND_OPT_DNSSL */
++    u_int8_t len;   /* >= 2 */
++    ovs_be16 reserved;
++    ovs_16aligned_be32 lifetime;
++};
++BUILD_ASSERT_DECL(ND_DNSSL_OPT_LEN == sizeof(struct ovs_nd_dnssl));
++
+ #define DHCPV6_DUID_LL      3
+ #define DHCPV6_HW_TYPE_ETH  1
+ 
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6477,6 +6477,10 @@ copy_ra_to_sb(struct ovn_port *op, const
+     if (rdnss) {
+         smap_add(&options, "ipv6_ra_rdnss", rdnss);
+     }
++    const char *dnssl = smap_get(&op->nbrp->ipv6_ra_configs, "dnssl");
++    if (dnssl) {
++        smap_add(&options, "ipv6_ra_dnssl", dnssl);
++    }
+ 
+     smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s);
+ 
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -1890,6 +1890,11 @@
+         IPv6 address of RDNSS server announced in RA packets. At the moment
+         OVN supports just one RDNSS server.
+       </column>
++
++      <column name="ipv6_ra_configs" key="dnssl">
++        DNS Search List announced in RA packets. Multiple DNS Search List
++        must be 'comma' separated (e.g. "a.b.c, d.e.f")
++      </column>
+     </group>
+ 
+     <group title="Options">
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -12522,14 +12522,15 @@ construct_expected_ra() {
+     local mtu=$1
+     local ra_mo=$2
+     local rdnss=$3
+-    local ra_prefix_la=$4
++    local dnssl=$4
++    local ra_prefix_la=$5
+ 
+     local slla=0101${src_mac}
+     local mtu_opt=""
+     if test $mtu != 0; then
+         mtu_opt=05010000${mtu}
+     fi
+-    shift 4
++    shift 5
+ 
+     local prefix=""
+     while [[ $# -gt 0 ]] ; do
+@@ -12543,8 +12544,12 @@ construct_expected_ra() {
+     if test $rdnss != 0; then
+         rdnss_opt=19030000ffffffff${rdnss}
+     fi
++    local dnssl_opt=""
++    if test $dnssl != 0; then
++        dnssl_opt=1f030000ffffffff${dnssl}
++    fi
+ 
+-    local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}
++    local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}${dnssl_opt}
+     local icmp=8600XXXX${ra}
+ 
+     local ip_len=$(expr ${#icmp} / 2)
+@@ -12579,28 +12584,33 @@ ra_test() {
+ }
+ 
+ # Baseline test with no MTU
+-ra_test 0 00 0 c0 40 aef00000000000000000000000000000
++ra_test 0 00 0 0 c0 40 aef00000000000000000000000000000
+ 
+ # Now make sure an MTU option makes it
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500
+-ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000
++ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000
+ 
+ # Now test for multiple network prefixes
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48'
+-ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # Now test for RDNSS
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11'
+ dns_addr=aef00000000000000000000000000011
+-ra_test 000005dc 00 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 00 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++
++# Now test for DNSSL
++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:dnssl="aa.bb.cc"
++dnssl=02616102626202636300000000000000
++ra_test 000005dc 00 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+-# Test a different address mode now
++## Test a different address mode now
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful
+-ra_test 000005dc 80 $dns_addr 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 80 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # And the other address mode
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless
+-ra_test 000005dc 40 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 40 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ OVN_CLEANUP([hv1],[hv2])
+ AT_CLEANUP
diff --git a/SOURCES/0001-Add-RDNSS-support-to-OVN.patch b/SOURCES/0001-Add-RDNSS-support-to-OVN.patch
new file mode 100644
index 0000000..cb385f4
--- /dev/null
+++ b/SOURCES/0001-Add-RDNSS-support-to-OVN.patch
@@ -0,0 +1,207 @@
+From fd8175f3e6b1d2b17521ab7bee3c86a6b5c20cc7 Mon Sep 17 00:00:00 2001
+Message-Id: <fd8175f3e6b1d2b17521ab7bee3c86a6b5c20cc7.1572424395.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Mon, 28 Oct 2019 15:37:39 +0100
+Subject: [PATCH ovn] Add RDNSS support to OVN
+
+Introduce the possibility to specify an RDNSS option to Router
+Advertisement packets. DNS IPv6 address can be specified using
+'rdnss' tag in the ipv6_ra_configs column of logical router
+port table
+
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/pinctrl.c | 39 +++++++++++++++++++++++++++++++++++++++
+ ovn/lib/ovn-l7.h         | 11 +++++++++++
+ ovn/northd/ovn-northd.c  |  5 +++++
+ ovn/ovn-nb.xml           |  5 +++++
+ tests/ovn.at         | 27 +++++++++++++++++++--------
+ 5 files changed, 79 insertions(+), 8 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2196,6 +2196,8 @@ struct ipv6_ra_config {
+     uint8_t mo_flags; /* Managed/Other flags for RAs */
+     uint8_t la_flags; /* On-link/autonomous flags for address prefixes */
+     struct lport_addresses prefixes;
++    struct in6_addr rdnss;
++    bool has_rdnss;
+ };
+ 
+ struct ipv6_ra_state {
+@@ -2293,6 +2295,12 @@ ipv6_ra_update_config(const struct sbrec
+         VLOG_WARN("Invalid IP source %s", ip_addr);
+         goto fail;
+     }
++    const char *rdnss = smap_get(&pb->options, "ipv6_ra_rdnss");
++    if (rdnss && !ipv6_parse(rdnss, &config->rdnss)) {
++        VLOG_WARN("Invalid RDNSS source %s", rdnss);
++        goto fail;
++    }
++    config->has_rdnss = !!rdnss;
+ 
+     return config;
+ 
+@@ -2323,6 +2331,33 @@ put_load(uint64_t value, enum mf_field_i
+     bitwise_one(ofpact_set_field_mask(sf), sf->field->n_bytes, ofs, n_bits);
+ }
+ 
++static void
++packet_put_ra_rdnss_opt(struct dp_packet *b, uint8_t num,
++                        ovs_be32 lifetime, const struct in6_addr *dns)
++{
++    size_t prev_l4_size = dp_packet_l4_size(b);
++    struct ip6_hdr *nh = dp_packet_l3(b);
++    size_t len = 2 * num + 1;
++
++    nh->ip6_plen = htons(prev_l4_size + len * 8);
++
++    struct nd_rdnss_opt *nd_rdnss = dp_packet_put_uninit(b, sizeof *nd_rdnss);
++    nd_rdnss->type = ND_OPT_RDNSS;
++    nd_rdnss->len = len;
++    nd_rdnss->reserved = 0;
++    put_16aligned_be32(&nd_rdnss->lifetime, lifetime);
++
++    for (int i = 0; i < num; i++) {
++        dp_packet_put(b, &dns[i], sizeof(ovs_be32[4]));
++    }
++
++    struct ovs_ra_msg *ra = dp_packet_l4(b);
++    ra->icmph.icmp6_cksum = 0;
++    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
++    ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra,
++                                                      prev_l4_size + len * 8));
++}
++
+ /* Called with in the pinctrl_handler thread context. */
+ static long long int
+ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra)
+@@ -2347,6 +2382,10 @@ ipv6_ra_send(struct rconn *swconn, struc
+             ra->config->la_flags, htonl(IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME),
+             htonl(IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME), addr);
+     }
++    if (ra->config->has_rdnss) {
++        packet_put_ra_rdnss_opt(&packet, 1, htonl(0xffffffff),
++                                &ra->config->rdnss);
++    }
+ 
+     uint64_t ofpacts_stub[4096 / 8];
+     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -220,6 +220,17 @@ struct dhcpv6_opt_ia_na {
+     ovs_be32 t2;
+ });
+ 
++/* RDNSS option RFC 6106 */
++#define ND_RDNSS_OPT_LEN    8
++#define ND_OPT_RDNSS        25
++struct nd_rdnss_opt {
++    uint8_t type;         /* ND_OPT_RDNSS. */
++    uint8_t len;          /* >= 3. */
++    ovs_be16 reserved;    /* Always 0. */
++    ovs_16aligned_be32 lifetime;
++};
++BUILD_ASSERT_DECL(ND_RDNSS_OPT_LEN == sizeof(struct nd_rdnss_opt));
++
+ #define DHCPV6_DUID_LL      3
+ #define DHCPV6_HW_TYPE_ETH  1
+ 
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6473,6 +6473,11 @@ copy_ra_to_sb(struct ovn_port *op, const
+     smap_add(&options, "ipv6_ra_prefixes", ds_cstr(&s));
+     ds_destroy(&s);
+ 
++    const char *rdnss = smap_get(&op->nbrp->ipv6_ra_configs, "rdnss");
++    if (rdnss) {
++        smap_add(&options, "ipv6_ra_rdnss", rdnss);
++    }
++
+     smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s);
+ 
+     sbrec_port_binding_set_options(op->sb, &options);
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -1885,6 +1885,11 @@
+         is one-third of <ref column="ipv6_ra_configs" key="max_interval"/>,
+         i.e. 200 seconds if that key is unset.
+       </column>
++
++      <column name="ipv6_ra_configs" key="rdnss">
++        IPv6 address of RDNSS server announced in RA packets. At the moment
++        OVN supports just one RDNSS server.
++      </column>
+     </group>
+ 
+     <group title="Options">
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -12521,14 +12521,15 @@ construct_expected_ra() {
+ 
+     local mtu=$1
+     local ra_mo=$2
+-    local ra_prefix_la=$3
++    local rdnss=$3
++    local ra_prefix_la=$4
+ 
+     local slla=0101${src_mac}
+     local mtu_opt=""
+     if test $mtu != 0; then
+         mtu_opt=05010000${mtu}
+     fi
+-    shift 3
++    shift 4
+ 
+     local prefix=""
+     while [[ $# -gt 0 ]] ; do
+@@ -12538,7 +12539,12 @@ construct_expected_ra() {
+         shift 2
+     done
+ 
+-    local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}
++    local rdnss_opt=""
++    if test $rdnss != 0; then
++        rdnss_opt=19030000ffffffff${rdnss}
++    fi
++
++    local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}
+     local icmp=8600XXXX${ra}
+ 
+     local ip_len=$(expr ${#icmp} / 2)
+@@ -12573,23 +12579,28 @@ ra_test() {
+ }
+ 
+ # Baseline test with no MTU
+-ra_test 0 00 c0 40 aef00000000000000000000000000000
++ra_test 0 00 0 c0 40 aef00000000000000000000000000000
+ 
+ # Now make sure an MTU option makes it
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500
+-ra_test 000005dc 00 c0 40 aef00000000000000000000000000000
++ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000
+ 
+ # Now test for multiple network prefixes
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48'
+-ra_test 000005dc 00 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 00 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++
++# Now test for RDNSS
++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11'
++dns_addr=aef00000000000000000000000000011
++ra_test 000005dc 00 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # Test a different address mode now
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful
+-ra_test 000005dc 80 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 80 $dns_addr 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # And the other address mode
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless
+-ra_test 000005dc 40 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 40 $dns_addr c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ OVN_CLEANUP([hv1],[hv2])
+ AT_CLEANUP
diff --git a/SOURCES/0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch b/SOURCES/0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch
new file mode 100644
index 0000000..14c1025
--- /dev/null
+++ b/SOURCES/0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch
@@ -0,0 +1,270 @@
+From 9c04a4b62484895dc301875ad9da7c77c17a99c7 Mon Sep 17 00:00:00 2001
+Message-Id: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 24 Sep 2019 18:39:54 +0200
+Subject: [PATCH ovn 1/3] Add egress QoS mapping for non-tunnel interfaces
+
+Introduce add_localnet_egress_interface_mappings routine in order to collect as
+egress interfaces all ovs bridge interfaces marked with ovn-egress-iface
+in the external_ids column of ovs interface table.
+ovn-egress-iface is used to indicate to which localnet ports QoS egress
+shaping has to be applied.
+Refactor add_bridge_mappings routine
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+---
+ ovn/controller/binding.c        | 51 ++++++++++++++++++++++++-
+ ovn/controller/binding.h        |  4 ++
+ ovn/controller/ovn-controller.c |  3 +-
+ ovn/controller/patch.c          | 76 +++++++++++++++++++++----------------
+ ovn/controller/patch.h          |  4 ++
+ 5 files changed, 103 insertions(+), 35 deletions(-)
+
+--- a/ovn/controller/binding.c
++++ b/ovn/controller/binding.c
+@@ -18,6 +18,7 @@
+ #include "ha-chassis.h"
+ #include "lflow.h"
+ #include "lport.h"
++#include "patch.h"
+ 
+ #include "lib/bitmap.h"
+ #include "openvswitch/poll-loop.h"
+@@ -521,6 +522,9 @@ consider_local_datapath(struct ovsdb_idl
+         /* Add all localnet ports to local_lports so that we allocate ct zones
+          * for them. */
+         sset_add(local_lports, binding_rec->logical_port);
++        if (qos_map && ovs_idl_txn) {
++            get_qos_params(binding_rec, qos_map);
++        }
+     } else if (!strcmp(binding_rec->type, "external")) {
+         if (ha_chassis_group_contains(binding_rec->ha_chassis_group,
+                                       chassis_rec)) {
+@@ -609,9 +613,47 @@ consider_local_datapath(struct ovsdb_idl
+ }
+ 
+ static void
++add_localnet_egress_interface_mappings(
++        const struct sbrec_port_binding *port_binding,
++        struct shash *bridge_mappings, struct sset *egress_ifaces)
++{
++    const char *network = smap_get(&port_binding->options, "network_name");
++    if (!network) {
++        return;
++    }
++
++    struct ovsrec_bridge *br_ln = shash_find_data(bridge_mappings, network);
++    if (!br_ln) {
++        return;
++    }
++
++    /* Add egress-ifaces from the connected bridge */
++    for (size_t i = 0; i < br_ln->n_ports; i++) {
++        const struct ovsrec_port *port_rec = br_ln->ports[i];
++
++        for (size_t j = 0; j < port_rec->n_interfaces; j++) {
++            const struct ovsrec_interface *iface_rec;
++
++            iface_rec = port_rec->interfaces[j];
++            bool is_egress_iface = smap_get_bool(&iface_rec->external_ids,
++                                                 "ovn-egress-iface", false);
++            if (!is_egress_iface) {
++                continue;
++            }
++            sset_add(egress_ifaces, iface_rec->name);
++        }
++    }
++}
++
++static void
+ consider_localnet_port(const struct sbrec_port_binding *binding_rec,
++                       struct shash *bridge_mappings,
++                       struct sset *egress_ifaces,
+                        struct hmap *local_datapaths)
+ {
++    add_localnet_egress_interface_mappings(binding_rec,
++            bridge_mappings, egress_ifaces);
++
+     struct local_datapath *ld
+         = get_local_datapath(local_datapaths,
+                              binding_rec->datapath->tunnel_key);
+@@ -644,6 +686,8 @@ binding_run(struct ovsdb_idl_txn *ovnsb_
+             const struct ovsrec_bridge *br_int,
+             const struct sbrec_chassis *chassis_rec,
+             const struct sset *active_tunnels,
++            const struct ovsrec_bridge_table *bridge_table,
++            const struct ovsrec_open_vswitch_table *ovs_table,
+             struct hmap *local_datapaths, struct sset *local_lports,
+             struct sset *local_lport_ids)
+ {
+@@ -652,6 +696,7 @@ binding_run(struct ovsdb_idl_txn *ovnsb_
+     }
+ 
+     const struct sbrec_port_binding *binding_rec;
++    struct shash bridge_mappings = SHASH_INITIALIZER(&bridge_mappings);
+     struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
+     struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
+     struct hmap qos_map;
+@@ -677,14 +722,18 @@ binding_run(struct ovsdb_idl_txn *ovnsb_
+ 
+     }
+ 
++    add_ovs_bridge_mappings(ovs_table, bridge_table, &bridge_mappings);
++
+     /* 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);
++            consider_localnet_port(binding_rec, &bridge_mappings,
++                                   &egress_ifaces, local_datapaths);
+         }
+     }
++    shash_destroy(&bridge_mappings);
+ 
+     if (!sset_is_empty(&egress_ifaces)
+         && set_noop_qos(ovs_idl_txn, port_table, qos_table, &egress_ifaces)) {
+--- a/ovn/controller/binding.h
++++ b/ovn/controller/binding.h
+@@ -26,6 +26,8 @@ struct ovsdb_idl_txn;
+ struct ovsrec_bridge;
+ struct ovsrec_port_table;
+ struct ovsrec_qos_table;
++struct ovsrec_bridge_table;
++struct ovsrec_open_vswitch_table;
+ struct sbrec_chassis;
+ struct sbrec_port_binding_table;
+ struct sset;
+@@ -42,6 +44,8 @@ void binding_run(struct ovsdb_idl_txn *o
+                  const struct ovsrec_bridge *br_int,
+                  const struct sbrec_chassis *,
+                  const struct sset *active_tunnels,
++                 const struct ovsrec_bridge_table *bridge_table,
++                 const struct ovsrec_open_vswitch_table *ovs_table,
+                  struct hmap *local_datapaths,
+                  struct sset *local_lports, struct sset *local_lport_ids);
+ bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -1045,7 +1045,8 @@ en_runtime_data_run(struct engine_node *
+                 sbrec_port_binding_by_name,
+                 port_table, qos_table, pb_table,
+                 br_int, chassis,
+-                active_tunnels, local_datapaths,
++                active_tunnels, bridge_table,
++                ovs_table, local_datapaths,
+                 local_lports, local_lport_ids);
+ 
+     update_ct_zones(local_lports, local_datapaths, ct_zones,
+--- a/ovn/controller/patch.c
++++ b/ovn/controller/patch.c
+@@ -129,6 +129,48 @@ remove_port(const struct ovsrec_bridge_t
+     }
+ }
+ 
++void
++add_ovs_bridge_mappings(const struct ovsrec_open_vswitch_table *ovs_table,
++                        const struct ovsrec_bridge_table *bridge_table,
++                        struct shash *bridge_mappings)
++{
++    const struct ovsrec_open_vswitch *cfg;
++
++    cfg = ovsrec_open_vswitch_table_first(ovs_table);
++    if (cfg) {
++        const char *mappings_cfg;
++        char *cur, *next, *start;
++
++        mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
++        if (!mappings_cfg || !mappings_cfg[0]) {
++            return;
++        }
++
++        next = start = xstrdup(mappings_cfg);
++        while ((cur = strsep(&next, ",")) && *cur) {
++            const struct ovsrec_bridge *ovs_bridge;
++            char *network, *bridge = cur;
++
++            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);
++    }
++}
++
+ /* 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'. */
+@@ -142,41 +184,9 @@ add_bridge_mappings(struct ovsdb_idl_txn
+                     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);
++    add_ovs_bridge_mappings(ovs_table, bridge_table, &bridge_mappings);
+ 
+     const struct sbrec_port_binding *binding;
+     SBREC_PORT_BINDING_TABLE_FOR_EACH (binding, port_binding_table) {
+--- a/ovn/controller/patch.h
++++ b/ovn/controller/patch.h
+@@ -30,7 +30,11 @@ struct ovsrec_open_vswitch_table;
+ struct ovsrec_port_table;
+ struct sbrec_port_binding_table;
+ struct sbrec_chassis;
++struct shash;
+ 
++void add_ovs_bridge_mappings(const struct ovsrec_open_vswitch_table *ovs_table,
++                             const struct ovsrec_bridge_table *bridge_table,
++                             struct shash *bridge_mappings);
+ void patch_run(struct ovsdb_idl_txn *ovs_idl_txn,
+                const struct ovsrec_bridge_table *,
+                const struct ovsrec_open_vswitch_table *,
diff --git a/SOURCES/0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch b/SOURCES/0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch
new file mode 100644
index 0000000..279a123
--- /dev/null
+++ b/SOURCES/0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch
@@ -0,0 +1,166 @@
+From d7b024a24ee249c3ce6bf593499fdb6a79d78706 Mon Sep 17 00:00:00 2001
+Message-Id: <d7b024a24ee249c3ce6bf593499fdb6a79d78706.1575382494.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Thu, 28 Nov 2019 14:48:35 +0200
+Subject: [PATCH 1/2] Add support to Default Router Preference (PRF) - RFC 4191
+
+Introduce support for Default Router Preference (PRF) in IPv6 Router
+Advertisement according to RFC 4191
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/pinctrl.c | 20 +++++++++++++++++---
+ ovn/lib/ovn-l7.h         |  4 ++++
+ ovn/northd/ovn-northd.c  |  8 ++++++++
+ ovn/ovn-nb.xml           | 12 ++++++++++++
+ tests/ovn.at             | 13 +++++++++----
+ 5 files changed, 50 insertions(+), 7 deletions(-)
+
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index fa9c827c1..ea0551c21 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2267,10 +2267,10 @@ ipv6_ra_update_config(const struct sbrec_port_binding *pb)
+         goto fail;
+     }
+     if (!strcmp(address_mode, "dhcpv6_stateless")) {
+-        config->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
++        config->mo_flags |= IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
+         config->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS;
+     } else if (!strcmp(address_mode, "dhcpv6_stateful")) {
+-        config->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
++        config->mo_flags |= IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
+     } else if (!strcmp(address_mode, "slaac")) {
+         config->la_flags |= IPV6_ND_RA_OPT_PREFIX_AUTONOMOUS;
+     } else {
+@@ -2278,6 +2278,13 @@ ipv6_ra_update_config(const struct sbrec_port_binding *pb)
+         goto fail;
+     }
+ 
++    const char *prf = smap_get(&pb->options, "ipv6_ra_prf");
++    if (!strcmp(prf, "HIGH")) {
++        config->mo_flags |= IPV6_ND_RA_OPT_PRF_HIGH;
++    } else if (!strcmp(prf, "LOW")) {
++        config->mo_flags |= IPV6_ND_RA_OPT_PRF_LOW;
++    }
++
+     const char *prefixes = smap_get(&pb->options, "ipv6_ra_prefixes");
+     if (prefixes && !extract_ip_addresses(prefixes, &config->prefixes)) {
+         VLOG_WARN("Invalid IPv6 prefixes: %s", prefixes);
+@@ -2427,10 +2434,17 @@ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra)
+ 
+     uint64_t packet_stub[128 / 8];
+     struct dp_packet packet;
++    uint16_t router_lt = IPV6_ND_RA_LIFETIME;
++
++    if (!router_lt) {
++        /* Reset PRF to MEDIUM if router lifetime is not set */
++        ra->config->mo_flags &= ~IPV6_ND_RA_OPT_PRF_LOW;
++    }
++
+     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+     compose_nd_ra(&packet, ra->config->eth_src, ra->config->eth_dst,
+             &ra->config->ipv6_src, &ra->config->ipv6_dst,
+-            255, ra->config->mo_flags, htons(IPV6_ND_RA_LIFETIME), 0, 0,
++            255, ra->config->mo_flags, htons(router_lt), 0, 0,
+             ra->config->mtu);
+ 
+     for (int i = 0; i < ra->config->prefixes.n_ipv6_addrs; i++) {
+diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h
+index 5fc370bf5..fd898b0dc 100644
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -287,6 +287,10 @@ nd_ra_opts_destroy(struct hmap *nd_ra_opts)
+ #define IPV6_ND_RA_OPT_PREFIX_VALID_LIFETIME        0xffffffff
+ #define IPV6_ND_RA_OPT_PREFIX_PREFERRED_LIFETIME    0xffffffff
+ 
++#define IPV6_ND_RA_OPT_PRF_NORMAL                   0x00
++#define IPV6_ND_RA_OPT_PRF_HIGH                     0x08
++#define IPV6_ND_RA_OPT_PRF_LOW                      0x18
++
+ static inline void
+ nd_ra_opts_init(struct hmap *nd_ra_opts)
+ {
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 0e6f8f621..1bcf34369 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6755,6 +6755,14 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode)
+ 
+     smap_add(&options, "ipv6_ra_src_eth", op->lrp_networks.ea_s);
+ 
++    const char *prf = smap_get(&op->nbrp->ipv6_ra_configs,
++                               "router_preference");
++    if (!prf || (strcmp(prf, "HIGH") && strcmp(prf, "LOW"))) {
++        smap_add(&options, "ipv6_ra_prf", "MEDIUM");
++    } else {
++        smap_add(&options, "ipv6_ra_prf", prf);
++    }
++
+     sbrec_port_binding_set_options(op->sb, &options);
+     smap_destroy(&options);
+ }
+diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
+index 6f6e06777..16851dba1 100644
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -1859,6 +1859,18 @@
+         </ul>
+       </column>
+ 
++      <column name="ipv6_ra_configs" key="router_preference">
++        Default Router Preference (PRF) indicates whether to prefer this
++        router over other default routers (RFC 4191).
++        Possible values are:
++
++        <ul>
++          <li>HIGH: mapped to 0x01 in RA PRF field</li>
++          <li>MEDIUM: mapped to 0x00 in RA PRF field</li>
++          <li>LOW: mapped to 0x11 in RA PRF field</li>
++        </ul>
++      </column>
++
+       <column name="ipv6_ra_configs" key="mtu">
+         The recommended MTU for the link. Default is 0, which means no MTU
+         Option will be included in RA packet replied by ovn-controller.
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 33989e697..78524256f 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -11488,23 +11488,28 @@ ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48'
+ ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
++# Test PRF for default gw
++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="LOW"
++ra_test 000005dc 18 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++
+ # Now test for RDNSS
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11'
+ dns_addr=aef00000000000000000000000000011
+-ra_test 000005dc 00 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 18 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # Now test for DNSSL
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:dnssl="aa.bb.cc"
++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="HIGH"
+ dnssl=02616102626202636300000000000000
+-ra_test 000005dc 00 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 08 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ ## Test a different address mode now
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful
+-ra_test 000005dc 80 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 88 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # And the other address mode
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless
+-ra_test 000005dc 40 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 48 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ OVN_CLEANUP([hv1],[hv2])
+ AT_CLEANUP
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch b/SOURCES/0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch
new file mode 100644
index 0000000..460f9d0
--- /dev/null
+++ b/SOURCES/0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch
@@ -0,0 +1,402 @@
+From 1abcdb8d6995fac449e1a06dfd5a2b25ed888daa Mon Sep 17 00:00:00 2001
+From: Ihar Hrachyshka <ihrachys@redhat.com>
+Date: Thu, 5 Mar 2020 20:44:24 -0500
+Subject: [PATCH] Broadcast DHCPREPLY when BROADCAST flag is set
+
+As per RFC2131, section 4.1:
+   A server or relay agent sending or relaying a DHCP message directly
+   to a DHCP client (i.e., not to a relay agent specified in the
+   'giaddr' field) SHOULD examine the BROADCAST bit in the 'flags'
+   field.  If this bit is set to 1, the DHCP message SHOULD be sent as
+   an IP broadcast using an IP broadcast address (preferably 0xffffffff)
+   as the IP destination address and the link-layer broadcast address as
+   the link-layer destination address.
+
+This patch changes destination IP address to 255.255.255.255 when client
+set BROADCAST flag in their DHCPREQUEST. Note: the offered IP address is
+still part of the DHCP payload.
+
+While the new DHCP response is sent as a broadcast IP frame, it's
+handled locally, as any other DHCP reply by the native responder.
+Meaning, the reply is sent to the client port that initiated the DHCP
+session only.
+
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1801006
+
+Backport changes:
+  * ovs_srcdir -> top_srcdir in tests
+
+Signed-off-by: Ihar Hrachyshka <ihrachys@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+(cherry picked from commit 4f8045b3b5f2c3376f86f5edc4e3f7507c2b1148)
+---
+ ovn/controller/pinctrl.c    | 15 ++++++
+ ovn/lib/ovn-l7.h            |  2 +
+ ovn/northd/ovn-northd.8.xml |  5 +-
+ ovn/northd/ovn-northd.c     |  7 ++-
+ tests/ovn.at                | 98 +++++++++++++++++++++++++++----------
+ 5 files changed, 93 insertions(+), 34 deletions(-)
+
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 01a1bcbe0..2c7c2b217 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -972,6 +972,12 @@ pinctrl_handle_tcp_reset(struct rconn *swconn, const struct flow *ip_flow,
+     dp_packet_uninit(&packet);
+ }
+ 
++static bool
++is_dhcp_flags_broadcast(ovs_be16 flags)
++{
++    return flags & htons(DHCP_BROADCAST_FLAG);
++}
++
+ /* Called with in the pinctrl_handler thread context. */
+ static void
+ pinctrl_handle_put_dhcp_opts(
+@@ -1196,7 +1202,16 @@ pinctrl_handle_put_dhcp_opts(
+ 
+     udp->udp_len = htons(new_l4_size);
+ 
++    /* Send a broadcast IP frame when BROADCAST flag is set. */
+     struct ip_header *out_ip = dp_packet_l3(&pkt_out);
++    ovs_be32 ip_dst;
++    if (!is_dhcp_flags_broadcast(dhcp_data->flags)) {
++        ip_dst = *offer_ip;
++    } else {
++        ip_dst = htonl(0xffffffff);
++    }
++    put_16aligned_be32(&out_ip->ip_dst, ip_dst);
++
+     out_ip->ip_tot_len = htons(pkt_out.l4_ofs - pkt_out.l3_ofs + new_l4_size);
+     udp->udp_csum = 0;
+     /* Checksum needs to be initialized to zero. */
+diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h
+index ae6dbfdfb..c43218224 100644
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -32,6 +32,8 @@ struct gen_opts_map {
+     size_t code;
+ };
+ 
++#define DHCP_BROADCAST_FLAG 0x8000
++
+ #define DHCP_OPTION(NAME, CODE, TYPE) \
+     {.name = NAME, .code = CODE, .type = TYPE}
+ 
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 04be2e7c7..c09e5ff44 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -855,7 +855,6 @@ next;
+         <pre>
+ eth.dst = eth.src;
+ eth.src = <var>E</var>;
+-ip4.dst = <var>A</var>;
+ ip4.src = <var>S</var>;
+ udp.src = 67;
+ udp.dst = 68;
+@@ -866,8 +865,8 @@ output;
+ 
+         <p>
+           where <var>E</var> is the server MAC address and <var>S</var> is the
+-          server IPv4 address defined in the DHCPv4 options and <var>A</var> is
+-          the IPv4 address defined in the logical port's addresses column.
++          server IPv4 address defined in the DHCPv4 options. Note that
++          <code>ip4.dst</code> field is handled by <code>put_dhcp_opts</code>.
+         </p>
+ 
+         <p>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index b324fa8e0..abe5508c3 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -4180,10 +4180,9 @@ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
+     ds_put_cstr(options_action, "); next;");
+ 
+     ds_put_format(response_action, "eth.dst = eth.src; eth.src = %s; "
+-                  "ip4.dst = "IP_FMT"; ip4.src = %s; udp.src = 67; "
+-                  "udp.dst = 68; outport = inport; flags.loopback = 1; "
+-                  "output;",
+-                  server_mac, IP_ARGS(offer_ip), server_ip);
++                  "ip4.src = %s; udp.src = 67; udp.dst = 68; "
++                  "outport = inport; flags.loopback = 1; output;",
++                  server_mac, server_ip);
+ 
+     ds_put_format(ipv4_addr_match,
+                   "ip4.src == "IP_FMT" && ip4.dst == {%s, 255.255.255.255}",
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 3ce8514e5..dd94776e6 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -4567,10 +4567,11 @@ sleep 2
+ as hv1 ovs-vsctl show
+ 
+ # This shell function sends a DHCP request packet
+-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP REQUEST_IP ...
++# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST CIADDR OFFER_IP REQUEST_IP USE_IP ...
+ test_dhcp() {
+-    local inport=$1 src_mac=$2 dhcp_type=$3 ciaddr=$4 offer_ip=$5 request_ip=$6 use_ip=$7
+-    shift; shift; shift; shift; shift; shift; shift;
++    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 ciaddr=$5 offer_ip=$6 request_ip=$7 use_ip=$8
++    shift; shift; shift; shift; shift; shift; shift; shift;
++
+     if test $use_ip != 0; then
+         src_ip=$1
+         dst_ip=$2
+@@ -4579,6 +4580,7 @@ test_dhcp() {
+         src_ip=`ip_to_hex 0 0 0 0`
+         dst_ip=`ip_to_hex 255 255 255 255`
+     fi
++
+     if test $request_ip != 0; then
+         ip_len=0120
+         udp_len=010b
+@@ -4586,10 +4588,19 @@ test_dhcp() {
+         ip_len=011a
+         udp_len=0106
+     fi
++
++    if test $broadcast != 0; then
++        flags=8000
++        reply_dst_ip=`ip_to_hex 255 255 255 255`
++    else
++        flags=0000
++        reply_dst_ip=${offer_ip}
++    fi
++
+     local request=ffffffffffff${src_mac}08004510${ip_len}0000000080110000${src_ip}${dst_ip}
+     # udp header and dhcp header
+     request=${request}00440043${udp_len}0000
+-    request=${request}010106006359aa7600000000${ciaddr}000000000000000000000000${src_mac}
++    request=${request}010106006359aa760000${flags}${ciaddr}000000000000000000000000${src_mac}
+     # client hardware padding
+     request=${request}00000000000000000000
+     # server hostname
+@@ -4627,10 +4638,10 @@ test_dhcp() {
+         ip_len=$(printf "%x" $ip_len)
+         udp_len=$(printf "%x" $udp_len)
+         # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len
+-        local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
++        local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${reply_dst_ip}
+         # udp header and dhcp header.
+         # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
+-        reply=${reply}004300440${udp_len}0000020106006359aa7600000000${ciaddr}
++        reply=${reply}004300440${udp_len}0000020106006359aa760000${flags}${ciaddr}
+         # your ip address; 0 for NAK
+         if test $dhcp_reply_type = 06; then
+             reply=${reply}00000000
+@@ -4701,7 +4712,7 @@ server_ip=`ip_to_hex 10 0 0 1`
+ ciaddr=`ip_to_hex 0 0 0 0`
+ request_ip=0
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 1 f00000000001 01 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
++test_dhcp 1 f00000000001 01 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 1.
+ OVS_WAIT_UNTIL([test 1 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4727,7 +4738,7 @@ server_ip=`ip_to_hex 10 0 0 1`
+ ciaddr=`ip_to_hex 0 0 0 0`
+ request_ip=$offer_ip
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 05 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 2.
+ OVS_WAIT_UNTIL([test 2 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4751,7 +4762,7 @@ server_ip=`ip_to_hex 10 0 0 1`
+ ciaddr=`ip_to_hex 0 0 0 0`
+ request_ip=`ip_to_hex 10 0 0 7`
+ expected_dhcp_opts=""
+-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 06 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 3.
+ OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4775,7 +4786,7 @@ rm -f 2.expected
+ ciaddr=`ip_to_hex 0 0 0 0`
+ offer_ip=0
+ request_ip=0
+-test_dhcp 2 f00000000002 08 $ciaddr $offer_ip $request_ip 0 1 1
++test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 1
+ 
+ # NXT_RESUMEs should be 4.
+ OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4792,12 +4803,12 @@ rm -f 2.expected
+ # ls2-lp2 (vif4-tx.pcap) should receive the DHCPv4 request packet once.
+ 
+ ciaddr=`ip_to_hex 0 0 0 0`
+-test_dhcp 3 f00000000003 01 $ciaddr 0 0 4 0
++test_dhcp 3 f00000000003 01 0 $ciaddr 0 0 4 0
+ 
+ # Send DHCPv4 packet on ls2-lp2. "router" DHCPv4 option is not defined for
+ # this lport.
+ ciaddr=`ip_to_hex 0 0 0 0`
+-test_dhcp 4 f00000000004 01 $ciaddr 0 0 3 0
++test_dhcp 4 f00000000004 01 0 $ciaddr 0 0 3 0
+ 
+ # NXT_RESUMEs should be 4.
+ OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4814,7 +4825,7 @@ request_ip=0
+ src_ip=$offer_ip
+ dst_ip=$server_ip
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 5.
+ OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4840,7 +4851,7 @@ request_ip=0
+ src_ip=$offer_ip
+ dst_ip=`ip_to_hex 255 255 255 255`
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 6.
+ OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4866,7 +4877,7 @@ request_ip=0
+ src_ip=$offer_ip
+ dst_ip=`ip_to_hex 255 255 255 255`
+ expected_dhcp_opts=""
+-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 7.
+ OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4892,7 +4903,7 @@ request_ip=0
+ src_ip=$offer_ip
+ dst_ip=`ip_to_hex 255 255 255 255`
+ expected_dhcp_opts=""
+-test_dhcp 2 f00000000002 03 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 06 $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 8.
+ OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4914,7 +4925,7 @@ rm -f 2.expected
+ ciaddr=`ip_to_hex 0 0 0 0`
+ src_ip=`ip_to_hex 10 0 0 6`
+ dst_ip=`ip_to_hex 10 0 0 4`
+-test_dhcp 2 f00000000002 03 $ciaddr 0 0 1 $src_ip $dst_ip 1
++test_dhcp 2 f00000000002 03 0 $ciaddr 0 0 1 $src_ip $dst_ip 1
+ 
+ # NXT_RESUMEs should be 8.
+ OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4922,6 +4933,29 @@ OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ # vif1-tx.pcap should have received the DHCPv4 request packet
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [1.expected])
+ 
++reset_pcap_file hv1-vif1 hv1/vif1
++reset_pcap_file hv1-vif2 hv1/vif2
++rm -f 1.expected
++rm -f 2.expected
++
++# Send DHCPDISCOVER with BROADCAST flag on.
++offer_ip=`ip_to_hex 10 0 0 4`
++server_ip=`ip_to_hex 10 0 0 1`
++ciaddr=`ip_to_hex 0 0 0 0`
++request_ip=0
++expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
++test_dhcp 1 f00000000001 01 1 $ciaddr $offer_ip $request_ip 0 ff1000000001 $server_ip 02 $expected_dhcp_opts
++
++# NXT_RESUMEs should be 9.
++OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
++cat 1.expected | cut -c -48 > expout
++AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
++# Skipping the IPv4 checksum.
++cat 1.expected | cut -c 53- > expout
++AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
++
+ OVN_CLEANUP([hv1])
+ 
+ AT_CLEANUP
+@@ -12963,10 +12997,11 @@ as hv1
+ ovs-vsctl show
+ 
+ # This shell function sends a DHCP request packet
+-# test_dhcp INPORT SRC_MAC DHCP_TYPE OFFER_IP ...
++# test_dhcp INPORT SRC_MAC DHCP_TYPE BROADCAST OFFER_IP ...
+ test_dhcp() {
+-    local inport=$1 src_mac=$2 dhcp_type=$3 offer_ip=$4 use_ip=$5
+-    shift; shift; shift; shift; shift;
++    local inport=$1 src_mac=$2 dhcp_type=$3 broadcast=$4 offer_ip=$5 use_ip=$6
++    shift; shift; shift; shift; shift; shift;
++
+     if test $use_ip != 0; then
+         src_ip=$1
+         dst_ip=$2
+@@ -12975,10 +13010,19 @@ test_dhcp() {
+         src_ip=`ip_to_hex 0 0 0 0`
+         dst_ip=`ip_to_hex 255 255 255 255`
+     fi
++
++    if test $broadcast != 0; then
++        flags=8000
++        reply_dst_ip=`ip_to_hex 255 255 255 255`
++    else
++        flags=0000
++        reply_dst_ip=${offer_ip}
++    fi
++
+     local request=ffffffffffff${src_mac}0800451001100000000080110000${src_ip}${dst_ip}
+     # udp header and dhcp header
+     request=${request}0044004300fc0000
+-    request=${request}010106006359aa760000000000000000000000000000000000000000${src_mac}
++    request=${request}010106006359aa760000${flags}00000000000000000000000000000000${src_mac}
+     # client hardware padding
+     request=${request}00000000000000000000
+     # server hostname
+@@ -13002,10 +13046,10 @@ test_dhcp() {
+     ip_len=$(printf "%x" $ip_len)
+     udp_len=$(printf "%x" $udp_len)
+     # $ip_len var will be in 3 digits i.e 134. So adding a '0' before $ip_len
+-    local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${offer_ip}
++    local reply=${src_mac}${srv_mac}080045100${ip_len}000000008011XXXX${srv_ip}${reply_dst_ip}
+     # udp header and dhcp header.
+     # $udp_len var will be in 3 digits. So adding a '0' before $udp_len
+-    reply=${reply}004300440${udp_len}0000020106006359aa760000000000000000
++    reply=${reply}004300440${udp_len}0000020106006359aa760000${flags}00000000
+     # your ip address
+     reply=${reply}${offer_ip}
+     # next server ip address, relay agent ip address, client mac address
+@@ -13124,7 +13168,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
+ server_ip=`ip_to_hex 10 0 0 1`
+ server_mac=ff1000000001
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
+ $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 1 in hv1.
+@@ -13222,7 +13266,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
+ server_ip=`ip_to_hex 10 0 0 1`
+ server_mac=ff1000000001
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
+ $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 2 in hv1.
+@@ -13332,7 +13376,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
+ server_ip=`ip_to_hex 10 0 0 1`
+ server_mac=ff1000000001
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
+ $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 3 in hv1.
+@@ -13412,7 +13456,7 @@ offer_ip=`ip_to_hex 10 0 0 6`
+ server_ip=`ip_to_hex 10 0 0 1`
+ server_mac=ff1000000001
+ expected_dhcp_opts=330400000e100104ffffff0003040a00000136040a000001
+-test_dhcp 1 f00000000003 01 $offer_ip 0 $server_mac $server_ip \
++test_dhcp 1 f00000000003 01 0 $offer_ip 0 $server_mac $server_ip \
+ $expected_dhcp_opts
+ 
+ # NXT_RESUMEs should be 4 in hv1.
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch b/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch
new file mode 100644
index 0000000..c27f337
--- /dev/null
+++ b/SOURCES/0001-DNS-Make-DNS-lookups-case-insensitive.patch
@@ -0,0 +1,257 @@
+From afd10572ecbdd1d538ed4280f705ee79423b3c07 Mon Sep 17 00:00:00 2001
+Message-Id: <afd10572ecbdd1d538ed4280f705ee79423b3c07.1594300918.git.lorenzo.bianconi@redhat.com>
+From: Mark Michelson <mmichels@redhat.com>
+Date: Mon, 20 Apr 2020 09:25:09 -0400
+Subject: [PATCH] DNS: Make DNS lookups case insensitive.
+
+From RFC 1035 Section 2.3.3:
+
+"For all parts of the DNS that are part of the official protocol, all
+comparisons between character strings (e.g., labels, domain names, etc.)
+are done in a case-insensitive manner."
+
+OVN was using case-sensitive lookups and therefore was not complying.
+This change makes lookups case insensitive by storing lowercase record
+names in the southbound database and converting incoming query names to
+lowercase.
+
+Change-Id: I164898989a39c2eddac3f54396c5861ad8c18bb1
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1819069
+Reported-by: Jianlin Shi <jishi@redhat.com>
+Acked-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ ovn/controller/pinctrl.c |  7 ++++-
+ ovn/lib/ovn-util.c       | 15 ++++++++++
+ ovn/lib/ovn-util.h       |  5 ++++
+ ovn/northd/ovn-northd.c  | 15 +++++++++-
+ ovn/ovn-sb.xml           |  3 +-
+ tests/ovn.at             | 61 +++++++++++++++++++++++++++++-----------
+ 6 files changed, 87 insertions(+), 19 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -1807,7 +1807,12 @@ pinctrl_handle_dns_lookup(
+         struct dns_data *d = iter->data;
+         for (size_t i = 0; i < d->n_dps; i++) {
+             if (d->dps[i] == dp_key) {
+-                answer_ips = smap_get(&d->records, ds_cstr(&query_name));
++                /* DNS records in SBDB are stored in lowercase. Convert to
++                 * lowercase to perform case insensitive lookup
++                 */
++                char *query_name_lower = str_tolower(ds_cstr(&query_name));
++                answer_ips = smap_get(&d->records, query_name_lower);
++                free(query_name_lower);
+                 if (answer_ips) {
+                     break;
+                 }
+--- a/ovn/lib/ovn-util.c
++++ b/ovn/lib/ovn-util.c
+@@ -19,6 +19,7 @@
+ #include "openvswitch/ofp-parse.h"
+ #include "ovn/lib/ovn-nb-idl.h"
+ #include "ovn/lib/ovn-sb-idl.h"
++#include <ctype.h>
+ 
+ VLOG_DEFINE_THIS_MODULE(ovn_util);
+ 
+@@ -411,3 +412,17 @@ datapath_is_switch(const struct sbrec_da
+ {
+     return smap_get(&ldp->external_ids, "logical-switch") != NULL;
+ }
++
++char *
++str_tolower(const char *orig)
++{
++    char *copy = xmalloc(strlen(orig) + 1);
++    char *p = copy;
++
++    while (*orig) {
++        *p++ = tolower(*orig++);
++    }
++    *p = '\0';
++
++    return copy;
++}
+--- a/ovn/lib/ovn-util.h
++++ b/ovn/lib/ovn-util.h
+@@ -87,4 +87,9 @@ uint32_t ovn_logical_flow_hash(const str
+                                uint16_t priority,
+                                const char *match, const char *actions);
+ bool datapath_is_switch(const struct sbrec_datapath_binding *);
++
++/* Returns a lowercase copy of orig.
++ * Caller must free the returned string.
++ */
++char *str_tolower(const char *orig);
+ #endif
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -9767,7 +9767,20 @@ sync_dns_entries(struct northd_context *
+             dns_info->sb_dns,
+             (struct sbrec_datapath_binding **)dns_info->sbs,
+             dns_info->n_sbs);
+-        sbrec_dns_set_records(dns_info->sb_dns, &dns_info->nb_dns->records);
++
++        /* DNS lookups are case-insensitive. Convert records to lowercase so
++         * we can do consistent lookups when DNS requests arrive
++         */
++        struct smap lower_records = SMAP_INITIALIZER(&lower_records);
++        struct smap_node *node;
++        SMAP_FOR_EACH (node, &dns_info->nb_dns->records) {
++            smap_add_nocopy(&lower_records, xstrdup(node->key),
++                            str_tolower(node->value));
++        }
++
++        sbrec_dns_set_records(dns_info->sb_dns, &lower_records);
++
++        smap_destroy(&lower_records);
+         free(dns_info->sbs);
+         free(dns_info);
+     }
+--- a/ovn/ovn-sb.xml
++++ b/ovn/ovn-sb.xml
+@@ -3502,7 +3502,8 @@ tcp.flags = RST;
+     <column name="records">
+       Key-value pair of DNS records with <code>DNS query name</code> as the key
+       and a string of IP address(es) separated by comma or space as the
+-      value.
++      value. ovn-northd stores the DNS query name in all lowercase in order to
++      facilitate case-insensitive lookups.
+ 
+       <p><b>Example: </b> "vm1.ovn.org" = "10.0.0.4 aef0::4"</p>
+     </column>
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -8380,6 +8380,12 @@ set_dns_params() {
+         # IPv4 address - 10.0.0.4
+         expected_dns_answer=${query_name}00010001${ttl}00040a000004
+         ;;
++    VM1)
++        # VM1.OVN.ORG
++        query_name=03564d31034f564e034f524700
++        # IPv4 address - 10.0.0.4
++        expected_dns_answer=${query_name}00010001${ttl}00040a000004
++        ;;
+     vm2)
+         # vm2.ovn.org
+         query_name=03766d32036f766e036f726700
+@@ -8542,6 +8548,29 @@ reset_pcap_file hv1-vif2 hv1/vif2
+ rm -f 1.expected
+ rm -f 2.expected
+ 
++# Try vm1 again but an all-caps query name
++
++set_dns_params VM1
++src_ip=`ip_to_hex 10 0 0 6`
++dst_ip=`ip_to_hex 10 0 0 1`
++dns_reply=1
++test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
++
++# NXT_RESUMEs should be 3.
++OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
++cat 2.expected | cut -c -48 > expout
++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
++# Skipping the IPv4 checksum.
++cat 2.expected | cut -c 53- > expout
++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
++
++reset_pcap_file hv1-vif1 hv1/vif1
++reset_pcap_file hv1-vif2 hv1/vif2
++rm -f 1.expected
++rm -f 2.expected
++
+ # Clear the query name options for ls1-lp2
+ ovn-nbctl --wait=hv remove DNS $DNS1 records vm2.ovn.org
+ 
+@@ -8551,8 +8580,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=0
+ test_dns 1 f00000000001 f00000000002 $src_ip $dst_ip $dns_reply $dns_req_data
+ 
+-# NXT_RESUMEs should be 3.
+-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 4.
++OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
+ AT_CHECK([cat 1.packets], [0], [])
+@@ -8573,8 +8602,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=0
+ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
+ 
+-# NXT_RESUMEs should be 3 only.
+-OVS_WAIT_UNTIL([test 3 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 4 only.
++OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+ AT_CHECK([cat 2.packets], [0], [])
+@@ -8594,8 +8623,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=1
+ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+ 
+-# NXT_RESUMEs should be 4.
+-OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 5.
++OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+ cat 2.expected | cut -c -48 > expout
+@@ -8616,8 +8645,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=1
+ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+ 
+-# NXT_RESUMEs should be 5.
+-OVS_WAIT_UNTIL([test 5 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 6.
++OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+ cat 2.expected | cut -c -48 > expout
+@@ -8638,8 +8667,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=0
+ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
+ 
+-# NXT_RESUMEs should be 6.
+-OVS_WAIT_UNTIL([test 6 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 7.
++OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+ AT_CHECK([cat 2.packets], [0], [])
+@@ -8656,8 +8685,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=0
+ test_dns 2 f00000000002 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data
+ 
+-# NXT_RESUMEs should be 7.
+-OVS_WAIT_UNTIL([test 7 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 8.
++OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+ AT_CHECK([cat 2.packets], [0], [])
+@@ -8676,8 +8705,8 @@ dst_ip=`ip_to_hex 10 0 0 1`
+ dns_reply=1
+ test_dns 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+ 
+-# NXT_RESUMEs should be 8.
+-OVS_WAIT_UNTIL([test 8 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 9.
++OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
+ cat 1.expected | cut -c -48 > expout
+@@ -8698,8 +8727,8 @@ dst_ip=aef00000000000000000000000000001
+ dns_reply=1
+ test_dns6 1 f00000000001 f000000000f0 $src_ip $dst_ip $dns_reply $dns_req_data $dns_resp_data
+ 
+-# NXT_RESUMEs should be 9.
+-OVS_WAIT_UNTIL([test 9 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
++# NXT_RESUMEs should be 10
++OVS_WAIT_UNTIL([test 10 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+ 
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap > 1.packets
+ # Skipping the UDP checksum.
diff --git a/SOURCES/0001-Disable-conjunction-by-force-cross-product-for-all-t.patch b/SOURCES/0001-Disable-conjunction-by-force-cross-product-for-all-t.patch
new file mode 100644
index 0000000..0f42a02
--- /dev/null
+++ b/SOURCES/0001-Disable-conjunction-by-force-cross-product-for-all-t.patch
@@ -0,0 +1,1383 @@
+From 55f3558950476c557ac595a273db7f9b735f69b5 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <nusiddiq@redhat.com>
+Date: Fri, 27 Sep 2019 17:23:57 +0530
+Subject: [PATCH] Disable conjunction by force cross product for all the
+ fields.
+
+With this ovn-controller will not generate conjunction flows.
+There are issues with the conjunction flows generated by ovn-controller.
+Please see the commit 298701dbc996 for more information.
+
+Acked-by: Han Zhou <hzhou8@ebay.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+
+(cherry-picked from ovn commit 6f914327a55aaab11507db0911b08d695e17ce2a)
+---
+ ovn/TODO.rst   |   10 +
+ ovn/lib/expr.c |   20 +-
+ tests/ovn.at   | 1244 +++++++++++++++++++++++++++++++++++++++++++++---
+ 3 files changed, 1204 insertions(+), 70 deletions(-)
+
+diff --git a/ovn/TODO.rst b/ovn/TODO.rst
+index 33489174f..8fcdf0396 100644
+--- a/ovn/TODO.rst
++++ b/ovn/TODO.rst
+@@ -145,3 +145,13 @@ OVN To-do List
+   * Support FTP ALGs.
+ 
+   * Support reject action.
++
++* Conjunction: Conjunction is disabled in OVN. This needs to be revisisted
++  to enable conjunction again after addressing the issues related to it.
++  Like, if there are multiple ACLs with overlapping Conjunction matches,
++  conjunction flows are not added properly.
++  Eg. match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} &&
++      tcp.dst >= 800 && tcp.dst <= 900) actions=drop
++
++      match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} &&
++      tcp.dst >= 1000 && tcp.dst <= 2000) actions=allow
+diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
+index c0871e1e8..64ea0aafa 100644
+--- a/ovn/lib/expr.c
++++ b/ovn/lib/expr.c
+@@ -32,6 +32,24 @@
+ 
+ VLOG_DEFINE_THIS_MODULE(expr);
+ 
++/* Right now conjunction flows generated by ovn-controller
++ * has issues. If there are multiple flows with the same
++ * match for different conjunctions, ovn-controller doesn't
++ * handle it properly.
++ * Eg.
++ * match 1 - ip4.src == {IP1, IP2} && tcp.dst >=500 && tcp.src <=600
++ * action - drop
++ *
++ * match 2 - ip4.src == {IP1, IP2} && tcp.dst >=700 && tcp.src <=800
++ * action - allow.
++ *
++ * To handle this issue temporarily force crossproduct so that conjunction
++ * flows are not generated.
++ *
++ * Remove this once fixed.
++ * */
++static bool force_crossproduct = true;
++
+ static struct expr *parse_and_annotate(const char *s,
+                                        const struct shash *symtab,
+                                        struct ovs_list *nesting,
+@@ -2633,7 +2651,7 @@ expr_normalize_and(struct expr *expr)
+ 
+         ovs_assert(sub->type == EXPR_T_OR);
+         const struct expr_symbol *symbol = expr_get_unique_symbol(sub);
+-        if (!symbol || symbol->must_crossproduct) {
++        if (!symbol || force_crossproduct || symbol->must_crossproduct ) {
+             struct expr *or = expr_create_andor(EXPR_T_OR);
+             struct expr *k;
+ 
+diff --git a/tests/ovn.at b/tests/ovn.at
+index e4e70456b..3f7e06cf5 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -580,16 +580,14 @@ tcp,reg14=0x6,tp_dst=500
+ tcp,reg14=0x6,tp_dst=501
+ ])
+ AT_CHECK([expr_to_flow 'outport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.src == {400, 401} && tcp.dst == {500, 501}'], [0], [dnl
+-conj_id=1,tcp,reg15=0x5
+-conj_id=2,tcp,reg15=0x6
+-tcp,reg15=0x5,tp_dst=500: conjunction(1, 0/2)
+-tcp,reg15=0x5,tp_dst=501: conjunction(1, 0/2)
+-tcp,reg15=0x5,tp_src=400: conjunction(1, 1/2)
+-tcp,reg15=0x5,tp_src=401: conjunction(1, 1/2)
+-tcp,reg15=0x6,tp_dst=500: conjunction(2, 0/2)
+-tcp,reg15=0x6,tp_dst=501: conjunction(2, 0/2)
+-tcp,reg15=0x6,tp_src=400: conjunction(2, 1/2)
+-tcp,reg15=0x6,tp_src=401: conjunction(2, 1/2)
++tcp,reg15=0x5,tp_src=400,tp_dst=500
++tcp,reg15=0x5,tp_src=400,tp_dst=501
++tcp,reg15=0x5,tp_src=401,tp_dst=500
++tcp,reg15=0x5,tp_src=401,tp_dst=501
++tcp,reg15=0x6,tp_src=400,tp_dst=500
++tcp,reg15=0x6,tp_src=400,tp_dst=501
++tcp,reg15=0x6,tp_src=401,tp_dst=500
++tcp,reg15=0x6,tp_src=401,tp_dst=501
+ ])
+ AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
+ (no flows)
+@@ -711,22 +709,27 @@ reg15=0x11
+ ])
+ AT_CLEANUP
+ 
+-AT_SETUP([ovn -- converting expressions to flows -- conjunction])
+-AT_KEYWORDS([conjunction])
++AT_SETUP([ovn -- converting expressions to flows -- no conjunction])
++AT_KEYWORDS([no conjunction])
+ expr_to_flow () {
+     echo "$1" | ovstest test-ovn expr-to-flows | sort
+ }
+ 
++# conjunction is disabled in OVN until some of the issues
++# related to conjunction flows are fixed.
++# expr-to-flows should not generate any conjunction flows.
+ lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
+ ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3}"
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-conj_id=1,ip
+-ip,nw_dst=20.0.0.1: conjunction(1, 0/2)
+-ip,nw_dst=20.0.0.2: conjunction(1, 0/2)
+-ip,nw_dst=20.0.0.3: conjunction(1, 0/2)
+-ip,nw_src=10.0.0.1: conjunction(1, 1/2)
+-ip,nw_src=10.0.0.2: conjunction(1, 1/2)
+-ip,nw_src=10.0.0.3: conjunction(1, 1/2)
++ip,nw_src=10.0.0.1,nw_dst=20.0.0.1
++ip,nw_src=10.0.0.1,nw_dst=20.0.0.2
++ip,nw_src=10.0.0.1,nw_dst=20.0.0.3
++ip,nw_src=10.0.0.2,nw_dst=20.0.0.1
++ip,nw_src=10.0.0.2,nw_dst=20.0.0.2
++ip,nw_src=10.0.0.2,nw_dst=20.0.0.3
++ip,nw_src=10.0.0.3,nw_dst=20.0.0.1
++ip,nw_src=10.0.0.3,nw_dst=20.0.0.2
++ip,nw_src=10.0.0.3,nw_dst=20.0.0.3
+ ])
+ 
+ lflow="ip && (!ct.est || (ct.est && ct_label.blocked == 1))"
+@@ -740,12 +743,12 @@ ct_state=-est+trk,ipv6
+ lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
+ ip4.dst == {20.0.0.1, 20.0.0.2}"
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-conj_id=1,ip
+-ip,nw_dst=20.0.0.1: conjunction(1, 0/2)
+-ip,nw_dst=20.0.0.2: conjunction(1, 0/2)
+-ip,nw_src=10.0.0.1: conjunction(1, 1/2)
+-ip,nw_src=10.0.0.2: conjunction(1, 1/2)
+-ip,nw_src=10.0.0.3: conjunction(1, 1/2)
++ip,nw_src=10.0.0.1,nw_dst=20.0.0.1
++ip,nw_src=10.0.0.1,nw_dst=20.0.0.2
++ip,nw_src=10.0.0.2,nw_dst=20.0.0.1
++ip,nw_src=10.0.0.2,nw_dst=20.0.0.2
++ip,nw_src=10.0.0.3,nw_dst=20.0.0.1
++ip,nw_src=10.0.0.3,nw_dst=20.0.0.2
+ ])
+ 
+ lflow="ip4 && ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
+@@ -753,19 +756,60 @@ ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3} && \
+ tcp.dst >= 1000 && tcp.dst <= 1010"
+ 
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-conj_id=1,tcp
+-tcp,nw_dst=20.0.0.1: conjunction(1, 0/3)
+-tcp,nw_dst=20.0.0.2: conjunction(1, 0/3)
+-tcp,nw_dst=20.0.0.3: conjunction(1, 0/3)
+-tcp,nw_src=10.0.0.1: conjunction(1, 1/3)
+-tcp,nw_src=10.0.0.2: conjunction(1, 1/3)
+-tcp,nw_src=10.0.0.3: conjunction(1, 1/3)
+-tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/3)
+-tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/3)
+-tcp,tp_dst=0x3f0/0xfffe: conjunction(1, 2/3)
+-tcp,tp_dst=1000: conjunction(1, 2/3)
+-tcp,tp_dst=1001: conjunction(1, 2/3)
+-tcp,tp_dst=1010: conjunction(1, 2/3)
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1000
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1001
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1010
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1000
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1001
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1010
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1000
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1001
++tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1010
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1000
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1001
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1010
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1000
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1001
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1010
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1000
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1001
++tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1010
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1000
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1001
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1010
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1000
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1001
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1010
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1000
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1001
++tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1010
+ ])
+ 
+ lflow="ip4 && ip4.src == {10.0.0.4, 10.0.0.5, 10.0.0.6} && \
+@@ -774,41 +818,1101 @@ tcp.dst <= 2000 && tcp.src >=1000 && tcp.src <= 2000) \
+ || ip4.dst == 20.0.0.5 || ip4.dst == 20.0.0.6)"
+ 
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-conj_id=1,tcp
+ ip,nw_src=10.0.0.4,nw_dst=20.0.0.5
+ ip,nw_src=10.0.0.4,nw_dst=20.0.0.6
+ ip,nw_src=10.0.0.5,nw_dst=20.0.0.5
+ ip,nw_src=10.0.0.5,nw_dst=20.0.0.6
+ ip,nw_src=10.0.0.6,nw_dst=20.0.0.5
+ ip,nw_src=10.0.0.6,nw_dst=20.0.0.6
+-tcp,nw_dst=20.0.0.4: conjunction(1, 0/4)
+-tcp,nw_dst=20.0.0.7: conjunction(1, 0/4)
+-tcp,nw_dst=20.0.0.8: conjunction(1, 0/4)
+-tcp,nw_src=10.0.0.4: conjunction(1, 1/4)
+-tcp,nw_src=10.0.0.5: conjunction(1, 1/4)
+-tcp,nw_src=10.0.0.6: conjunction(1, 1/4)
+-tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/4)
+-tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/4)
+-tcp,tp_dst=0x3f0/0xfff0: conjunction(1, 2/4)
+-tcp,tp_dst=0x400/0xfe00: conjunction(1, 2/4)
+-tcp,tp_dst=0x600/0xff00: conjunction(1, 2/4)
+-tcp,tp_dst=0x700/0xff80: conjunction(1, 2/4)
+-tcp,tp_dst=0x780/0xffc0: conjunction(1, 2/4)
+-tcp,tp_dst=0x7c0/0xfff0: conjunction(1, 2/4)
+-tcp,tp_dst=1000: conjunction(1, 2/4)
+-tcp,tp_dst=1001: conjunction(1, 2/4)
+-tcp,tp_dst=2000: conjunction(1, 2/4)
+-tcp,tp_src=0x3ea/0xfffe: conjunction(1, 3/4)
+-tcp,tp_src=0x3ec/0xfffc: conjunction(1, 3/4)
+-tcp,tp_src=0x3f0/0xfff0: conjunction(1, 3/4)
+-tcp,tp_src=0x400/0xfe00: conjunction(1, 3/4)
+-tcp,tp_src=0x600/0xff00: conjunction(1, 3/4)
+-tcp,tp_src=0x700/0xff80: conjunction(1, 3/4)
+-tcp,tp_src=0x780/0xffc0: conjunction(1, 3/4)
+-tcp,tp_src=0x7c0/0xfff0: conjunction(1, 3/4)
+-tcp,tp_src=1000: conjunction(1, 3/4)
+-tcp,tp_src=1001: conjunction(1, 3/4)
+-tcp,tp_src=2000: conjunction(1, 3/4)
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001
++tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000
+ ])
+ AT_CLEANUP
+ 
+@@ -12267,9 +13371,11 @@ AT_CHECK([cat 2.packets], [0], [expout])
+ # priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2)
+ # priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2)
+ 
+-OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \
++# conjunction is disabled in OVN until the issues related to it are
++# fixed.
++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \
+ grep conjunction | wc -l`])
+-OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \
++OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \
+ grep conj_id | wc -l`])
+ 
+ as hv1 ovs-ofctl dump-flows br-int
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch b/SOURCES/0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch
new file mode 100644
index 0000000..8c96d96
--- /dev/null
+++ b/SOURCES/0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch
@@ -0,0 +1,99 @@
+From 308df38edabc40221b9ce293e73ea4ac71eb965e Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Tue, 5 Nov 2019 23:11:56 +0530
+Subject: [PATCH ovn] Fix ha chassis failover issues for stale ha chassis
+ entries
+
+If ha chassis rows of an HA chassis group become stale i.e the HA_Chassis.chassis
+column is empty (because ovn-controller is not running in that chassis)
+except one row and when ha_chassis_group_is_active()
+is called on that ovn-controller, then it returns false. Ideally it should
+become active since its the only active chassis. This patch fixes this issue.
+
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1762777
+Reported-by: Daniel Alvarez <dalvarez@redhat.com>
+
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/ha-chassis.c | 25 +++++++++++++++++++++++++
+ tests/ovn.at                | 20 +++++++++++++++++++-
+ 2 files changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/ovn/controller/ha-chassis.c b/ovn/controller/ha-chassis.c
+index 6d9426a..d6ec7b6 100644
+--- a/ovn/controller/ha-chassis.c
++++ b/ovn/controller/ha-chassis.c
+@@ -142,6 +142,27 @@ ha_chassis_destroy_ordered(struct ha_chassis_ordered *ordered_ha_ch)
+     }
+ }
+ 
++/* Returns true if there is only one active ha chassis in the chassis group
++ * (i.e HA_Chassis.chassis column is set) and that active ha chassis is
++ * local chassis.
++ * Returns false otherwise. */
++static bool
++is_local_chassis_only_candidate(const struct sbrec_ha_chassis_group *ha_ch_grp,
++                                const struct sbrec_chassis *local_chassis)
++{
++    size_t n_active_ha_chassis = 0;
++    bool local_chassis_present = false;
++    for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
++        if (ha_ch_grp->ha_chassis[i]->chassis) {
++            n_active_ha_chassis++;
++            if (ha_ch_grp->ha_chassis[i]->chassis == local_chassis) {
++                local_chassis_present = true;
++            }
++        }
++    }
++
++    return (local_chassis_present && n_active_ha_chassis == 1);
++}
+ 
+ /* Returns true if the local_chassis is the master of
+  * the HA chassis group, false otherwise. */
+@@ -159,6 +180,10 @@ ha_chassis_group_is_active(
+         return (ha_ch_grp->ha_chassis[0]->chassis == local_chassis);
+     }
+ 
++    if (is_local_chassis_only_candidate(ha_ch_grp, local_chassis)) {
++        return true;
++    }
++
+     if (sset_is_empty(active_tunnels)) {
+         /* If active tunnel sset is empty, it means it has lost
+          * connectivity with other chassis. */
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 410f4b5..cb7903d 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -13413,7 +13413,25 @@ OVS_WAIT_UNTIL(
+ logical_port=ls1-lp_ext1`
+     test "$chassis" = "$hv1_uuid"])
+ 
+-OVN_CLEANUP([hv1],[hv2],[hv3])
++# Stop ovn-controllers on hv1 and hv3.
++as hv1 ovs-appctl -t ovn-controller exit
++as hv3 ovs-appctl -t ovn-controller exit
++
++# hv2 should be master and claim ls1-lp_ext1
++OVS_WAIT_UNTIL(
++    [chassis=`ovn-sbctl --bare --columns chassis find port_binding \
++logical_port=ls1-lp_ext1`
++    test "$chassis" = "$hv2_uuid"])
++
++as hv1
++OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as hv3
++OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++OVN_CLEANUP([hv2])
+ AT_CLEANUP
+ 
+ AT_SETUP([ovn -- Address Set Incremental Processing])
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch b/SOURCES/0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch
new file mode 100644
index 0000000..e0be69e
--- /dev/null
+++ b/SOURCES/0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch
@@ -0,0 +1,61 @@
+From 65d601cd755051fcf38b707b1283e29152aa0dcb Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Mon, 11 Nov 2019 12:26:40 +0530
+Subject: [PATCH] Fix testsuite 85 - "ensure one gw controller restart in HA
+ doesn't bounce the master ok".
+
+This testsuite is failing frequently in travis CI and locally when tests are run with "-j5".
+
+The test case deletes the chassis row for chassis 'gw2' and expects that this
+doesn't cause the chassisresident port on the master ('gw1') to not bounce as 'gw1'
+has higher priority than gw2. But since the ha chassis group has only 2 chassis,
+'gw2' can claim the port momentarily when 'gw2' chassis row is recreated until
+BFD session with 'gw1' is not established.
+
+This patch changes the test assertion approach and makes sure that 'gw1' is the
+owner of the chassisresident port eventually.
+
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Tested-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ tests/ovn.at | 20 +++++++++++++++++---
+ 1 file changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 3e429e347..5fc2de2d3 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -11033,12 +11033,26 @@ ovn-nbctl --wait=hv --timeout=3 sync
+ # doesn't have the same effect because "name" is conserved, and the
+ # Chassis entry is not replaced.
+ 
+-> gw1/ovn-controller.log
+-
+ gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
+ ovn-sbctl destroy Chassis $gw2_chassis
+ 
+-OVS_WAIT_UNTIL([test 0 = `grep -c "Releasing lport" gw1/ovn-controller.log`])
++# Wait for the gw2_chassis row is recreated.
++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns=_uuid find Chassis name=gw2 | wc -l`])
++
++gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1)
++gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
++
++# When gw2 chassis row is destroyed, it gets recreated. There
++# is a small window in which gw2 may claim the cr-outside port if
++# it has not established bfd tunnel with gw1.
++# So make sure that, cr-outside is claimed by gw1 finally.
++OVS_WAIT_WHILE(
++    [cr_outside_ch=`ovn-sbctl --bare --columns=chassis find Port_binding logical_port=cr-outside`
++     test $cr_outside_ch = $gw2_chassis])
++
++OVS_WAIT_UNTIL(
++    [cr_outside_ch=`ovn-sbctl --bare --columns=chassis find Port_binding logical_port=cr-outside`
++     test $cr_outside_ch = $gw1_chassis])
+ 
+ OVN_CLEANUP([gw1],[gw2],[hv1])
+ 
+-- 
+2.23.0
+
diff --git a/SOURCES/0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch b/SOURCES/0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch
new file mode 100644
index 0000000..90430b1
--- /dev/null
+++ b/SOURCES/0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch
@@ -0,0 +1,153 @@
+From d15bb6f025e14fc38415124b0794de40c40c69a2 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Wed, 16 Oct 2019 22:14:45 +0530
+Subject: [PATCH] Fix virtual port binding when the parents are scheduled in
+ the same chassis
+
+If a virtual port has 2 parents and if both of them are scheduled
+on the same chassis, then virtual port binding doesn't work.
+
+For virtual port binding we have the below logical flows:
+inport == p1 && !is_chassis_resident("vip-port") && arp .. actions=bind_vport(vip-port); next;
+inport == p2 && !is_chassis_resident("vip-port") && arp .. actions=bind_vport(vip-port); next;
+
+Since we have !is_chassis_resident, as soon as p1 binds the port, the corresponding OF flows
+for both the logical flows above are deleted. Because of this, when p2 becomes the
+parent of vip-port it can't bind it.
+
+This patch fixes this issue by removing this condition.
+
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1762341
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml |  2 +-
+ ovn/northd/ovn-northd.c     |  3 +--
+ tests/ovn.at                | 39 ++++++++++++++++++++++++++++++-------
+ 3 files changed, 34 insertions(+), 10 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 38e6ecec5..ad84a5783 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -580,7 +580,7 @@
+           column with the match
+         </p>
+         <pre>
+-<code>inport == <var>P</var> &amp;&amp; !is_chassis_resident(<var>V</var>) &amp;&amp; ((arp.op == 1 &amp;&amp; arp.spa == <var>VIP</var> &amp;&amp; arp.tpa == <var>VIP</var>) || (arp.op == 2 &amp;&amp; arp.spa == <var>VIP</var>))</code>
++<code>inport == <var>P</var> &amp;&amp; &amp;&amp; ((arp.op == 1 &amp;&amp; arp.spa == <var>VIP</var> &amp;&amp; arp.tpa == <var>VIP</var>) || (arp.op == 2 &amp;&amp; arp.spa == <var>VIP</var>))</code>
+         </pre>
+ 
+         <p>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 84b2a9ff1..c506f22f4 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5255,11 +5255,10 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                 ds_clear(&match);
+                 ds_put_format(&match, "inport == \"%s\" && "
+-                              "!is_chassis_resident(%s) && "
+                               "((arp.op == 1 && arp.spa == %s && "
+                               "arp.tpa == %s) || (arp.op == 2 && "
+                               "arp.spa == %s))",
+-                              vparent, op->json_key, virtual_ip, virtual_ip,
++                              vparent, virtual_ip, virtual_ip,
+                               virtual_ip);
+                 ds_clear(&actions);
+                 ds_put_format(&actions,
+diff --git a/tests/ovn.at b/tests/ovn.at
+index e38d14cdf..dad6c2d39 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14503,7 +14503,7 @@ ovn-nbctl lsp-set-addresses sw0-vir "50:54:00:00:00:10 10.0.0.10"
+ ovn-nbctl lsp-set-port-security sw0-vir "50:54:00:00:00:10 10.0.0.10"
+ ovn-nbctl lsp-set-type sw0-vir virtual
+ ovn-nbctl set logical_switch_port sw0-vir options:virtual-ip=10.0.0.10
+-ovn-nbctl set logical_switch_port sw0-vir options:virtual-parents=sw0-p1,sw0-p2
++ovn-nbctl set logical_switch_port sw0-vir options:virtual-parents=sw0-p1,sw0-p2,sw0-p3
+ 
+ ovn-nbctl lsp-add sw0 sw0-p1
+ ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
+@@ -14515,7 +14515,7 @@ ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4 10.0.0.10"
+ 
+ ovn-nbctl lsp-add sw0 sw0-p3
+ ovn-nbctl lsp-set-addresses sw0-p3 "50:54:00:00:00:05 10.0.0.5"
+-ovn-nbctl lsp-set-port-security sw0-p3 "50:54:00:00:00:05 10.0.0.5"
++ovn-nbctl lsp-set-port-security sw0-p3 "50:54:00:00:00:05 10.0.0.5 10.0.0.10"
+ 
+ # Create the second logical switch with one port
+ ovn-nbctl ls-add sw1
+@@ -14546,8 +14546,9 @@ ovn-nbctl --wait=hv sync
+ ovn-sbctl dump-flows sw0 | grep ls_in_arp_rsp | grep bind_vport > lflows.txt
+ 
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p1" && !is_chassis_resident("sw0-vir") && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
+-  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p2" && !is_chassis_resident("sw0-vir") && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
++  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p1" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
++  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p2" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
++  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p3" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
+ ])
+ 
+ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+@@ -14595,6 +14596,29 @@ AT_CHECK([cat lflows.txt], [0], [dnl
+   table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
+ ])
+ 
++# From sw0-p3 send GARP for 10.0.0.10. hv1 should claim sw0-vir
++# and sw0-p3 should be its virtual_parent.
++eth_src=505400000005
++eth_dst=ffffffffffff
++spa=$(ip_to_hex 10 0 0 10)
++tpa=$(ip_to_hex 10 0 0 10)
++send_garp 1 2 $eth_src $eth_dst $spa $tpa
++
++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
++logical_port=sw0-vir) = x$hv1_ch_uuid], [0], [])
++
++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \
++logical_port=sw0-vir) = xsw0-p3])
++
++ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
++> lflows.txt
++
++# There should be an arp resolve flow to resolve the virtual_ip with the
++# sw0-p2's MAC.
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:05; next;)
++])
++
+ # send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir
+ # and sw0-p2 shpuld be its virtual_parent.
+ eth_src=505400000004
+@@ -14613,7 +14637,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ > lflows.txt
+ 
+ # There should be an arp resolve flow to resolve the virtual_ip with the
+-# sw0-p2's MAC.
++# sw0-p3's MAC.
+ AT_CHECK([cat lflows.txt], [0], [dnl
+   table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
+ ])
+@@ -14658,7 +14682,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl
+ ])
+ 
+ # Now send arp reply from sw0-p2. hv2 should claim sw0-vir
+-# and sw0-p2 shpuld be its virtual_parent.
++# and sw0-p2 should be its virtual_parent.
+ eth_src=505400000004
+ eth_dst=ffffffffffff
+ spa=$(ip_to_hex 10 0 0 10)
+@@ -14701,7 +14725,8 @@ ovn-nbctl --wait=hv set logical_switch_port sw0-vir options:virtual-ip=10.0.0.10
+ ovn-sbctl dump-flows sw0 | grep ls_in_arp_rsp | grep bind_vport > lflows.txt
+ 
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p1" && !is_chassis_resident("sw0-vir") && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
++  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p1" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
++  table=11(ls_in_arp_rsp      ), priority=100  , match=(inport == "sw0-p3" && ((arp.op == 1 && arp.spa == 10.0.0.10 && arp.tpa == 10.0.0.10) || (arp.op == 2 && arp.spa == 10.0.0.10))), action=(bind_vport("sw0-vir", inport); next;)
+ ])
+ 
+ ovn-nbctl --wait=hv remove logical_switch_port sw0-vir options virtual-parents
+-- 
+2.23.0
+
diff --git a/SOURCES/0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch b/SOURCES/0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch
new file mode 100644
index 0000000..dc256e0
--- /dev/null
+++ b/SOURCES/0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch
@@ -0,0 +1,146 @@
+From 3c6763fdbc1c8b2d12d52c8fe28914ba55cdef6a Mon Sep 17 00:00:00 2001
+From: Numan Siddique <nusiddiq@redhat.com>
+Date: Sun, 1 Sep 2019 22:36:15 +0530
+Subject: [PATCH] Handle GARP reply packets from provider networks only on
+ gateway chassis
+
+Suppose there is a provider network (with localnet port) and it is
+connected to a logical router via a distributed gateway router port.
+When an external switch sends GARP request packets, these packets
+enter the br-int (via the patch port from the provider bridge) and
+the action - put_arp is applied in the router pipeline only on the
+gateway chassis where the distributed gateway router port is scheduled.
+Other chassis even if they receive don't apply this action. This functionality
+was added by the commit [1].
+
+But that is not the case with GARP reply packets sent by the external
+switch. ovn-controllers running on all the chassis configured with
+ovn-bridge-mappings receive these packets as packet-ins because
+of put_arp action. This results in unnecessary processing of these
+packets only to be ignored. pinctrl thread wakes up the main ovn-controller
+thread wasting few CPU cycles. It is enough for the gateway chassis to
+handle these packets where the distributed router gateway port is scheduled.
+
+This patch achieves the same - similar to GARP request packets. The below
+logical flows are added
+ - table=1 (lr_in_ip_input), priority=92,
+   match=(inport == "lrp" &&
+          !is_chassis_resident("cr-lrp") && eth.bcast &&
+          arp.op == 2 && arp.spa == PROVIDER_NETWORK_CIDR), action=(drop;)
+
+ - table=1 (lr_in_ip_input), priority=92
+   match=(inport == "lr0-public" && is_chassis_resident("cr-lr0-public")
+          && eth.bcast && arp.op == 2 && arp.spa == PROVIDER_NETWORK_CIDR),
+   action=(put_arp(inport, arp.spa, arp.sha);)
+
+This patch doesn't affect the arp replies from the overlay networks in the
+router pipeline. This only handles GARP replies from the provider networks.
+
+In the present master this is not much of any issue even if ovn-controller
+wakes up, thanks to incremental processing engine. But in the older
+versions - 2.11, 2.10 and 2.9, this results in complete flow calculations
+resulting in much more CPU cyles. This patch will definitely help in saving
+these CPU cyles if backported.
+
+[1] - 3dd9febe953f("ovn-northd: Support learning neighbor from ARP request.")
+
+(cherry-picked from ovn commit 2b4e4d7399f10a8cc9f6873c3820860f6f736ad3)
+CC: Han ZHou <hzhou8@ebay.com>
+Acked-by: Han ZHou <hzhou8@ebay.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/northd/ovn-northd.8.xml | 31 +++++++++++++++++++++++++++----
+ ovn/northd/ovn-northd.c     | 35 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 62 insertions(+), 4 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 6d2fbe349..ad98951f0 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1404,10 +1404,33 @@ arp.sha = <var>external_mac</var>;
+       </li>
+ 
+       <li>
+-        ARP reply handling.  This flow uses ARP replies to populate the
+-        logical router's ARP table.  A priority-90 flow with match <code>arp.op
+-        == 2</code> has actions <code>put_arp(inport, arp.spa,
+-        arp.sha);</code>.
++        <p>
++          ARP reply handling.  Following flows are added to handle ARP replies.
++        </p>
++
++        <p>
++          For each distributed gateway logical router port a priority-92 flow
++          with match <code>inport == <var>P</var> &amp;&amp;
++          is_chassis_resident(cr-<var>P</var>) &amp;&amp; eth.bcast &amp;&amp;
++          arp.op == 2 &amp;&amp; arp.spa == <var>I</var></code> with the
++          action <code>put_arp(inport, arp.spa, arp.sha);</code> so that the
++          resident gateway chassis can learn the GARP reply, where
++          <var>P</var> is the distributed gateway router port name,
++          <var>I</var> is the logical router port's network address.
++        </p>
++
++        <p>
++          For each distributed gateway logical router port a priority-92 flow
++          with match <code>inport == <var>P</var> &amp;&amp;
++          !is_chassis_resident(cr-<var>P</var>) &amp;&amp; eth.bcast &amp;&amp;
++          arp.op == 2 &amp;&amp; arp.spa == <var>I</var></code> with the action
++          <code>drop;</code> so that other chassis drop this packet.
++        </p>
++
++        <p>
++          A priority-90 flow with match <code>arp.op == 2</code> has actions
++          <code>put_arp(inport, arp.spa, arp.sha);</code>.
++        </p>
+       </li>
+ 
+       <li>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 6c6de2afd..20133ab41 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6398,6 +6398,41 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+         }
+ 
++        /* Handle GARP reply packets received on a distributed router gateway
++         * port. GARP reply broadcast packets could be sent by external
++         * switches. We don't want them to be handled by all the
++         * ovn-controllers if they receive it. So add a priority-92 flow to
++         * apply the put_arp action on a redirect chassis and drop it on
++         * other chassis.
++         * Note that we are already adding a priority-90 logical flow in the
++         * table S_ROUTER_IN_IP_INPUT to apply the put_arp action if
++         * arp.op == 2.
++         * */
++        if (op->od->l3dgw_port && op == op->od->l3dgw_port
++                && op->od->l3redirect_port) {
++            for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
++                ds_clear(&match);
++                ds_put_format(&match,
++                              "inport == %s && is_chassis_resident(%s) && "
++                              "eth.bcast && arp.op == 2 && arp.spa == %s/%u",
++                              op->json_key, op->od->l3redirect_port->json_key,
++                              op->lrp_networks.ipv4_addrs[i].network_s,
++                              op->lrp_networks.ipv4_addrs[i].plen);
++                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92,
++                              ds_cstr(&match),
++                              "put_arp(inport, arp.spa, arp.sha);");
++                ds_clear(&match);
++                ds_put_format(&match,
++                              "inport == %s && !is_chassis_resident(%s) && "
++                              "eth.bcast && arp.op == 2 && arp.spa == %s/%u",
++                              op->json_key, op->od->l3redirect_port->json_key,
++                              op->lrp_networks.ipv4_addrs[i].network_s,
++                              op->lrp_networks.ipv4_addrs[i].plen);
++                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92,
++                              ds_cstr(&match), "drop;");
++            }
++        }
++
+         /* A set to hold all load-balancer vips that need ARP responses. */
+         struct sset all_ips = SSET_INITIALIZER(&all_ips);
+         int addr_family;
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-Honour-router_preference-for-solicited-RA.patch b/SOURCES/0001-Honour-router_preference-for-solicited-RA.patch
new file mode 100644
index 0000000..dc3982a
--- /dev/null
+++ b/SOURCES/0001-Honour-router_preference-for-solicited-RA.patch
@@ -0,0 +1,198 @@
+From 114ca5e976d46bb7285c993879a2837146701386 Mon Sep 17 00:00:00 2001
+Message-Id: <114ca5e976d46bb7285c993879a2837146701386.1593090759.git.lorenzo.bianconi@redhat.com>
+From: Gabriele Cerami <gcerami@redhat.com>
+Date: Sat, 13 Jun 2020 10:20:23 +0100
+Subject: [PATCH] Honour router_preference for solicited RA
+
+Replies to router solicitation follow a different flow than periodic RA.
+This flow currently does not honour the router_preference configuration.
+
+This patch modifies the flow to honour the flag, and send
+router-preference indications in the reply RA following RFC4191
+specifications
+
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1804576
+Signed-off-by: Gabriele Cerami <gcerami@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ AUTHORS.rst             |  1 +
+ ovn/lib/actions.c       | 29 +++++++++++++++++++++++++++--
+ ovn/lib/ovn-l7.h        |  5 +++++
+ ovn/northd/ovn-northd.c |  6 ++++++
+ tests/ovn.at            | 26 ++++++++++++++++----------
+ 5 files changed, 55 insertions(+), 12 deletions(-)
+
+--- a/AUTHORS.rst
++++ b/AUTHORS.rst
+@@ -145,6 +145,7 @@ Frédéric Tobias Christ             fch
+ Frode Nordahl                      frode.nordahl@gmail.com
+ FUJITA Tomonori                    fujita.tomonori@lab.ntt.co.jp
+ Gabe Beged-Dov                     gabe@begeddov.com
++Gabriele Cerami                    gcerami@redhat.com
+ Gaetano Catalli                    gaetano.catalli@gmail.com
+ Gal Sagie                          gal.sagie@gmail.com
+ Genevieve LEsperance               glesperance@pivotal.io
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -2298,6 +2298,12 @@ parse_put_nd_ra_opts(struct action_conte
+             }
+             break;
+ 
++        case ND_RA_FLAG_PRF:
++            ok = (c->string && (!strcmp(c->string, "MEDIUM") ||
++                                !strcmp(c->string, "HIGH") ||
++                                !strcmp(c->string, "LOW")));
++            break;
++
+         case ND_OPT_SOURCE_LINKADDR:
+             ok = c->format == LEX_F_ETHERNET;
+             slla_present = true;
+@@ -2352,9 +2358,22 @@ encode_put_nd_ra_option(const struct ovn
+     {
+         struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra);
+         if (!strcmp(c->string, "dhcpv6_stateful")) {
+-            ra->mo_flags = IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
++            ra->mo_flags |= IPV6_ND_RA_FLAG_MANAGED_ADDR_CONFIG;
+         } else if (!strcmp(c->string, "dhcpv6_stateless")) {
+-            ra->mo_flags = IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
++            ra->mo_flags |= IPV6_ND_RA_FLAG_OTHER_ADDR_CONFIG;
++        }
++        break;
++    }
++
++    case ND_RA_FLAG_PRF:
++    {
++        struct ovs_ra_msg *ra = ofpbuf_at(ofpacts, ra_offset, sizeof *ra);
++        if (!strcmp(c->string, "LOW")) {
++            ra->mo_flags |= IPV6_ND_RA_OPT_PRF_LOW;
++        } else if (!strcmp(c->string, "HIGH")) {
++            ra->mo_flags |= IPV6_ND_RA_OPT_PRF_HIGH;
++        } else {
++            ra->mo_flags |= IPV6_ND_RA_OPT_PRF_NORMAL;
+         }
+         break;
+     }
+@@ -2435,6 +2454,12 @@ encode_PUT_ND_RA_OPTS(const struct ovnac
+         encode_put_nd_ra_option(o, ofpacts, ra_offset);
+     }
+ 
++    /* RFC4191 section 2.2 */
++    struct ovs_ra_msg *new_ra = ofpbuf_at(ofpacts, ra_offset, sizeof *new_ra);
++    if (ntohs(new_ra->router_lifetime) == 0) {
++        new_ra->mo_flags &= IPV6_ND_RA_OPT_PRF_RESET_MASK;
++    }
++
+     encode_finish_controller_op(oc_offset, ofpacts);
+ }
+ 
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -295,6 +295,9 @@ nd_ra_opts_destroy(struct hmap *nd_ra_op
+ 
+ 
+ #define ND_RA_FLAG_ADDR_MODE    0
++/* all small numbers seems to be all already taken but nothing guarantees this
++ * code will not be assigned by IANA to another option */
++#define ND_RA_FLAG_PRF          255
+ 
+ 
+ /* Default values of various IPv6 Neighbor Discovery protocol options and
+@@ -316,11 +319,13 @@ nd_ra_opts_destroy(struct hmap *nd_ra_op
+ #define IPV6_ND_RA_OPT_PRF_NORMAL                   0x00
+ #define IPV6_ND_RA_OPT_PRF_HIGH                     0x08
+ #define IPV6_ND_RA_OPT_PRF_LOW                      0x18
++#define IPV6_ND_RA_OPT_PRF_RESET_MASK               0xe7
+ 
+ static inline void
+ nd_ra_opts_init(struct hmap *nd_ra_opts)
+ {
+     nd_ra_opt_add(nd_ra_opts, "addr_mode", ND_RA_FLAG_ADDR_MODE, "str");
++    nd_ra_opt_add(nd_ra_opts, "router_preference", ND_RA_FLAG_PRF, "str");
+     nd_ra_opt_add(nd_ra_opts, "slla", ND_OPT_SOURCE_LINKADDR, "mac");
+     nd_ra_opt_add(nd_ra_opts, "prefix", ND_OPT_PREFIX_INFORMATION, "ipv6");
+     nd_ra_opt_add(nd_ra_opts, "mtu", ND_OPT_MTU, "uint32");
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -8551,6 +8551,12 @@ build_lrouter_flows(struct hmap *datapat
+             ds_put_format(&actions, ", mtu = %u", mtu);
+         }
+ 
++        const char *prf = smap_get_def(
++            &op->nbrp->ipv6_ra_configs, "router_preference", "MEDIUM");
++        if (strcmp(prf, "MEDIUM")) {
++            ds_put_format(&actions, ", router_preference = \"%s\"", prf);
++        }
++
+         bool add_rs_response_flow = false;
+ 
+         for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -1326,14 +1326,14 @@ log(severity=notice);
+     Syntax error at `;' expecting verdict.
+ 
+ # put_nd_ra_opts
+-reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64, slla = ae:01:02:03:04:05);
+-    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.00.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
++reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, router_preference = "HIGH", prefix = aef0::/64, slla = ae:01:02:03:04:05);
++    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.08.ff.ff.00.00.00.00.00.00.00.00.05.01.00.00.00.00.05.dc.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.05,pause)
+     has prereqs ip6
+-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", slla = ae:01:02:03:04:10, mtu = 1450);
++reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateful", router_preference = "MEDIUM", slla = ae:01:02:03:04:10, mtu = 1450);
+     encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.80.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.10.05.01.00.00.00.00.05.aa,pause)
+     has prereqs ip6
+-reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", slla = ae:01:02:03:04:06, prefix = aef0::/64);
+-    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.40.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause)
++reg1[0] = put_nd_ra_opts(addr_mode = "dhcpv6_stateless", router_preference = "LOW", slla = ae:01:02:03:04:06, prefix = aef0::/64);
++    encodes as controller(userdata=00.00.00.08.00.00.00.00.00.01.de.10.00.00.00.40.86.00.00.00.ff.58.ff.ff.00.00.00.00.00.00.00.00.01.01.ae.01.02.03.04.06.03.04.40.c0.ff.ff.ff.ff.ff.ff.ff.ff.00.00.00.00.ae.f0.00.00.00.00.00.00.00.00.00.00.00.00.00.00,pause)
+     has prereqs ip6
+ reg1[0] = put_nd_ra_opts(addr_mode = "slaac", mtu = 1500, prefix = aef0::/64);
+     slla option not present
+@@ -10075,13 +10075,16 @@ reset_pcap_file hv1-vif1 hv1/vif1
+ reset_pcap_file hv1-vif2 hv1/vif2
+ reset_pcap_file hv1-vif3 hv1/vif3
+ 
+-# Set the MTU to 1500
++# Set the MTU to 1500, send_periodic to false, preference to LOW
+ ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:mtu=1500
++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:send_periodic="false"
++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="LOW"
+ 
+ # Make sure that ovn-controller has installed the corresponding OF Flow.
+ OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
+ 
+-addr_mode=00
++# addr_mode byte also includes router preference information
++addr_mode=18
+ default_prefix_option_config=030440c0ffffffffffffffff00000000
+ src_mac=fa163e000003
+ src_lla=fe80000000000000f8163efffe000003
+@@ -10106,12 +10109,14 @@ reset_pcap_file hv1-vif1 hv1/vif1
+ reset_pcap_file hv1-vif2 hv1/vif2
+ reset_pcap_file hv1-vif3 hv1/vif3
+ 
+-# Set the address mode to dhcpv6_stateful
++# Set the address mode to dhcpv6_stateful, router_preference to HIGH
+ ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateful
++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="HIGH"
+ # Make sure that ovn-controller has installed the corresponding OF Flow.
+ OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
+ 
+-addr_mode=80
++# addr_mode byte also includes router preference information
++addr_mode=88
+ default_prefix_option_config=03044080ffffffffffffffff00000000
+ src_mac=fa163e000004
+ src_lla=fe80000000000000f8163efffe000004
+@@ -10136,8 +10141,9 @@ reset_pcap_file hv1-vif1 hv1/vif1
+ reset_pcap_file hv1-vif2 hv1/vif2
+ reset_pcap_file hv1-vif3 hv1/vif3
+ 
+-# Set the address mode to dhcpv6_stateless
++# Set the address mode to dhcpv6_stateless, reset router preference to default
+ ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:address_mode=dhcpv6_stateless
++ovn-nbctl --wait=hv set Logical_Router_Port lrp0 ipv6_ra_configs:router_preference="MEDIUM"
+ # Make sure that ovn-controller has installed the corresponding OF Flow.
+ OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int | grep -c "ipv6_dst=ff02::2,nw_ttl=255,icmp_type=133,icmp_code=0"`])
+ 
diff --git a/SOURCES/0001-Make-is_switch-in-lflow.c-a-util-function.patch b/SOURCES/0001-Make-is_switch-in-lflow.c-a-util-function.patch
new file mode 100644
index 0000000..be4358e
--- /dev/null
+++ b/SOURCES/0001-Make-is_switch-in-lflow.c-a-util-function.patch
@@ -0,0 +1,94 @@
+From 44a2c882067553a069c1b294644ebbda50d326c8 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Wed, 22 Jan 2020 17:58:23 +0530
+Subject: [PATCH 1/4] Make is_switch() in lflow.c a util function
+
+This patch renames is_switch() to datapath_is_switch() and moves to
+lib/ovn-util.c.
+
+Upcoming patch will make use of it.
+
+Acked-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream master commit 280429aaa380da43820961a552fe5e18e07578e5)
+
+Change-Id: I3091105a6cb56857ee2752749c5910f0c9aaa867
+---
+ ovn/controller/lflow.c | 11 ++---------
+ ovn/lib/ovn-util.c     |  6 ++++++
+ ovn/lib/ovn-util.h     |  3 ++-
+ 3 files changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
+index 9dbce91d1..b38a3f38a 100644
+--- a/ovn/controller/lflow.c
++++ b/ovn/controller/lflow.c
+@@ -128,13 +128,6 @@ is_chassis_resident_cb(const void *c_aux_, const char *port_name)
+     }
+ }
+ 
+-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)
+ {
+@@ -682,7 +675,7 @@ consider_logical_flow(
+     struct ovnact_encode_params ep = {
+         .lookup_port = lookup_port_cb,
+         .aux = &aux,
+-        .is_switch = is_switch(ldp),
++        .is_switch = datapath_is_switch(ldp),
+         .group_table = group_table,
+         .meter_table = meter_table,
+         .lflow_uuid = lflow->header_.uuid,
+@@ -706,7 +699,7 @@ consider_logical_flow(
+         if (m->match.wc.masks.conj_id) {
+             m->match.flow.conj_id += *conj_id_ofs;
+         }
+-        if (is_switch(ldp)) {
++        if (datapath_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];
+diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
+index 91ec3d62e..9b6efdfc6 100644
+--- a/ovn/lib/ovn-util.c
++++ b/ovn/lib/ovn-util.c
+@@ -405,3 +405,9 @@ ovn_logical_flow_hash(const struct uuid *logical_datapath,
+     hash = hash_string(match, hash);
+     return hash_string(actions, hash);
+ }
++
++bool
++datapath_is_switch(const struct sbrec_datapath_binding *ldp)
++{
++    return smap_get(&ldp->external_ids, "logical-switch") != NULL;
++}
+diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
+index 8461db505..f49f91862 100644
+--- a/ovn/lib/ovn-util.h
++++ b/ovn/lib/ovn-util.h
+@@ -23,6 +23,7 @@ struct sbrec_logical_flow;
+ struct uuid;
+ struct eth_addr;
+ struct sbrec_port_binding;
++struct sbrec_datapath_binding;
+ 
+ struct ipv4_netaddr {
+     ovs_be32 addr;            /* 192.168.10.123 */
+@@ -85,5 +86,5 @@ uint32_t ovn_logical_flow_hash(const struct uuid *logical_datapath,
+                                uint8_t table_id, const char *pipeline,
+                                uint16_t priority,
+                                const char *match, const char *actions);
+-
++bool datapath_is_switch(const struct sbrec_datapath_binding *);
+ #endif
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch b/SOURCES/0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch
new file mode 100644
index 0000000..11f3486
--- /dev/null
+++ b/SOURCES/0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch
@@ -0,0 +1,55 @@
+From e6975c1475c5de28f3b2d61a683472defefa0d9a Mon Sep 17 00:00:00 2001
+From: Michele Baldessari <michele@acksyn.org>
+Date: Wed, 14 Aug 2019 10:39:13 +0200
+Subject: [PATCH] Make pidfile_is_running more robust against empty pidfiles
+
+In some of our destructive testing of ovn-dbs inside containers managed
+by pacemaker we reached a situation where /var/run/openvswitch had
+empty .pid files. The current code does not deal well with them
+and pidfile_is_running() returns true in such a case and this confuses
+the OCF resource agent.
+
+- Before this change:
+Inside a container run:
+  killall ovsdb-server;
+  echo -n '' > /var/run/openvswitch/ovnnb_db.pid; echo -n '' > /var/run/openvswitch/ovnsb_db.pid
+
+We will observe that the cluster is unable to ever recover because
+it believes the ovn processes to be running when they really aren't and
+eventually just fails:
+ podman container set: ovn-dbs-bundle [192.168.24.1:8787/rhosp15/openstack-ovn-northd:pcmklatest]
+   ovn-dbs-bundle-0     (ocf::ovn:ovndb-servers):       Master controller-0
+   ovn-dbs-bundle-1     (ocf::ovn:ovndb-servers):       Stopped controller-1
+   ovn-dbs-bundle-2     (ocf::ovn:ovndb-servers):       Slave controller-2
+
+- After this change the cluster is able to recover from this state and
+correctly start the resource:
+ podman container set: ovn-dbs-bundle [192.168.24.1:8787/rhosp15/openstack-ovn-northd:pcmklatest]
+   ovn-dbs-bundle-0     (ocf::ovn:ovndb-servers):       Master controller-0
+   ovn-dbs-bundle-1     (ocf::ovn:ovndb-servers):       Slave controller-1
+   ovn-dbs-bundle-2     (ocf::ovn:ovndb-servers):       Slave controller-2
+
+Signed-off-by: Michele Baldessari <michele@acksyn.org>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+
+(cherry picked from ovn commit 94a13b76bc914a698e4756b70045fa9a371140d1)
+---
+ ovn/utilities/ovn-ctl | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ovn/utilities/ovn-ctl b/ovn/utilities/ovn-ctl
+index 7e5cd469c..65f03e28d 100755
+--- a/ovn/utilities/ovn-ctl
++++ b/ovn/utilities/ovn-ctl
+@@ -35,7 +35,7 @@ ovn_northd_db_conf_file="$etcdir/ovn-northd-db-params.conf"
+ 
+ pidfile_is_running () {
+     pidfile=$1
+-    test -e "$pidfile" && pid=`cat "$pidfile"` && pid_exists "$pid"
++    test -e "$pidfile" && [ -s "$pidfile" ] && pid=`cat "$pidfile"` && pid_exists "$pid"
+ } >/dev/null 2>&1
+ 
+ stop_nb_ovsdb() {
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch b/SOURCES/0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch
new file mode 100644
index 0000000..9c1cf03
--- /dev/null
+++ b/SOURCES/0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch
@@ -0,0 +1,517 @@
+From 7d45e142d6d1d964ff76b66db78d5e47267fe6b2 Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Sat, 17 Aug 2019 00:36:43 +0000
+Subject: [PATCH 01/12] OVN: Do not replace router port mac on gateway chassis.
+
+With 795d7f24ce0e2ed5454e193a059451d237289542 we have added
+support for E-W routing on vlan backed networks by replacing
+router port macs with chassis macs.
+
+This replacement of router port mac need NOT be done on
+gateway chassis for following reasons:
+
+a. For N-S traffic, gateway chassis will respond to ARP
+for the router port (to which it is attached) and
+traffic will be using router port mac as destination mac.
+
+b. Chassis redirect port is a centralized version of distributed
+router port, hence we need not replace its mac with chassis mac
+on the resident chassis.
+
+This patch addresses the same.
+
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+
+(cherry-picked from upstream ovn commit 26cdf840108495316f2b53b17b8d3810489e52cb)
+
+Change-Id: I2e0c712595209fb49ae6e5b90a6b0bf096509dc7
+---
+ ovn/controller/lport.c    |  20 +++
+ ovn/controller/lport.h    |   6 +
+ ovn/controller/physical.c |  18 ++-
+ ovn/controller/pinctrl.c  |  20 +--
+ tests/ovn.at              | 320 ++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 363 insertions(+), 21 deletions(-)
+
+diff --git a/ovn/controller/lport.c b/ovn/controller/lport.c
+index cc5c5fbb2..3cf54d16f 100644
+--- a/ovn/controller/lport.c
++++ b/ovn/controller/lport.c
+@@ -17,6 +17,7 @@
+ 
+ #include "lib/sset.h"
+ #include "lport.h"
++#include "ha-chassis.h"
+ #include "hash.h"
+ #include "openvswitch/vlog.h"
+ #include "ovn/lib/ovn-sb-idl.h"
+@@ -64,6 +65,25 @@ lport_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+     return retval;
+ }
+ 
++bool
++lport_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name,
++                          const struct sbrec_chassis *chassis,
++                          const struct sset *active_tunnels,
++                          const char *port_name)
++{
++    const struct sbrec_port_binding *pb
++        = lport_lookup_by_name(sbrec_port_binding_by_name, port_name);
++    if (!pb || !pb->chassis) {
++        return false;
++    }
++    if (strcmp(pb->type, "chassisredirect")) {
++        return pb->chassis == chassis;
++    } else {
++        return ha_chassis_group_is_active(pb->ha_chassis_group,
++                                          active_tunnels, chassis);
++    }
++}
++
+ const struct sbrec_datapath_binding *
+ datapath_lookup_by_key(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                        uint64_t dp_key)
+diff --git a/ovn/controller/lport.h b/ovn/controller/lport.h
+index 7dcd5bee0..89e3b85eb 100644
+--- a/ovn/controller/lport.h
++++ b/ovn/controller/lport.h
+@@ -23,6 +23,7 @@ struct sbrec_chassis;
+ struct sbrec_datapath_binding;
+ struct sbrec_multicast_group;
+ struct sbrec_port_binding;
++struct sset;
+ 
+ 
+ /* Database indexes.
+@@ -49,4 +50,9 @@ 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);
+ 
++bool
++lport_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name,
++                          const struct sbrec_chassis *chassis,
++                          const struct sset *active_tunnels,
++                          const char *port_name);
+ #endif /* ovn/lport.h */
+diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
+index 7ad3e6f8f..425110d0b 100644
+--- a/ovn/controller/physical.c
++++ b/ovn/controller/physical.c
+@@ -228,9 +228,12 @@ get_zone_ids(const struct sbrec_port_binding *binding,
+ }
+ 
+ static void
+-put_replace_router_port_mac_flows(const struct
++put_replace_router_port_mac_flows(struct ovsdb_idl_index
++                                  *sbrec_port_binding_by_name,
++                                  const struct
+                                   sbrec_port_binding *localnet_port,
+                                   const struct sbrec_chassis *chassis,
++                                  const struct sset *active_tunnels,
+                                   const struct hmap *local_datapaths,
+                                   struct ofpbuf *ofpacts_p,
+                                   ofp_port_t ofport,
+@@ -270,6 +273,16 @@ put_replace_router_port_mac_flows(const struct
+         struct eth_addr router_port_mac;
+         struct match match;
+         struct ofpact_mac *replace_mac;
++        char *cr_peer_name = xasprintf("cr-%s", rport_binding->logical_port);
++        if (lport_is_chassis_resident(sbrec_port_binding_by_name,
++                                      chassis, active_tunnels,
++                                      cr_peer_name)) {
++            /* If a router port's chassisredirect port is
++             * resident on this chassis, then we need not do mac replace. */
++            free(cr_peer_name);
++            continue;
++        }
++        free(cr_peer_name);
+ 
+         /* Table 65, priority 150.
+          * =======================
+@@ -787,7 +800,8 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                         &match, ofpacts_p, &binding->header_.uuid);
+ 
+         if (!strcmp(binding->type, "localnet")) {
+-            put_replace_router_port_mac_flows(binding, chassis,
++            put_replace_router_port_mac_flows(sbrec_port_binding_by_name,
++                                              binding, chassis, active_tunnels,
+                                               local_datapaths, ofpacts_p,
+                                               ofport, flow_table);
+         }
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 25b18789e..c6dd69fb3 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -3950,24 +3950,6 @@ get_localnet_vifs_l3gwports(
+     sbrec_port_binding_index_destroy_row(target);
+ }
+ 
+-static bool
+-pinctrl_is_chassis_resident(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+-                            const struct sbrec_chassis *chassis,
+-                            const struct sset *active_tunnels,
+-                            const char *port_name)
+-{
+-    const struct sbrec_port_binding *pb
+-        = lport_lookup_by_name(sbrec_port_binding_by_name, port_name);
+-    if (!pb || !pb->chassis) {
+-        return false;
+-    }
+-    if (strcmp(pb->type, "chassisredirect")) {
+-        return pb->chassis == chassis;
+-    } else {
+-        return ha_chassis_group_is_active(pb->ha_chassis_group,
+-                                          active_tunnels, chassis);
+-    }
+-}
+ 
+ /* Extracts the mac, IPv4 and IPv6 addresses, and logical port from
+  * 'addresses' which should be of the format 'MAC [IP1 IP2 ..]
+@@ -4048,7 +4030,7 @@ consider_nat_address(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+     char *lport = NULL;
+     if (!extract_addresses_with_port(nat_address, laddrs, &lport)
+         || (!lport && !strcmp(pb->type, "patch"))
+-        || (lport && !pinctrl_is_chassis_resident(
++        || (lport && !lport_is_chassis_resident(
+                 sbrec_port_binding_by_name, chassis,
+                 active_tunnels, lport))) {
+         destroy_lport_addresses(laddrs);
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 335ccd8c5..b7ee7beed 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -29,6 +29,22 @@ m4_define([OVN_CHECK_PACKETS],
+   [ovn_check_packets__ "$1" "$2"
+    AT_CHECK([sort $rcv_text], [0], [expout])])
+ 
++m4_define([OVN_CHECK_PACKETS_REMOVE_BROADCAST],
++  [ovn_check_packets__ () {
++   echo "checking packets in $1 against $2:"
++   rcv_pcap=$1
++   exp_text=$2
++   exp_n=`wc -l < "$exp_text"`
++   OVS_WAIT_UNTIL(
++     [$PYTHON "$top_srcdir/ovs/utilities/ovs-pcap.in" $rcv_pcap > $rcv_text
++      sed -i '/ffffffffffff/d' $rcv_text
++      rcv_n=`wc -l < "$rcv_text"`
++      echo "rcv_n=$rcv_n exp_n=$exp_n"
++      test $rcv_n -ge $exp_n])
++   sort $exp_text > expout
++ }
++])
++
+ AT_BANNER([OVN components])
+ 
+ AT_SETUP([ovn -- lexer])
+@@ -15976,3 +15992,307 @@ OVS_WAIT_UNTIL([
+ 
+ OVN_CLEANUP([hv1])
+ AT_CLEANUP
++
++
++AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S ARP handling])
++ovn_start
++
++# In this test cases we create 3 switches, all connected to same
++# physical network (through br-phys on each HV). LS1 and LS2 have
++# 1 VIF each. Each HV has 1 VIF port. The first digit
++# of VIF port name indicates the hypervisor it is bound to, e.g.
++# lp23 means VIF 3 on hv2.
++#
++# All the switches are connected to a logical router "router".
++#
++# Each switch's VLAN tag and their logical switch ports are:
++#   - ls1:
++#       - tagged with VLAN 101
++#       - ports: lp11
++#   - ls2:
++#       - tagged with VLAN 201
++#       - ports: lp22
++#   - ls-underlay:
++#       - tagged with VLAN 1000
++# Note: a localnet port is created for each switch to connect to
++# physical network.
++
++for i in 1 2; do
++    ls_name=ls$i
++    ovn-nbctl ls-add $ls_name
++    ln_port_name=ln$i
++    if test $i -eq 1; then
++        ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
++    elif test $i -eq 2; then
++        ovn-nbctl lsp-add $ls_name $ln_port_name "" 201
++    fi
++    ovn-nbctl lsp-set-addresses $ln_port_name unknown
++    ovn-nbctl lsp-set-type $ln_port_name localnet
++    ovn-nbctl lsp-set-options $ln_port_name network_name=phys
++done
++
++# lsp_to_ls LSP
++#
++# Prints the name of the logical switch that contains LSP.
++lsp_to_ls () {
++    case $1 in dnl (
++        lp?[[11]]) echo ls1 ;; dnl (
++        lp?[[12]]) echo ls2 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_hv () {
++    case $1 in dnl (
++        vif[[1]]?) echo hv1 ;; dnl (
++        vif[[2]]?) echo hv2 ;; dnl (
++        vif?[[north]]?) echo hv4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++ip_to_hex() {
++       printf "%02x%02x%02x%02x" "$@"
++}
++
++net_add n1
++for i in 1 2; do
++    sim_add hv$i
++    as hv$i
++    ovs-vsctl add-br br-phys
++    ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++    ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
++    ovn_attach n1 br-phys 192.168.0.$i
++
++    ovs-vsctl add-port br-int vif$i$i -- \
++        set Interface vif$i$i external-ids:iface-id=lp$i$i \
++                              options:tx_pcap=hv$i/vif$i$i-tx.pcap \
++                              options:rxq_pcap=hv$i/vif$i$i-rx.pcap \
++                              ofport-request=$i$i
++
++    lsp_name=lp$i$i
++    ls_name=$(lsp_to_ls $lsp_name)
++
++    ovn-nbctl lsp-add $ls_name $lsp_name
++    ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i"
++    ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i
++
++    OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
++
++done
++
++ovn-nbctl ls-add ls-underlay
++ovn-nbctl lsp-add ls-underlay ln3 "" 1000
++ovn-nbctl lsp-set-addresses ln3 unknown
++ovn-nbctl lsp-set-type ln3 localnet
++ovn-nbctl lsp-set-options ln3 network_name=phys
++
++ovn-nbctl ls-add ls-north
++ovn-nbctl lsp-add ls-north ln4 "" 1000
++ovn-nbctl lsp-set-addresses ln4 unknown
++ovn-nbctl lsp-set-type ln4 localnet
++ovn-nbctl lsp-set-options ln4 network_name=phys
++
++# Add a VM on ls-north
++ovn-nbctl lsp-add ls-north lp-north
++ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10"
++ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11
++
++# Add 3rd hypervisor
++sim_add hv3
++as hv3 ovs-vsctl add-br br-phys
++as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33"
++as hv3 ovn_attach n1 br-phys 192.168.0.3
++
++# Add 4th hypervisor
++sim_add hv4
++as hv4 ovs-vsctl add-br br-phys
++as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44"
++as hv4 ovn_attach n1 br-phys 192.168.0.4
++
++as hv4 ovs-vsctl add-port br-int vif-north -- \
++        set Interface vif-north external-ids:iface-id=lp-north \
++                              options:tx_pcap=hv4/vif-north-tx.pcap \
++                              options:rxq_pcap=hv4/vif-north-rx.pcap \
++                              ofport-request=44
++
++ovn-nbctl lr-add router
++ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24
++ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24
++ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24
++
++ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \
++          options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router
++ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \
++          options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router
++ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \
++                              underlay-to-router type=router \
++                              options:router-port=router-to-underlay \
++                              -- lsp-set-addresses underlay-to-router router
++
++
++OVN_POPULATE_ARP
++
++# lsp_to_ls LSP
++#
++# Prints the name of the logical switch that contains LSP.
++lsp_to_ls () {
++    case $1 in dnl (
++        lp?[[11]]) echo ls1 ;; dnl (
++        lp?[[12]]) echo ls2 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_ls () {
++    case $1 in dnl (
++        vif?[[11]]) echo ls1 ;; dnl (
++        vif?[[12]]) echo ls2 ;; dnl (
++        vif-north) echo ls-north ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++hv_to_num () {
++    case $1 in dnl (
++        hv1) echo 1 ;; dnl (
++        hv2) echo 2 ;; dnl (
++        hv3) echo 3 ;; dnl (
++        hv4) echo 4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_num () {
++    case $1 in dnl (
++        vif22) echo 22 ;; dnl (
++        vif21) echo 21 ;; dnl (
++        vif11) echo 11 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_hv () {
++    case $1 in dnl (
++        vif[[1]]?) echo hv1 ;; dnl (
++        vif[[2]]?) echo hv2 ;; dnl (
++        vif-north) echo hv4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_lrp () {
++    echo router-to-`vif_to_ls $1`
++}
++
++ip_to_hex() {
++       printf "%02x%02x%02x%02x" "$@"
++}
++
++# test_arp INPORT SHA SPA TPA [REPLY_HA]
++#
++# Causes a packet to be received on INPORT.  The packet is an ARP
++# request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
++# it should be the hardware address of the target to expect to receive in an
++# ARP reply; otherwise no reply is expected.
++#
++# INPORT is an logical switch port number, e.g. 11 for vif11.
++# SHA and REPLY_HA are each 12 hex digits.
++# SPA and TPA are each 8 hex digits.
++test_arp() {
++    local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
++    local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
++    hv=`vif_to_hv $inport`
++    as $hv ovs-appctl netdev-dummy/receive $inport $request
++
++    if test X$reply_ha = X; then
++        # Expect to receive the broadcast ARP on the other logical switch ports
++        # if no reply is expected.
++        local i j
++        for i in 1 2 3; do
++            for j in 1 2 3; do
++                if test $i$j != $inport; then
++                    echo $request >> $i$j.expected
++                fi
++            done
++        done
++    else
++        # Expect to receive the reply, if any.
++        local reply=${sha}${reply_ha}08060001080006040002${reply_ha}${tpa}${sha}${spa}
++        local reply_vid=${sha}${reply_ha}810003e808060001080006040002${reply_ha}${tpa}${sha}${spa}
++        echo $reply_vid >> ${inport}_vid.expected
++        echo $reply >> $inport.expected
++    fi
++}
++
++sip=`ip_to_hex 172 31 0 10`
++tip=`ip_to_hex 172 31 0 1`
++
++# Set a hypervisor as gateway chassis, for router port 172.31.0.1
++ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3
++ovn-nbctl --wait=sb sync
++
++# Dump a bunch of info helpful for debugging if there's a failure.
++
++echo "------ OVN dump ------"
++ovn-nbctl show
++ovn-sbctl show
++ovn-sbctl list port_binding
++ovn-sbctl list mac_binding
++
++echo "------ hv1 dump ------"
++as hv1 ovs-vsctl show
++as hv1 ovs-vsctl list Open_Vswitch
++
++echo "------ hv2 dump ------"
++as hv2 ovs-vsctl show
++as hv2 ovs-vsctl list Open_Vswitch
++
++echo "------ hv3 dump ------"
++as hv3 ovs-vsctl show
++as hv3 ovs-vsctl list Open_Vswitch
++
++echo "------ hv4 dump ------"
++as hv4 ovs-vsctl show
++as hv4 ovs-vsctl list Open_Vswitch
++
++OVS_WAIT_UNTIL([test x`ovn-sbctl --bare --columns chassis find port_binding  logical_port=cr-router-to-underlay | wc -l` = x1])
++
++test_arp vif-north f0f000000011 $sip $tip 000001010207
++
++# Confirm that vif-north gets a single ARP reply
++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected])
++
++# Confirm that only redirect chassis allowed arp resolution.
++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv3/br-phys_n1-tx.pcap], [vif-north_vid.expected])
++
++# Confirm that other OVN chassis did not generate ARP reply.
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/br-phys_n1-tx.pcap > hv1/br-phys_n1-tx.packets
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/br-phys_n1-tx.pcap > hv2/br-phys_n1-tx.packets
++
++AT_CHECK([grep 000001010207 hv1/br-phys_n1-tx.packets | wc -l], [0], [[0
++]])
++AT_CHECK([grep 000001010207 hv2/br-phys_n1-tx.packets | wc -l], [0], [[0
++]])
++
++echo "----------- Post Traffic hv1 dump -----------"
++as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
++as hv1 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv2 dump -----------"
++as hv2 ovs-ofctl -O OpenFlow13 dump-flows br-int
++as hv2 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv3 dump -----------"
++as hv3 ovs-ofctl -O OpenFlow13 dump-flows br-int
++as hv3 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv4 dump -----------"
++as hv4 ovs-ofctl -O OpenFlow13 dump-flows br-int
++as hv4 ovs-appctl fdb/show br-phys
++
++OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
++
++AT_CLEANUP
+-- 
+2.23.0
+
diff --git a/SOURCES/0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch b/SOURCES/0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch
new file mode 100644
index 0000000..296a80a
--- /dev/null
+++ b/SOURCES/0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch
@@ -0,0 +1,34 @@
+From 1a3e6dfb5e2fd5bbb625f637792f91a02767ff3b Mon Sep 17 00:00:00 2001
+From: Numan Siddique <nusiddiq@redhat.com>
+Date: Mon, 30 Sep 2019 18:45:20 +0530
+Subject: [PATCH] Partially revert "Exclude inport and outport symbol tables
+ from conjunction."
+
+This partially revers the commit - 298701dbc99645700be41680a43d049cb061847a
+as the commit [1] disables the conjunction.
+
+We still need the changes to the tests/ovn.at file.
+
+CC: Han Zhou <hzhou8@ebay.com>
+Acked-by: Han Zhou <hzhou8@ebay.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ lib/expr.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
+index 64ea0aafa..9b9b6bcca 100644
+--- a/ovn/lib/expr.c
++++ b/ovn/lib/expr.c
+@@ -1517,7 +1517,7 @@ expr_symtab_add_string(struct shash *symtab, const char *name,
+     const struct mf_field *field = mf_from_id(id);
+     struct expr_symbol *symbol;
+ 
+-    symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, true,
++    symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false,
+                         field->writable);
+     symbol->field = field;
+     return symbol;
+-- 
+2.23.0
+
diff --git a/SOURCES/0001-Prevent-erroneous-duplicate-IP-address-messages.patch b/SOURCES/0001-Prevent-erroneous-duplicate-IP-address-messages.patch
new file mode 100644
index 0000000..e83f975
--- /dev/null
+++ b/SOURCES/0001-Prevent-erroneous-duplicate-IP-address-messages.patch
@@ -0,0 +1,50 @@
+From 21c29d5b0ca10f36dc623866de74b8bc51def714 Mon Sep 17 00:00:00 2001
+From: Mark Michelson <mmichels@redhat.com>
+Date: Thu, 8 Aug 2019 17:00:05 -0400
+Subject: [PATCH] Prevent erroneous duplicate IP address messages.
+
+When using dynamic address assignment for logical switches, OVN reserves
+the first address in the subnet for the attached router port to use.
+
+In commit 488d153ee87841c042af05bc0eb8b5481aaa98cf, the IPAM code was
+modified to add assigned router port addresses to IPAM. The use case for
+this was when a switch was joined to multiple routers, and all router
+addresses were dynamically assigned.
+
+However, that commit also made it so that when a router rightly claimed
+the first address in the subnet, ovn-northd would issue a warning about
+a duplicate IP address being set. This change fixes the issue by adding
+a special case so that we don't add the router's IP address to IPAM if
+it is the first address in the subnet. This prevents the warning message
+from appearing.
+
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Numan Siddique <nusiddiq@redhat.com>
+Acked-by: Han ZHou <hzhou8@ebay.com>
+---
+ northd/ovn-northd.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 78246506c..4163b8e7f 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -1256,7 +1256,14 @@ ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op)
+ 
+         for (size_t i = 0; i < lrp_networks.n_ipv4_addrs; i++) {
+             uint32_t ip = ntohl(lrp_networks.ipv4_addrs[i].addr);
+-            ipam_insert_ip(op->peer->od, ip);
++            /* If the router has the first IP address of the subnet, don't add
++             * it to IPAM. We already added this when we initialized IPAM for
++             * the datapath. This will just result in an erroneous message
++             * about a duplicate IP address.
++             */
++            if (ip != op->peer->od->ipam_info.start_ipv4) {
++                ipam_insert_ip(op->peer->od, ip);
++            }
+         }
+ 
+         destroy_lport_addresses(&lrp_networks);
+-- 
+2.14.5
+
diff --git a/SOURCES/0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch b/SOURCES/0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch
new file mode 100644
index 0000000..23df75c
--- /dev/null
+++ b/SOURCES/0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch
@@ -0,0 +1,68 @@
+From 93ce545cbe5699814713913eff94a2b3dec7d7f7 Mon Sep 17 00:00:00 2001
+From: Timothy Redaelli <tredaelli@redhat.com>
+Date: Thu, 16 Jan 2020 15:25:13 +0100
+Subject: [PATCH] RHEL-only: Add compatibility with openvswitch2.11 < 2.11.0-43
+
+This is done by adding
+	EnvironmentFile=/etc/openvswitch/default.conf
+	EnvironmentFile=-/etc/sysconfig/openvswitch
+to the service files.
+
+Signed-off-by: Timothy Redaelli <tredaelli@redhat.com>
+---
+ rhel/usr_lib_systemd_system_ovn-controller-vtep.service | 4 +++-
+ rhel/usr_lib_systemd_system_ovn-controller.service      | 4 +++-
+ rhel/usr_lib_systemd_system_ovn-northd.service          | 4 +++-
+ 3 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/rhel/usr_lib_systemd_system_ovn-controller-vtep.service b/rhel/usr_lib_systemd_system_ovn-controller-vtep.service
+index b1e239f57..852cd1955 100644
+--- a/rhel/usr_lib_systemd_system_ovn-controller-vtep.service
++++ b/rhel/usr_lib_systemd_system_ovn-controller-vtep.service
+@@ -38,8 +38,10 @@ PIDFile=/var/run/openvswitch/ovn-controller-vtep.pid
+ Restart=on-failure
+ Environment=OVN_DB=unix:%t/openvswitch/ovnsb_db.sock
+ Environment=VTEP_DB=unix:%t/openvswitch/db.sock
++EnvironmentFile=/etc/openvswitch/default.conf
++EnvironmentFile=-/etc/sysconfig/openvswitch
+ EnvironmentFile=-/etc/sysconfig/ovn-controller-vtep
+-EnvironmentFile=/run/openvswitch.useropts
++EnvironmentFile=-/run/openvswitch.useropts
+ ExecStart=/usr/share/openvswitch/scripts/ovn-ctl \
+           --db-sb-sock=${OVN_DB} --db-sock=${VTEP_DB} \
+           --ovn-user=${OVS_USER_ID} \
+diff --git a/rhel/usr_lib_systemd_system_ovn-controller.service b/rhel/usr_lib_systemd_system_ovn-controller.service
+index 335cd5a52..f0df89331 100644
+--- a/rhel/usr_lib_systemd_system_ovn-controller.service
++++ b/rhel/usr_lib_systemd_system_ovn-controller.service
+@@ -23,8 +23,10 @@ After=openvswitch.service
+ Type=forking
+ PIDFile=/var/run/openvswitch/ovn-controller.pid
+ Restart=on-failure
++EnvironmentFile=/etc/openvswitch/default.conf
++EnvironmentFile=-/etc/sysconfig/openvswitch
+ EnvironmentFile=-/etc/sysconfig/ovn-controller
+-EnvironmentFile=/run/openvswitch.useropts
++EnvironmentFile=-/run/openvswitch.useropts
+ ExecStart=/usr/share/openvswitch/scripts/ovn-ctl --no-monitor \
+           --ovn-user=${OVS_USER_ID} \
+           start_controller $OVN_CONTROLLER_OPTS
+diff --git a/rhel/usr_lib_systemd_system_ovn-northd.service b/rhel/usr_lib_systemd_system_ovn-northd.service
+index ea8c191e3..82f3c4cfc 100644
+--- a/rhel/usr_lib_systemd_system_ovn-northd.service
++++ b/rhel/usr_lib_systemd_system_ovn-northd.service
+@@ -23,8 +23,10 @@ After=openvswitch.service
+ Type=oneshot
+ RemainAfterExit=yes
+ Environment=OVS_RUNDIR=%t/openvswitch OVS_DBDIR=/var/lib/openvswitch
++EnvironmentFile=/etc/openvswitch/default.conf
++EnvironmentFile=-/etc/sysconfig/openvswitch
+ EnvironmentFile=-/etc/sysconfig/ovn-northd
+-EnvironmentFile=/run/openvswitch.useropts
++EnvironmentFile=-/run/openvswitch.useropts
+ ExecStartPre=-/usr/bin/chown -R ${OVS_USER_ID} ${OVS_DBDIR}
+ ExecStart=/usr/share/openvswitch/scripts/ovn-ctl \
+           --ovs-user=${OVS_USER_ID} --ovn-user=${OVS_USER_ID} \
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch b/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch
new file mode 100644
index 0000000..0b3e1fb
--- /dev/null
+++ b/SOURCES/0001-Rely-on-unique-name-for-ovn-qos-meters.patch
@@ -0,0 +1,73 @@
+From 37affe59c85fa9be1cacf8a6f0681b6327ca36e0 Mon Sep 17 00:00:00 2001
+Message-Id: <37affe59c85fa9be1cacf8a6f0681b6327ca36e0.1594290284.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Mon, 27 Apr 2020 17:45:20 +0200
+Subject: [PATCH] Rely on unique name for ovn qos meters
+
+ovn currently identifies qos meters according to the rate and burst values
+configured. Doing so 2 meters on the same hv assigned to 2 different logical
+switch ports and configured with the same values for rate and burst will be
+mapped to the same ovs kernel mater and will share the bandwidth.
+Fix this behavior making qos meter name unique
+
+Tested-By: Maciej Jozefczyk <mjozefcz@redhat.com>
+Acked-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/ofctrl.c |  2 +-
+ ovn/lib/actions.c       | 11 ++++++-----
+ tests/ovn.at            | 10 ++++++++++
+ 3 files changed, 17 insertions(+), 6 deletions(-)
+
+--- a/ovn/controller/ofctrl.c
++++ b/ovn/controller/ofctrl.c
+@@ -969,7 +969,7 @@ add_meter_string(struct ovn_extend_table
+     enum ofputil_protocol usable_protocols;
+     char *meter_string = xasprintf("meter=%"PRIu32",%s",
+                                    m_desired->table_id,
+-                                   &m_desired->name[9]);
++                                   &m_desired->name[52]);
+     char *error = parse_ofp_meter_mod_str(&mm, meter_string, OFPMC13_ADD,
+                                           &usable_protocols);
+     if (!error) {
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -2670,12 +2670,13 @@ encode_SET_METER(const struct ovnact_set
+      * describes the meter itself. */
+     char *name;
+     if (cl->burst) {
+-        name = xasprintf("__string: kbps burst stats bands=type=drop "
+-                         "rate=%"PRId64" burst_size=%"PRId64"", cl->rate,
+-                         cl->burst);
++        name = xasprintf("__string: uuid "UUID_FMT" kbps burst stats "
++                         "bands=type=drop rate=%"PRId64" burst_size=%"PRId64,
++                         UUID_ARGS(&ep->lflow_uuid), cl->rate, cl->burst);
+     } else {
+-        name = xasprintf("__string: kbps stats bands=type=drop "
+-                         "rate=%"PRId64"", cl->rate);
++        name = xasprintf("__string: uuid "UUID_FMT" kbps stats "
++                         "bands=type=drop rate=%"PRId64,
++                         UUID_ARGS(&ep->lflow_uuid), cl->rate);
+     }
+ 
+     table_id = ovn_extend_table_assign_id(ep->meter_table, name,
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -7685,6 +7685,16 @@ AT_CHECK([as hv ovs-ofctl dump-flows br-
+ AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep rate=11123 | wc -l], [0], [0
+ ])
+ 
++# Check multiple qos meters
++ovn-nbctl qos-del lsw0
++ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp1" && is_chassis_resident("lp1")' rate=100000 burst=100000
++ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp2" && is_chassis_resident("lp2")' rate=100000 burst=100000
++ovn-nbctl qos-add lsw0 to-lport 1002 'inport=="lp1" && is_chassis_resident("lp1")' rate=100001 burst=100001
++ovn-nbctl qos-add lsw0 to-lport 1002 'inport=="lp2" && is_chassis_resident("lp2")' rate=100001 burst=100001
++
++AT_CHECK([as hv ovs-ofctl dump-meters br-int -O OpenFlow13 | grep meter | wc -l], [0], [4
++])
++
+ OVN_CLEANUP([hv])
+ AT_CLEANUP
+ 
diff --git a/SOURCES/0001-Revert-conjunctive-match-removal-patches.patch b/SOURCES/0001-Revert-conjunctive-match-removal-patches.patch
new file mode 100644
index 0000000..cc911b3
--- /dev/null
+++ b/SOURCES/0001-Revert-conjunctive-match-removal-patches.patch
@@ -0,0 +1,1383 @@
+From b9d4ba4f2fca05e1af38f091f755ba1d2429042e Mon Sep 17 00:00:00 2001
+From: Mark Michelson <mmichels@redhat.com>
+Date: Fri, 25 Oct 2019 09:06:46 -0400
+Subject: [PATCH 1/2] Revert conjunctive match removal patches.
+
+This partially reverts commits 6f914327a55aaab11507db0911b08d695e17ce2a
+and 298701dbc99645700be41680a43d049cb061847a. This restores the behavior
+of making conjunctive matches, and it restores the tests to their state.
+
+The one thing it does not revert is the addition of the
+force_cross_product variable in expr.c. This variable will be used in a
+later commit in this series. Instead of removing it, we just set it
+"false" to restore previous behavior.
+
+With this commit, the conjunctive match misbehavior described in commit
+298701dbc99645700be41680a43d049cb061847a is restored. However, this will
+be fixed in an upcoming commit in this series.
+
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/TODO.rst     |   10 -
+ ovn/lib/expr.c   |    9 +-
+ tests/ovn.at | 1250 +++-------------------------------------------------------
+ 3 files changed, 61 insertions(+), 1208 deletions(-)
+
+diff --git a/ovn/TODO.rst b/ovn/TODO.rst
+index ed55ea236..943d9bf81 100644
+--- a/ovn/TODO.rst
++++ b/ovn/TODO.rst
+@@ -145,13 +145,3 @@ OVN To-do List
+   * Support FTP ALGs.
+ 
+   * Support reject action.
+-
+-* Conjunction: Conjunction is disabled in OVN. This needs to be revisisted
+-  to enable conjunction again after addressing the issues related to it.
+-  Like, if there are multiple ACLs with overlapping Conjunction matches,
+-  conjunction flows are not added properly.
+-  Eg. match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} &&
+-      tcp.dst >= 800 && tcp.dst <= 900) actions=drop
+-
+-      match(ip4.src == {IP1, IP2, IP3} && ip4.dst == {IP4, IP5, IP6} &&
+-      tcp.dst >= 1000 && tcp.dst <= 2000) actions=allow
+diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
+index 9b9b6bcca..71de61543 100644
+--- a/ovn/lib/expr.c
++++ b/ovn/lib/expr.c
+@@ -42,13 +42,8 @@ VLOG_DEFINE_THIS_MODULE(expr);
+  *
+  * match 2 - ip4.src == {IP1, IP2} && tcp.dst >=700 && tcp.src <=800
+  * action - allow.
+- *
+- * To handle this issue temporarily force crossproduct so that conjunction
+- * flows are not generated.
+- *
+- * Remove this once fixed.
+- * */
+-static bool force_crossproduct = true;
++ */
++static bool force_crossproduct = false;
+ 
+ static struct expr *parse_and_annotate(const char *s,
+                                        const struct shash *symtab,
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 22b272a60..641a646fc 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -589,22 +589,6 @@ ip,reg14=0x6
+ ipv6,reg14=0x5
+ ipv6,reg14=0x6
+ ])
+-AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.dst == {500, 501}'], [0], [dnl
+-tcp,reg14=0x5,tp_dst=500
+-tcp,reg14=0x5,tp_dst=501
+-tcp,reg14=0x6,tp_dst=500
+-tcp,reg14=0x6,tp_dst=501
+-])
+-AT_CHECK([expr_to_flow 'outport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.src == {400, 401} && tcp.dst == {500, 501}'], [0], [dnl
+-tcp,reg15=0x5,tp_src=400,tp_dst=500
+-tcp,reg15=0x5,tp_src=400,tp_dst=501
+-tcp,reg15=0x5,tp_src=401,tp_dst=500
+-tcp,reg15=0x5,tp_src=401,tp_dst=501
+-tcp,reg15=0x6,tp_src=400,tp_dst=500
+-tcp,reg15=0x6,tp_src=400,tp_dst=501
+-tcp,reg15=0x6,tp_src=401,tp_dst=500
+-tcp,reg15=0x6,tp_src=401,tp_dst=501
+-])
+ AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
+ (no flows)
+ ])
+@@ -709,14 +693,6 @@ reg15=0x11
+ reg15=0x12
+ reg15=0x13
+ ])
+-AT_CHECK([expr_to_flow 'outport == @pg1 && ip4.src == {10.0.0.4, 10.0.0.5}'], [0], [dnl
+-ip,reg15=0x11,nw_src=10.0.0.4
+-ip,reg15=0x11,nw_src=10.0.0.5
+-ip,reg15=0x12,nw_src=10.0.0.4
+-ip,reg15=0x12,nw_src=10.0.0.5
+-ip,reg15=0x13,nw_src=10.0.0.4
+-ip,reg15=0x13,nw_src=10.0.0.5
+-])
+ AT_CHECK([expr_to_flow 'outport == {@pg_empty}'], [0], [dnl
+ (no flows)
+ ])
+@@ -725,27 +701,22 @@ reg15=0x11
+ ])
+ AT_CLEANUP
+ 
+-AT_SETUP([ovn -- converting expressions to flows -- no conjunction])
+-AT_KEYWORDS([no conjunction])
++AT_SETUP([ovn -- converting expressions to flows -- conjunction])
++AT_KEYWORDS([conjunction])
+ expr_to_flow () {
+     echo "$1" | ovstest test-ovn expr-to-flows | sort
+ }
+ 
+-# conjunction is disabled in OVN until some of the issues
+-# related to conjunction flows are fixed.
+-# expr-to-flows should not generate any conjunction flows.
+ lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
+ ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3}"
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-ip,nw_src=10.0.0.1,nw_dst=20.0.0.1
+-ip,nw_src=10.0.0.1,nw_dst=20.0.0.2
+-ip,nw_src=10.0.0.1,nw_dst=20.0.0.3
+-ip,nw_src=10.0.0.2,nw_dst=20.0.0.1
+-ip,nw_src=10.0.0.2,nw_dst=20.0.0.2
+-ip,nw_src=10.0.0.2,nw_dst=20.0.0.3
+-ip,nw_src=10.0.0.3,nw_dst=20.0.0.1
+-ip,nw_src=10.0.0.3,nw_dst=20.0.0.2
+-ip,nw_src=10.0.0.3,nw_dst=20.0.0.3
++conj_id=1,ip
++ip,nw_dst=20.0.0.1: conjunction(1, 0/2)
++ip,nw_dst=20.0.0.2: conjunction(1, 0/2)
++ip,nw_dst=20.0.0.3: conjunction(1, 0/2)
++ip,nw_src=10.0.0.1: conjunction(1, 1/2)
++ip,nw_src=10.0.0.2: conjunction(1, 1/2)
++ip,nw_src=10.0.0.3: conjunction(1, 1/2)
+ ])
+ 
+ lflow="ip && (!ct.est || (ct.est && ct_label.blocked == 1))"
+@@ -759,12 +730,12 @@ ct_state=-est+trk,ipv6
+ lflow="ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
+ ip4.dst == {20.0.0.1, 20.0.0.2}"
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-ip,nw_src=10.0.0.1,nw_dst=20.0.0.1
+-ip,nw_src=10.0.0.1,nw_dst=20.0.0.2
+-ip,nw_src=10.0.0.2,nw_dst=20.0.0.1
+-ip,nw_src=10.0.0.2,nw_dst=20.0.0.2
+-ip,nw_src=10.0.0.3,nw_dst=20.0.0.1
+-ip,nw_src=10.0.0.3,nw_dst=20.0.0.2
++conj_id=1,ip
++ip,nw_dst=20.0.0.1: conjunction(1, 0/2)
++ip,nw_dst=20.0.0.2: conjunction(1, 0/2)
++ip,nw_src=10.0.0.1: conjunction(1, 1/2)
++ip,nw_src=10.0.0.2: conjunction(1, 1/2)
++ip,nw_src=10.0.0.3: conjunction(1, 1/2)
+ ])
+ 
+ lflow="ip4 && ip4.src == {10.0.0.1, 10.0.0.2, 10.0.0.3} && \
+@@ -772,60 +743,19 @@ ip4.dst == {20.0.0.1, 20.0.0.2, 20.0.0.3} && \
+ tcp.dst >= 1000 && tcp.dst <= 1010"
+ 
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1000
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1001
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.1,tp_dst=1010
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1000
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1001
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.2,tp_dst=1010
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1000
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1001
+-tcp,nw_src=10.0.0.1,nw_dst=20.0.0.3,tp_dst=1010
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1000
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1001
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.1,tp_dst=1010
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1000
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1001
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.2,tp_dst=1010
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1000
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1001
+-tcp,nw_src=10.0.0.2,nw_dst=20.0.0.3,tp_dst=1010
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1000
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1001
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.1,tp_dst=1010
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1000
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1001
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.2,tp_dst=1010
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=0x3f0/0xfffe
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1000
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1001
+-tcp,nw_src=10.0.0.3,nw_dst=20.0.0.3,tp_dst=1010
++conj_id=1,tcp
++tcp,nw_dst=20.0.0.1: conjunction(1, 0/3)
++tcp,nw_dst=20.0.0.2: conjunction(1, 0/3)
++tcp,nw_dst=20.0.0.3: conjunction(1, 0/3)
++tcp,nw_src=10.0.0.1: conjunction(1, 1/3)
++tcp,nw_src=10.0.0.2: conjunction(1, 1/3)
++tcp,nw_src=10.0.0.3: conjunction(1, 1/3)
++tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/3)
++tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/3)
++tcp,tp_dst=0x3f0/0xfffe: conjunction(1, 2/3)
++tcp,tp_dst=1000: conjunction(1, 2/3)
++tcp,tp_dst=1001: conjunction(1, 2/3)
++tcp,tp_dst=1010: conjunction(1, 2/3)
+ ])
+ 
+ lflow="ip4 && ip4.src == {10.0.0.4, 10.0.0.5, 10.0.0.6} && \
+@@ -834,1101 +764,41 @@ tcp.dst <= 2000 && tcp.src >=1000 && tcp.src <= 2000) \
+ || ip4.dst == 20.0.0.5 || ip4.dst == 20.0.0.6)"
+ 
+ AT_CHECK([expr_to_flow "$lflow"], [0], [dnl
++conj_id=1,tcp
+ ip,nw_src=10.0.0.4,nw_dst=20.0.0.5
+ ip,nw_src=10.0.0.4,nw_dst=20.0.0.6
+ ip,nw_src=10.0.0.5,nw_dst=20.0.0.5
+ ip,nw_src=10.0.0.5,nw_dst=20.0.0.6
+ ip,nw_src=10.0.0.6,nw_dst=20.0.0.5
+ ip,nw_src=10.0.0.6,nw_dst=20.0.0.6
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.4,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.5,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.4,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.7,tp_src=2000,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ea/0xfffe,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3ec/0xfffc,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x3f0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x400/0xfe00,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x600/0xff00,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x700/0xff80,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x780/0xffc0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=0x7c0/0xfff0,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1000,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=1001,tp_dst=2000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ea/0xfffe
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3ec/0xfffc
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x3f0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x400/0xfe00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x600/0xff00
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x700/0xff80
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x780/0xffc0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=0x7c0/0xfff0
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1000
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=1001
+-tcp,nw_src=10.0.0.6,nw_dst=20.0.0.8,tp_src=2000,tp_dst=2000
++tcp,nw_dst=20.0.0.4: conjunction(1, 0/4)
++tcp,nw_dst=20.0.0.7: conjunction(1, 0/4)
++tcp,nw_dst=20.0.0.8: conjunction(1, 0/4)
++tcp,nw_src=10.0.0.4: conjunction(1, 1/4)
++tcp,nw_src=10.0.0.5: conjunction(1, 1/4)
++tcp,nw_src=10.0.0.6: conjunction(1, 1/4)
++tcp,tp_dst=0x3ea/0xfffe: conjunction(1, 2/4)
++tcp,tp_dst=0x3ec/0xfffc: conjunction(1, 2/4)
++tcp,tp_dst=0x3f0/0xfff0: conjunction(1, 2/4)
++tcp,tp_dst=0x400/0xfe00: conjunction(1, 2/4)
++tcp,tp_dst=0x600/0xff00: conjunction(1, 2/4)
++tcp,tp_dst=0x700/0xff80: conjunction(1, 2/4)
++tcp,tp_dst=0x780/0xffc0: conjunction(1, 2/4)
++tcp,tp_dst=0x7c0/0xfff0: conjunction(1, 2/4)
++tcp,tp_dst=1000: conjunction(1, 2/4)
++tcp,tp_dst=1001: conjunction(1, 2/4)
++tcp,tp_dst=2000: conjunction(1, 2/4)
++tcp,tp_src=0x3ea/0xfffe: conjunction(1, 3/4)
++tcp,tp_src=0x3ec/0xfffc: conjunction(1, 3/4)
++tcp,tp_src=0x3f0/0xfff0: conjunction(1, 3/4)
++tcp,tp_src=0x400/0xfe00: conjunction(1, 3/4)
++tcp,tp_src=0x600/0xff00: conjunction(1, 3/4)
++tcp,tp_src=0x700/0xff80: conjunction(1, 3/4)
++tcp,tp_src=0x780/0xffc0: conjunction(1, 3/4)
++tcp,tp_src=0x7c0/0xfff0: conjunction(1, 3/4)
++tcp,tp_src=1000: conjunction(1, 3/4)
++tcp,tp_src=1001: conjunction(1, 3/4)
++tcp,tp_src=2000: conjunction(1, 3/4)
+ ])
+ AT_CLEANUP
+ 
+@@ -13443,11 +12313,9 @@ AT_CHECK([cat 2.packets], [0], [expout])
+ # priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2)
+ # priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2)
+ 
+-# conjunction is disabled in OVN until the issues related to it are
+-# fixed.
+-OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \
++OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \
+ grep conjunction | wc -l`])
+-OVS_WAIT_UNTIL([test 0 = `as hv1 ovs-ofctl dump-flows br-int | \
++OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \
+ grep conj_id | wc -l`])
+ 
+ as hv1 ovs-ofctl dump-flows br-int
+-- 
+2.14.5
+
diff --git a/SOURCES/0001-controller-grant-cap_net_admin-to-ovn-controller.patch b/SOURCES/0001-controller-grant-cap_net_admin-to-ovn-controller.patch
new file mode 100644
index 0000000..efd6582
--- /dev/null
+++ b/SOURCES/0001-controller-grant-cap_net_admin-to-ovn-controller.patch
@@ -0,0 +1,28 @@
+From b179dc03af829443c2e11b4ee1ade456baa00af8 Mon Sep 17 00:00:00 2001
+Message-Id: <b179dc03af829443c2e11b4ee1ade456baa00af8.1582885124.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Thu, 27 Feb 2020 11:34:17 +0100
+Subject: [PATCH] controller: grant cap_net_admin to ovn-controller
+
+ovn-controller is currently running as non-root so it is not allowed to
+configure system networking breaking ovn QoS support. Fix the issue
+granting CAP_NET_ADMIN capability to ovn-controller process
+
+Tested-by: Ying Xu <yinxu@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/controller/ovn-controller.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -1756,7 +1756,7 @@ main(int argc, char *argv[])
+     char *ovs_remote = parse_options(argc, argv);
+     fatal_ignore_sigpipe();
+ 
+-    daemonize_start(false);
++    daemonize_start(true);
+ 
+     retval = unixctl_server_create(NULL, &unixctl);
+     if (retval) {
diff --git a/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch b/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch
new file mode 100644
index 0000000..d082027
--- /dev/null
+++ b/SOURCES/0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch
@@ -0,0 +1,105 @@
+From 7f60417ae6c7438565a21d5aee0bb8ae0b3a9b68 Mon Sep 17 00:00:00 2001
+Message-Id: <7f60417ae6c7438565a21d5aee0bb8ae0b3a9b68.1585835882.git.me@lorenzobianconi.net>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 24 Mar 2020 20:33:27 +0100
+Subject: [PATCH] controller: use LLA IPv6 address as NS source address
+
+Use router LLA IPv6 address as IPv6 source address for Neighbor
+Solicitation packets
+
+Fixes: c0bf32d72 ("Manage ARP process locally in a DVR scenario")
+Change-Id: Iafa26f4b3c20e181bd5b54a357d468ce61b589b6
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Lorenzo Bianconi <me@lorenzobianconi.net>
+---
+ ovn/controller/pinctrl.c |  4 +++-
+ tests/ovn.at         | 15 +++++++++------
+ 2 files changed, 12 insertions(+), 7 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -4363,9 +4363,11 @@ pinctrl_handle_nd_ns(struct rconn *swcon
+ 
+     uint64_t packet_stub[128 / 8];
+     struct dp_packet packet;
++    struct in6_addr ipv6_src;
+     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+ 
+-    compose_nd_ns(&packet, ip_flow->dl_src, &ip_flow->ipv6_src,
++    in6_generate_lla(ip_flow->dl_src, &ipv6_src);
++    compose_nd_ns(&packet, ip_flow->dl_src, &ipv6_src,
+                   &ip_flow->ipv6_dst);
+ 
+     /* Reload previous packet metadata and set actions from userdata. */
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -11225,13 +11225,13 @@ options:rxq_pcap=${pcap_file}-rx.pcap
+ # This function sends ipv6 packet
+ test_ipv6() {
+     local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5
+-    local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8
++    local dst_mcast_mac=$6 mcast_node_ip=$7 nd_target=$8 nd_src_ip=$9
+ 
+     local packet=${dst_mac}${src_mac}86dd6000000000083aff${src_ip}${dst_ip}
+     packet=${packet}8000000000000000
+ 
+     src_mac=000002010204
+-    expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${src_ip}
++    expected_packet=${dst_mcast_mac}${src_mac}86dd6000000000203aff${nd_src_ip}
+     expected_packet=${expected_packet}${mcast_node_ip}8700XXXX00000000
+     expected_packet=${expected_packet}${nd_target}0101${src_mac}
+ 
+@@ -11243,6 +11243,7 @@ test_ipv6() {
+ src_mac=506400000002
+ dst_mac=00000000af01
+ src_ip=aef0000000000000526400fffe000002
++nd_src_ip=fe80000000000000020002fffe010204
+ dst_ip=20010db800010000020002fffe010205
+ dst_mcast_mac=3333ff010205
+ mcast_node_ip=ff0200000000000000000001ff010205
+@@ -11250,7 +11251,7 @@ nd_target=20010db800010000020002fffe0102
+ # Send an IPv6 packet. Generated IPv6 Neighbor solicitation packet
+ # should be received by the ports attached to br-phys.
+ test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
+-$mcast_node_ip $nd_target
++$mcast_node_ip $nd_target $nd_src_ip
+ 
+ OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
+ OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
+@@ -11283,7 +11284,7 @@ dst_mcast_mac=3333ff011305
+ mcast_node_ip=ff0200000000000000000001ff011305
+ nd_target=20010db800010000020002fffe011305
+ test_ipv6 1 $src_mac $dst_mac $src_ip $dst_ip $dst_mcast_mac \
+-$mcast_node_ip $nd_target
++$mcast_node_ip $nd_target $nd_src_ip
+ 
+ OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys_n1-tx.pcap | cut -d " " -f1)])
+ OVS_WAIT_WHILE([test 24 = $(wc -c hv1/br-phys-tx.pcap | cut -d " " -f1)])
+@@ -14005,7 +14006,7 @@ send_na() {
+ get_nd() {
+     local eth_src=$1 src_ip=$2 dst_ip=$3 ta=$4
+     local ip6_hdr=6000000000203aff${src_ip}${dst_ip}
+-    request=3333ff000010${eth_src}86dd${ip6_hdr}8700357600000000${ta}0101${eth_src}
++    request=3333ff000010${eth_src}86dd${ip6_hdr}870051f400000000${ta}0101${eth_src}
+ 
+     echo $request
+ }
+@@ -14068,6 +14069,8 @@ router_mac1=000002010203
+ router_ip=$(ip_to_hex 172 16 1 1)
+ router_ip6=20020000000000000000000000000001
+ 
++nd_src_ip6=fe80000000000000020002fffe010203
++
+ dst_mac=001122334455
+ dst_ip=$(ip_to_hex 172 16 1 10)
+ dst_ip6=20020000000000000000000000000010
+@@ -14085,7 +14088,7 @@ nd_ip=ff0200000000000000000001ff000010
+ ip6_hdr=6000000000083afe${src_ip6}${dst_ip6}
+ 
+ send_icmp6_packet 1 1 $src_mac $router_mac0 $src_ip6 $dst_ip6
+-echo $(get_nd $router_mac1 $src_ip6 $nd_ip $dst_ip6) >> expected
++echo $(get_nd $router_mac1 $nd_src_ip6 $nd_ip $dst_ip6) >> expected
+ echo "${dst_mac}${router_mac1}86dd${ip6_hdr}8000dcb662f00001" >> expected
+ send_na 2 1 $dst_mac $router_mac1 $dst_ip6 $router_ip6
+ 
diff --git a/SOURCES/0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch b/SOURCES/0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch
new file mode 100644
index 0000000..4567c6b
--- /dev/null
+++ b/SOURCES/0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch
@@ -0,0 +1,49 @@
+From 4820216aa4bcadec2b77340ca7016a9d96a44b9a Mon Sep 17 00:00:00 2001
+From: Han Zhou <hzhou@ovn.org>
+Date: Mon, 21 Oct 2019 18:10:13 -0700
+Subject: [PATCH ovn] lflow.c: Fix memory leak of
+ lflow_ref_list_node->ref_name.
+
+The ref_name is copied in lflow_resource_add(), but forgot to free in
+lflow_resource_destroy_lflow(). It can be fixed by freeing it in
+lflow_resource_destroy_lflow(). However, this field is never really
+used, so just delete it from lflow_ref_list_node, together with the
+"type" field.
+
+Fixes: d2aa2c7cafead ("ovn-controller: Maintain resource references for logical flows.")
+Acked-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+---
+ ovn/controller/lflow.c | 2 --
+ ovn/controller/lflow.h | 2 --
+ 2 files changed, 4 deletions(-)
+
+diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
+index e3ed20c..24e3a52 100644
+--- a/ovn/controller/lflow.c
++++ b/ovn/controller/lflow.c
+@@ -230,8 +230,6 @@ lflow_resource_add(struct lflow_resource_ref *lfrr, enum ref_type type,
+     }
+ 
+     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);
+diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
+index 0572668..abdc55e 100644
+--- a/ovn/controller/lflow.h
++++ b/ovn/controller/lflow.h
+@@ -80,8 +80,6 @@ enum ref_type {
+ 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;
+ };
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch b/SOURCES/0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch
new file mode 100644
index 0000000..0e3b408
--- /dev/null
+++ b/SOURCES/0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch
@@ -0,0 +1,69 @@
+From d1b6e6838f0b69088122c125dd435899dbb1ec28 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 6 Dec 2019 15:11:44 -0500
+Subject: [PATCH ovn 1/4] nbctl: Log the source of duplicate IP addresses
+
+When doing an lsp-add, ovn-nbctl will ensure that there is not another
+port on the same logical switch with the same IP address.  I'm seeing
+this error occur with ovn-kubernetes, and I would find it helpful to
+see which port it thinks had the duplicate address, because it's not
+obvious what's happening.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit e34cd53a66dac5a371744459ca51b8df9eeea3c4)
+---
+ ovn/utilities/ovn-nbctl.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
+index 1a1d83e..3d2df13 100644
+--- a/ovn/utilities/ovn-nbctl.c
++++ b/ovn/utilities/ovn-nbctl.c
+@@ -1497,13 +1497,16 @@ nbctl_lsp_get_tag(struct ctl_context *ctx)
+ 
+ static char *
+ lsp_contains_duplicate_ip(struct lport_addresses *laddrs1,
+-                          struct lport_addresses *laddrs2)
++                          struct lport_addresses *laddrs2,
++                          const struct nbrec_logical_switch_port *lsp_test)
+ {
+     for (size_t i = 0; i < laddrs1->n_ipv4_addrs; i++) {
+         for (size_t j = 0; j < laddrs2->n_ipv4_addrs; j++) {
+             if (laddrs1->ipv4_addrs[i].addr == laddrs2->ipv4_addrs[j].addr) {
+-                return xasprintf("duplicate IPv4 address %s",
+-                                 laddrs1->ipv4_addrs[i].addr_s);
++                return xasprintf("duplicate IPv4 address '%s' found on "
++                                 "logical switch port '%s'",
++                                 laddrs1->ipv4_addrs[i].addr_s,
++                                 lsp_test->name);
+             }
+         }
+     }
+@@ -1512,8 +1515,10 @@ lsp_contains_duplicate_ip(struct lport_addresses *laddrs1,
+         for (size_t j = 0; j < laddrs2->n_ipv6_addrs; j++) {
+             if (IN6_ARE_ADDR_EQUAL(&laddrs1->ipv6_addrs[i].addr,
+                                    &laddrs2->ipv6_addrs[j].addr)) {
+-                return xasprintf("duplicate IPv6 address %s",
+-                                 laddrs1->ipv6_addrs[i].addr_s);
++                return xasprintf("duplicate IPv6 address '%s' found on "
++                                 "logical switch port '%s'",
++                                 laddrs1->ipv6_addrs[i].addr_s,
++                                 lsp_test->name);
+             }
+         }
+     }
+@@ -1544,7 +1549,8 @@ lsp_contains_duplicates(const struct nbrec_logical_switch *ls,
+                 addr = lsp_test->dynamic_addresses;
+             }
+             if (extract_lsp_addresses(addr, &laddrs_test)) {
+-                sub_error = lsp_contains_duplicate_ip(&laddrs, &laddrs_test);
++                sub_error = lsp_contains_duplicate_ip(&laddrs, &laddrs_test,
++                                                      lsp_test);
+                 destroy_lport_addresses(&laddrs_test);
+                 if (sub_error) {
+                     goto err_out;
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-northd-Allow-64-after-ipv6_prefix.patch b/SOURCES/0001-northd-Allow-64-after-ipv6_prefix.patch
new file mode 100644
index 0000000..4fd590b
--- /dev/null
+++ b/SOURCES/0001-northd-Allow-64-after-ipv6_prefix.patch
@@ -0,0 +1,89 @@
+From fbcef69a367112ff8012d0d0caec4320331f3641 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Wed, 19 Feb 2020 10:48:18 -0500
+Subject: [PATCH] northd: Allow /64 after ipv6_prefix
+
+We recently hit a bug in ovn-kubernetes, where I accidentally added
+/64 at the end of ipv6_prefix, to match the format we used for the
+subnet option for IPv4.  This was not allowed.
+
+This patch update ovn-northd to take the ipv6_prefix either with or
+without a trailing "/64".  It still enforces a /64 CIDR prefix length.
+
+A test case was updated to ensure that a prefix with "/64" is now
+accepted.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream commit d425050eb95c2b0ab5312d79fd0d6bd4a7d6d6e8)
+
+Change-Id: I29b65254ad20a91945fad77083afc149cadc4f60
+---
+ ovn/northd/ovn-northd.c | 33 +++++++++++++++++++++++++++++++--
+ tests/ovn.at            |  4 +++-
+ 2 files changed, 34 insertions(+), 3 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 1d21f8c34..c225dd2e5 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -713,8 +713,37 @@ init_ipam_info_for_datapath(struct ovn_datapath *od)
+     const char *ipv6_prefix = smap_get(&od->nbs->other_config, "ipv6_prefix");
+ 
+     if (ipv6_prefix) {
+-        od->ipam_info.ipv6_prefix_set = ipv6_parse(
+-            ipv6_prefix, &od->ipam_info.ipv6_prefix);
++        if (strstr(ipv6_prefix, "/")) {
++            /* If a prefix length was specified, it must be 64. */
++            struct in6_addr mask;
++            char *error
++                = ipv6_parse_masked(ipv6_prefix,
++                                    &od->ipam_info.ipv6_prefix, &mask);
++            if (error) {
++                static struct vlog_rate_limit rl
++                    = VLOG_RATE_LIMIT_INIT(5, 1);
++                VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s: %s",
++                             ipv6_prefix, error);
++                free(error);
++            } else {
++                if (ipv6_count_cidr_bits(&mask) == 64) {
++                    od->ipam_info.ipv6_prefix_set = true;
++                } else {
++                    static struct vlog_rate_limit rl
++                        = VLOG_RATE_LIMIT_INIT(5, 1);
++                    VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s: must be /64",
++                                 ipv6_prefix);
++                }
++            }
++        } else {
++            od->ipam_info.ipv6_prefix_set = ipv6_parse(
++                ipv6_prefix, &od->ipam_info.ipv6_prefix);
++            if (!od->ipam_info.ipv6_prefix_set) {
++                static struct vlog_rate_limit rl
++                    = VLOG_RATE_LIMIT_INIT(5, 1);
++                VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s", ipv6_prefix);
++            }
++        }
+     }
+ 
+     if (!subnet_str) {
+diff --git a/tests/ovn.at b/tests/ovn.at
+index c06337147..2e1c33237 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -12292,8 +12292,10 @@ ovn-nbctl set Logical_Switch ls1 \
+     other_config:subnet=10.1.0.0/24 other_config:ipv6_prefix="2001:db8:1::"
+ ovn-nbctl set Logical_Switch ls2 \
+     other_config:subnet=10.2.0.0/24 other_config:ipv6_prefix="2001:db8:2::"
++
++# A prefix length may be specified, but only if it is /64.
+ ovn-nbctl set Logical_Switch ls3 \
+-    other_config:subnet=10.3.0.0/24 other_config:ipv6_prefix="2001:db8:3::"
++    other_config:subnet=10.3.0.0/24 other_config:ipv6_prefix="2001:db8:3::/64"
+ 
+ ovn-nbctl lsp-add ls1 lp1
+ ovn-nbctl lsp-add ls2 lp2
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch b/SOURCES/0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch
new file mode 100644
index 0000000..1913c04
--- /dev/null
+++ b/SOURCES/0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch
@@ -0,0 +1,68 @@
+From 3dde59a7b7f0bb634fa5943b185b1cf1d4ed6907 Mon Sep 17 00:00:00 2001
+Message-Id: <3dde59a7b7f0bb634fa5943b185b1cf1d4ed6907.1594372314.git.lorenzo.bianconi@redhat.com>
+From: Mark Michelson <mmichels@redhat.com>
+Date: Tue, 2 Jun 2020 09:06:32 -0400
+Subject: [PATCH] northd: Fix IPAM IPv4 start address calculation.
+
+IPAM assumes the other_config:subnet of the logical switch is a network
+address, which can result in unusual address assignments.
+
+As an example, consider the following configuration:
+
+ovn-nbctl set logical_switch ls other_config:subnet=172.16.1.254/29
+
+172.16.1.254 is not a network address of a /29 network, but ovn-northd
+doesn't care. ovn-northd starts IP address allocation at 172.16.1.254, with 7
+assignable addresses in the subnet. The first address (172.16.1.255) is
+reserved for router port use. The first IP addresses to a logical switch
+port is 172.16.2.0, then 172.16.2.1, and so on.
+
+This patch changes the behavior by using the provided netmask to change
+the starting IP address to the network address of the subnet. In the
+previous example, the provided 172.16.1.254/29 would be converted
+internally to 172.16.1.248/29 . Therefore, the first IP address
+allocated to a switch port would be 172.16.1.250. Further allocations would
+continue up until 172.16.1.254.
+
+Reported at: https://bugzilla.redhat.com/show_bug.cgi?id=1823287
+
+Acked-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ ovn/northd/ovn-northd.c |  2 +-
+ tests/ovn.at            | 11 +++++++++++
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -764,7 +764,7 @@ init_ipam_info_for_datapath(struct ovn_d
+         return;
+     }
+ 
+-    od->ipam_info.start_ipv4 = ntohl(subnet) + 1;
++    od->ipam_info.start_ipv4 = ntohl(subnet & mask) + 1;
+     od->ipam_info.total_ipv4s = ~ntohl(mask);
+     od->ipam_info.allocated_ipv4s =
+         bitmap_allocate(od->ipam_info.total_ipv4s);
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -6636,6 +6636,17 @@ AT_CHECK([ovn-nbctl get Logical-Switch-P
+     ["00:11:22:a8:6e:0b 192.168.110.10 ae01::2"
+ ])
+ 
++# Configure subnet using address from middle of the subnet and ensure
++# address is allocated from the beginning.
++
++ovn-nbctl ls-add sw11
++ovn-nbctl --wait=sb set Logical-Switch sw11 other_config:subnet=172.16.1.254/29
++ovn-nbctl --wait=sb lsp-add sw11 p103 -- lsp-set-addresses p103 "22:33:44:55:66:77 dynamic"
++
++AT_CHECK([ovn-nbctl get Logical-Switch-Port p103 dynamic_addresses], [0],
++    ["22:33:44:55:66:77 172.16.1.250"
++])
++
+ as ovn-sb
+ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+ 
diff --git a/SOURCES/0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch b/SOURCES/0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch
new file mode 100644
index 0000000..0cbafcc
--- /dev/null
+++ b/SOURCES/0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch
@@ -0,0 +1,31 @@
+From 56e3168c643953c789ca55b352623b08f8c97a7d Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 25 Oct 2019 16:06:14 -0400
+Subject: [PATCH 1/5] northd: Fix table ID for IPv6 router ingress.
+
+I noticed that this table number was outdated.  This is now table 3.
+There are a few other sections of code for this table that were all
+correctly referencing table 3.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 0e463e0a8..7cfeb60be 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -7046,7 +7046,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         free(snat_ips);
+     }
+ 
+-    /* Logical router ingress table 1: IP Input for IPv6. */
++    /* Logical router ingress table 3: IP Input for IPv6. */
+     HMAP_FOR_EACH (op, key_node, ports) {
+         if (!op->nbrp) {
+             continue;
+-- 
+2.23.0
+
diff --git a/SOURCES/0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch b/SOURCES/0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch
new file mode 100644
index 0000000..e5f8b0f
--- /dev/null
+++ b/SOURCES/0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch
@@ -0,0 +1,42 @@
+From 84704b5236952e3a7c191b4b23f5af574f5840d9 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Mon, 18 Nov 2019 20:27:12 -0500
+Subject: [PATCH 1/2] northd: Match IPv4 or IPv6 for MAC resolution
+
+While debugging some problems in a cluster using ovn-kubernetes, I
+noticed that we're creating two conflicting logical flows.  These two
+flows only matched on the destination MAC address.  It was not
+deterministic whether you'd hit the IPv4 (ARP) or IPv6 (NS) version.
+
+This change adds an ip4 or ip6 match to each flow as appropriate.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+---
+ ovn/northd/ovn-northd.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index cb12be3ca..bcadcca3d 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -8661,7 +8661,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+ 
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
+-                      "eth.dst == 00:00:00:00:00:00",
++                      "eth.dst == 00:00:00:00:00:00 && ip4",
+                       "arp { "
+                       "eth.dst = ff:ff:ff:ff:ff:ff; "
+                       "arp.spa = reg1; "
+@@ -8670,7 +8670,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                       "output; "
+                       "};");
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 100,
+-                      "eth.dst == 00:00:00:00:00:00",
++                      "eth.dst == 00:00:00:00:00:00 && ip6",
+                       "nd_ns { "
+                       "nd.target = xxreg0; "
+                       "output; "
+-- 
+2.23.0
+
diff --git a/SOURCES/0001-northd-introduce-build_empty_lb_event_flow-routine.patch b/SOURCES/0001-northd-introduce-build_empty_lb_event_flow-routine.patch
new file mode 100644
index 0000000..4d09829
--- /dev/null
+++ b/SOURCES/0001-northd-introduce-build_empty_lb_event_flow-routine.patch
@@ -0,0 +1,92 @@
+From 3624362cb395b6bc90f6fd69e12988979db95b7e Mon Sep 17 00:00:00 2001
+Message-Id: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 3 Sep 2019 16:22:51 +0200
+Subject: [PATCH ovn 1/3] northd: introduce build_empty_lb_event_flow routine
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/northd/ovn-northd.c | 63 ++++++++++++++++++++++++++-------------------
+ 1 file changed, 37 insertions(+), 26 deletions(-)
+
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -3882,6 +3882,41 @@ ls_has_dns_records(const struct nbrec_lo
+ }
+ 
+ static void
++build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
++                          struct smap_node *node, char *ip_address,
++                          struct nbrec_load_balancer *lb, uint16_t port,
++                          int addr_family, int pl)
++{
++    if (!controller_event_en || node->value[0]) {
++        return;
++    }
++
++    struct ds match = DS_EMPTY_INITIALIZER;
++    char *action;
++
++    if (addr_family == AF_INET) {
++        ds_put_format(&match, "ip4.dst == %s && %s",
++                      ip_address, lb->protocol);
++    } else {
++        ds_put_format(&match, "ip6.dst == %s && %s",
++                      ip_address, lb->protocol);
++    }
++    if (port) {
++        ds_put_format(&match, " && %s.dst == %u", lb->protocol,
++                      port);
++    }
++    action = xasprintf("trigger_event(event = \"%s\", "
++                   "vip = \"%s\", protocol = \"%s\", "
++                   "load_balancer = \"" UUID_FMT "\");",
++                   event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
++                   node->key, lb->protocol,
++                   UUID_ARGS(&lb->header_.uuid));
++    ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
++    ds_destroy(&match);
++    free(action);
++}
++
++static void
+ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
+ {
+     /* Do not send ND packets to conntrack */
+@@ -3918,32 +3953,8 @@ build_pre_lb(struct ovn_datapath *od, st
+                 sset_add(&all_ips, ip_address);
+             }
+ 
+-            if (controller_event_en && !node->value[0]) {
+-                struct ds match = DS_EMPTY_INITIALIZER;
+-                char *action;
+-
+-                if (addr_family == AF_INET) {
+-                    ds_put_format(&match, "ip4.dst == %s && %s",
+-                                  ip_address, lb->protocol);
+-                } else {
+-                    ds_put_format(&match, "ip6.dst == %s && %s",
+-                                  ip_address, lb->protocol);
+-                }
+-                if (port) {
+-                    ds_put_format(&match, " && %s.dst == %u", lb->protocol,
+-                                  port);
+-                }
+-                action = xasprintf("trigger_event(event = \"%s\", "
+-                               "vip = \"%s\", protocol = \"%s\", "
+-                               "load_balancer = \"" UUID_FMT "\");",
+-                               event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
+-                               node->key, lb->protocol,
+-                               UUID_ARGS(&lb->header_.uuid));
+-                ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 120,
+-                              ds_cstr(&match), action);
+-                ds_destroy(&match);
+-                free(action);
+-            }
++            build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
++                                      port, addr_family, S_SWITCH_IN_PRE_LB);
+ 
+             free(ip_address);
+ 
diff --git a/SOURCES/0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch b/SOURCES/0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch
new file mode 100644
index 0000000..4a588c9
--- /dev/null
+++ b/SOURCES/0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch
@@ -0,0 +1,122 @@
+From db78e4fbe040a2e2a1b849dfdea4c494fe98af26 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <nusiddiq@redhat.com>
+Date: Sat, 14 Sep 2019 01:56:43 +0530
+Subject: [PATCH] ovn: Exclude inport and outport symbol tables from
+ conjunction
+
+If there are multiple ACLs associated with a port group and they
+match on a range of some field, then ovn-controller doesn't install
+the flows properly and this results in broken ACL functionality.
+
+For example, if there is a port group - pg1 with logical ports - [p1, p2]
+and if there are below ACLs (only match condition is shown)
+
+1 -  outport == @pg1 && ip4 && tcp.dst >= 500 && tcp.dst <= 501
+2 -  outport == @pg1 && ip4 && tcp.dst >= 600 && tcp.dst <= 601
+
+The first ACL will result in the below OF flows
+
+1.  conj_id=1,tcp
+2.  tcp,reg15=0x11: conjunction(1, 1/2)
+3.  tcp,reg15=0x12: conjunction(1, 1/2)
+5.  tcp,tp_dst=500: conjunction(1, 2/2)
+6.  tcp,tp_dst=501: conjunction(1, 2/2)
+
+The second ACL will result in the below OF flows
+7.  conj_id=2,tcp
+8.  tcp,reg15=0x11: conjunction(2, 1/2)
+9.  tcp,reg15=0x12: conjunction(2, 1/2)
+11. tcp,tp_dst=600: conjunction(2, 2/2)
+12. tcp,tp_dst=601: conjunction(2, 3/2)
+
+The OF flows (2) and (8) have the exact match but with different action.
+This results in only one of the flows getting installed. The same goes
+for the flows (3) and (9). And this completely breaks the ACL functionality
+for such scenarios.
+
+In order to fix this issue, this patch excludes the 'inport' and 'outport' symbols
+from conjunction. With this patch we will have the below flows.
+
+tcp,reg15=0x11,tp_dst=500
+tcp,reg15=0x11,tp_dst=501
+tcp,reg15=0x12,tp_dst=500
+tcp,reg15=0x12,tp_dst=501
+tcp,reg15=0x13,tp_dst=500
+tcp,reg15=0x13,tp_dst=501
+tcp,reg15=0x11,tp_dst=600
+tcp,reg15=0x11,tp_dst=601
+tcp,reg15=0x12,tp_dst=600
+tcp,reg15=0x12,tp_dst=601
+tcp,reg15=0x13,tp_dst=600
+tcp,reg15=0x13,tp_dst=601
+
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Daniel Alvarez <dalvarez@redhat.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+
+(cherry-picked from ovn commit 298701dbc99645700be41680a43d049cb061847a)
+---
+ ovn/lib/expr.c |  2 +-
+ tests/ovn.at   | 26 ++++++++++++++++++++++++++
+ 2 files changed, 27 insertions(+), 1 deletion(-)
+
+diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
+index e4c650f7c..c0871e1e8 100644
+--- a/ovn/lib/expr.c
++++ b/ovn/lib/expr.c
+@@ -1499,7 +1499,7 @@ expr_symtab_add_string(struct shash *symtab, const char *name,
+     const struct mf_field *field = mf_from_id(id);
+     struct expr_symbol *symbol;
+ 
+-    symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, false,
++    symbol = add_symbol(symtab, name, 0, prereqs, EXPR_L_NOMINAL, true,
+                         field->writable);
+     symbol->field = field;
+     return symbol;
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 2361524ff..54aa19bb2 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -573,6 +573,24 @@ ip,reg14=0x6
+ ipv6,reg14=0x5
+ ipv6,reg14=0x6
+ ])
++AT_CHECK([expr_to_flow 'inport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.dst == {500, 501}'], [0], [dnl
++tcp,reg14=0x5,tp_dst=500
++tcp,reg14=0x5,tp_dst=501
++tcp,reg14=0x6,tp_dst=500
++tcp,reg14=0x6,tp_dst=501
++])
++AT_CHECK([expr_to_flow 'outport == {"eth0", "eth1", "eth2"} && ip4 && tcp && tcp.src == {400, 401} && tcp.dst == {500, 501}'], [0], [dnl
++conj_id=1,tcp,reg15=0x5
++conj_id=2,tcp,reg15=0x6
++tcp,reg15=0x5,tp_dst=500: conjunction(1, 0/2)
++tcp,reg15=0x5,tp_dst=501: conjunction(1, 0/2)
++tcp,reg15=0x5,tp_src=400: conjunction(1, 1/2)
++tcp,reg15=0x5,tp_src=401: conjunction(1, 1/2)
++tcp,reg15=0x6,tp_dst=500: conjunction(2, 0/2)
++tcp,reg15=0x6,tp_dst=501: conjunction(2, 0/2)
++tcp,reg15=0x6,tp_src=400: conjunction(2, 1/2)
++tcp,reg15=0x6,tp_src=401: conjunction(2, 1/2)
++])
+ AT_CHECK([expr_to_flow 'inport == "eth0" && inport == "eth1"'], [0], [dnl
+ (no flows)
+ ])
+@@ -677,6 +695,14 @@ reg15=0x11
+ reg15=0x12
+ reg15=0x13
+ ])
++AT_CHECK([expr_to_flow 'outport == @pg1 && ip4.src == {10.0.0.4, 10.0.0.5}'], [0], [dnl
++ip,reg15=0x11,nw_src=10.0.0.4
++ip,reg15=0x11,nw_src=10.0.0.5
++ip,reg15=0x12,nw_src=10.0.0.4
++ip,reg15=0x12,nw_src=10.0.0.5
++ip,reg15=0x13,nw_src=10.0.0.4
++ip,reg15=0x13,nw_src=10.0.0.5
++])
+ AT_CHECK([expr_to_flow 'outport == {@pg_empty}'], [0], [dnl
+ (no flows)
+ ])
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch b/SOURCES/0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch
new file mode 100644
index 0000000..46dba6f
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch
@@ -0,0 +1,92 @@
+From 996e1077aa5ddf4731aba5805e99c59168eab3f9 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Mon, 2 Dec 2019 19:02:52 +0100
+Subject: [PATCH ovn] ovn-controller: Add command to trigger an I-P full
+ recompute.
+
+Incremental processing tries to minimize the number of times
+ovn-controller has to fully reprocess the contents of the southbound
+database. However, if a bug in the I-P code causes ovn-controller to
+end up in an inconsistent state, we have no easy way to force a full
+recalculation of the openflow entries.
+
+This commit adds a new command to ovn-controller, "recompute", which
+allows users to force a full recompute of the database. It can be
+triggered by the user in the following way:
+
+ovn-appctl -t ovn-controller recompute
+
+Reviewed-by: Daniel Alvarez <dalvarez@redhat.com>
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit c66e0cee15ef6716ff2c092dce3168c92975bbf1)
+---
+ ovn/controller/ovn-controller.8.xml | 14 ++++++++++++++
+ ovn/controller/ovn-controller.c     | 14 ++++++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml
+index 780625f..a226802 100644
+--- a/ovn/controller/ovn-controller.8.xml
++++ b/ovn/controller/ovn-controller.8.xml
+@@ -450,6 +450,20 @@
+       <dd>
+         Show OVN SBDB connection status for the chassis.
+       </dd>
++
++      <dt><code>recompute</code></dt>
++      <dd>
++      <p>
++        Trigger a full compute iteration in <code>ovn-controller</code> based
++        on the contents of the Southbound database and local OVS database.
++      </p>
++      <p>
++        This command is intended to use only in the event of a bug in the
++        incremental processing engine in <code>ovn-controller</code> to avoid
++        inconsistent states. It should therefore be used with care as full
++        recomputes are cpu intensive.
++      </p>
++      </dd>
+       </dl>
+     </p>
+ 
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index d7eb9d0..e864101 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -73,6 +73,7 @@ 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;
++static unixctl_cb_func engine_recompute_cmd;
+ 
+ #define DEFAULT_BRIDGE_NAME "br-int"
+ #define DEFAULT_PROBE_INTERVAL_MSEC 5000
+@@ -1895,6 +1896,9 @@ main(int argc, char *argv[])
+     unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
+                              &pending_pkt);
+ 
++    unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd,
++                             NULL);
++
+     uint64_t engine_run_id = 0;
+     uint64_t old_engine_run_id = 0;
+     bool engine_run_done = true;
+@@ -2391,3 +2395,13 @@ ovn_controller_conn_show(struct unixctl_conn *conn, int argc OVS_UNUSED,
+     }
+     unixctl_command_reply(conn, result);
+ }
++
++static void
++engine_recompute_cmd(struct unixctl_conn *conn OVS_UNUSED, int argc OVS_UNUSED,
++                     const char *argv[] OVS_UNUSED, void *arg OVS_UNUSED)
++{
++    VLOG_INFO("User triggered force recompute.");
++    engine_set_force_recompute(true);
++    poll_immediate_wake();
++    unixctl_command_reply(conn, NULL);
++}
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch b/SOURCES/0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch
new file mode 100644
index 0000000..9316f0f
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch
@@ -0,0 +1,233 @@
+From 07e82c86d4f6540b701ce43b0c39859a957ab820 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Mon, 2 Dec 2019 14:20:32 +0100
+Subject: [PATCH ovn] ovn-controller: Add missing port group lflow
+ references.
+
+The commit that adds incremental processing for port-group changes
+doesn't store logical flow references for port groups. If a port group
+is updated (e.g., a port is added) no logical flow recalculation will be
+performed.
+
+To fix this, when parsing the flow expression also store the referenced
+port groups and bind them to the logical flows that depend on them. If
+the port group is updated then the logical flows referring them will
+also be reinstalled.
+
+Reported-by: Daniel Alvarez <dalvarez@redhat.com>
+Reported-at: https://bugzilla.redhat.com/1778164
+CC: Han Zhou <hzhou@ovn.org>
+Fixes: 978f5e90af0a ("ovn-controller: Incremental processing for port-group changes.")
+Tested-By: Daniel Alvarez <dalvarez@redhat.com>
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit bbcac48d443e98cbe47d3941f7e192c9c3443cb5)
+---
+ include/ovn/expr.h        |  4 +++-
+ ovn/controller/lflow.c    |  9 ++++++++-
+ ovn/lib/actions.c         |  4 ++--
+ ovn/lib/expr.c            | 24 +++++++++++++++++-------
+ ovn/utilities/ovn-trace.c |  2 +-
+ tests/test-ovn.c          |  8 ++++----
+ 6 files changed, 35 insertions(+), 16 deletions(-)
+
+diff --git a/include/ovn/expr.h b/include/ovn/expr.h
+index 22f633e..21bf51c 100644
+--- a/include/ovn/expr.h
++++ b/include/ovn/expr.h
+@@ -390,11 +390,13 @@ 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 sset *addr_sets_ref,
++                        struct sset *port_groups_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,
++                               struct sset *port_groups_ref,
+                                char **errorp);
+ 
+ struct expr *expr_clone(struct expr *);
+diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
+index 9dda76f..9dbce91 100644
+--- a/ovn/controller/lflow.c
++++ b/ovn/controller/lflow.c
+@@ -616,14 +616,21 @@ consider_logical_flow(
+     struct expr *expr;
+ 
+     struct sset addr_sets_ref = SSET_INITIALIZER(&addr_sets_ref);
++    struct sset port_groups_ref = SSET_INITIALIZER(&port_groups_ref);
+     expr = expr_parse_string(lflow->match, &symtab, addr_sets, port_groups,
+-                             &addr_sets_ref, &error);
++                             &addr_sets_ref, &port_groups_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);
+     }
++    const char *port_group_name;
++    SSET_FOR_EACH (port_group_name, &port_groups_ref) {
++        lflow_resource_add(lfrr, REF_TYPE_PORTGROUP, port_group_name,
++                           &lflow->header_.uuid);
++    }
+     sset_destroy(&addr_sets_ref);
++    sset_destroy(&port_groups_ref);
+ 
+     if (!error) {
+         if (prereqs) {
+diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
+index 7857f65..82a9334 100644
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -240,8 +240,8 @@ add_prerequisite(struct action_context *ctx, const char *prerequisite)
+     struct expr *expr;
+     char *error;
+ 
+-    expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL, NULL,
+-                             &error);
++    expr = expr_parse_string(prerequisite, ctx->pp->symtab, NULL, NULL,
++                             NULL, NULL, &error);
+     ovs_assert(!error);
+     ctx->prereqs = expr_combine(EXPR_T_AND, ctx->prereqs, expr);
+ }
+diff --git a/ovn/lib/expr.c b/ovn/lib/expr.c
+index ca72075..4af1cb2 100644
+--- a/ovn/lib/expr.c
++++ b/ovn/lib/expr.c
+@@ -480,7 +480,8 @@ struct expr_context {
+     const struct shash *symtab;    /* Symbol table. */
+     const struct shash *addr_sets; /* Address set table. */
+     const struct shash *port_groups; /* Port group table. */
+-    struct sset *addr_sets_ref;       /* The set of address set referenced. */
++    struct sset *addr_sets_ref;      /* The set of address set referenced. */
++    struct sset *port_groups_ref;    /* The set of port groups referenced. */
+     bool not;                    /* True inside odd number of NOT operators. */
+     unsigned int paren_depth;    /* Depth of nested parentheses. */
+ };
+@@ -782,6 +783,10 @@ static bool
+ parse_port_group(struct expr_context *ctx, struct expr_constant_set *cs,
+                  size_t *allocated_values)
+ {
++    if (ctx->port_groups_ref) {
++        sset_add(ctx->port_groups_ref, ctx->lexer->token.s);
++    }
++
+     struct expr_constant_set *port_group
+         = (ctx->port_groups
+            ? shash_find_data(ctx->port_groups, ctx->lexer->token.s)
+@@ -1296,13 +1301,15 @@ struct expr *
+ expr_parse(struct lexer *lexer, const struct shash *symtab,
+            const struct shash *addr_sets,
+            const struct shash *port_groups,
+-           struct sset *addr_sets_ref)
++           struct sset *addr_sets_ref,
++           struct sset *port_groups_ref)
+ {
+     struct expr_context ctx = { .lexer = lexer,
+                                 .symtab = symtab,
+                                 .addr_sets = addr_sets,
+                                 .port_groups = port_groups,
+-                                .addr_sets_ref = addr_sets_ref };
++                                .addr_sets_ref = addr_sets_ref,
++                                .port_groups_ref = port_groups_ref };
+     return lexer->error ? NULL : expr_parse__(&ctx);
+ }
+ 
+@@ -1317,6 +1324,7 @@ expr_parse_string(const char *s, const struct shash *symtab,
+                   const struct shash *addr_sets,
+                   const struct shash *port_groups,
+                   struct sset *addr_sets_ref,
++                  struct sset *port_groups_ref,
+                   char **errorp)
+ {
+     struct lexer lexer;
+@@ -1324,7 +1332,7 @@ expr_parse_string(const char *s, const struct shash *symtab,
+     lexer_init(&lexer, s);
+     lexer_get(&lexer);
+     struct expr *expr = expr_parse(&lexer, symtab, addr_sets, port_groups,
+-                                   addr_sets_ref);
++                                   addr_sets_ref, port_groups_ref);
+     lexer_force_end(&lexer);
+     *errorp = lexer_steal_error(&lexer);
+     if (*errorp) {
+@@ -1550,7 +1558,8 @@ expr_get_level(const struct expr *expr)
+ static enum expr_level
+ expr_parse_level(const char *s, const struct shash *symtab, char **errorp)
+ {
+-    struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, errorp);
++    struct expr *expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL,
++                                          errorp);
+     enum expr_level level = expr ? expr_get_level(expr) : EXPR_L_NOMINAL;
+     expr_destroy(expr);
+     return level;
+@@ -1721,7 +1730,7 @@ parse_and_annotate(const char *s, const struct shash *symtab,
+     char *error;
+     struct expr *expr;
+ 
+-    expr = expr_parse_string(s, symtab, NULL, NULL, NULL, &error);
++    expr = expr_parse_string(s, symtab, NULL, NULL, NULL, NULL, &error);
+     if (expr) {
+         expr = expr_annotate_(expr, symtab, nesting, &error);
+     }
+@@ -3445,7 +3454,8 @@ expr_parse_microflow(const char *s, const struct shash *symtab,
+     lexer_init(&lexer, s);
+     lexer_get(&lexer);
+ 
+-    struct expr *e = expr_parse(&lexer, symtab, addr_sets, port_groups, NULL);
++    struct expr *e = expr_parse(&lexer, symtab, addr_sets, port_groups,
++                                NULL, NULL);
+     lexer_force_end(&lexer);
+ 
+     if (e) {
+diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
+index 7ed4a38..05d7113 100644
+--- a/ovn/utilities/ovn-trace.c
++++ b/ovn/utilities/ovn-trace.c
+@@ -866,7 +866,7 @@ read_flows(void)
+         char *error;
+         struct expr *match;
+         match = expr_parse_string(sblf->match, &symtab, &address_sets,
+-                                  &port_groups, NULL, &error);
++                                  &port_groups, NULL, NULL, &error);
+         if (error) {
+             VLOG_WARN("%s: parsing expression failed (%s)",
+                       sblf->match, error);
+diff --git a/tests/test-ovn.c b/tests/test-ovn.c
+index a7adc1c..b6eea4d 100644
+--- a/tests/test-ovn.c
++++ b/tests/test-ovn.c
+@@ -289,7 +289,7 @@ test_parse_expr__(int steps)
+         char *error;
+ 
+         expr = expr_parse_string(ds_cstr(&input), &symtab, &addr_sets,
+-                                 &port_groups, NULL, &error);
++                                 &port_groups, NULL, NULL, &error);
+         if (!error && steps > 0) {
+             expr = expr_annotate(expr, &symtab, &error);
+         }
+@@ -413,8 +413,8 @@ test_evaluate_expr(struct ovs_cmdl_context *ctx)
+     while (!ds_get_test_line(&input, stdin)) {
+         struct expr *expr;
+ 
+-        expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, NULL, NULL,
+-                                 &error);
++        expr = expr_parse_string(ds_cstr(&input), &symtab, NULL, NULL,
++                                 NULL, NULL, &error);
+         if (!error) {
+             expr = expr_annotate(expr, &symtab, &error);
+         }
+@@ -889,7 +889,7 @@ test_tree_shape_exhaustively(struct expr *expr, struct shash *symtab,
+ 
+             char *error;
+             modified = expr_parse_string(ds_cstr(&s), symtab, NULL,
+-                                         NULL, NULL, &error);
++                                         NULL, NULL, NULL, &error);
+             if (error) {
+                 fprintf(stderr, "%s fails to parse (%s)\n",
+                         ds_cstr(&s), error);
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-controller-Consider-non-virtual-ports-first-when.patch b/SOURCES/0001-ovn-controller-Consider-non-virtual-ports-first-when.patch
new file mode 100644
index 0000000..59d748f
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Consider-non-virtual-ports-first-when.patch
@@ -0,0 +1,178 @@
+From 726f0c5749f6ba3e147397c768854db515f5899a Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Fri, 22 Nov 2019 15:22:25 +0100
+Subject: [PATCH ovn] ovn-controller: Consider non-virtual ports first when
+ updating bindings.
+
+There's no guarantee SBREC_PORT_BINDING_TABLE_FOR_EACH will first
+return the non-virtual ports and then virtual ports. In the case when a
+virtual port is processed before its virtual_parent,
+consider_local_datapath might not release it in the current
+ovn-controller iteration even though the virtual_parent gets released.
+
+Right now this doesn't trigger any functionality issue because releasing
+the virtual_parent triggers a change in the SB DB which will wake up
+ovn-controller and trigger a new computation which will also update the
+virtual port.
+
+However, this is suboptimal, and we can notice that often ovn-controller
+gets the SB update notification before the "transaction successful"
+notification. In such cases the incremental engine doesn't run
+(ovnsb_idl_txn == NULL) and a full recompute is scheduled for the next
+run. By batching the two SB updates in a single transaction we
+lower the risk of this situation happening.
+
+CC: Numan Siddique <numans@ovn.org>
+Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry picked from upstream commit 5309099ec38cf41f4e41f1929c408741a3146dac)
+---
+ ovn/controller/binding.c | 97 ++++++++++++++++++++++++++++++++++--------------
+ 1 file changed, 69 insertions(+), 28 deletions(-)
+
+diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
+index 3adbfbe..1ae9151 100644
+--- a/ovn/controller/binding.c
++++ b/ovn/controller/binding.c
+@@ -461,7 +461,12 @@ is_our_chassis(const struct sbrec_chassis *chassis_rec,
+     return our_chassis;
+ }
+ 
+-static void
++/* Updates 'binding_rec' and if the port binding is local also updates the
++ * local datapaths and ports.
++ * Updates and returns the array of local virtual ports that will require
++ * additional processing.
++ */
++static const struct sbrec_port_binding **
+ 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,
+@@ -474,7 +479,9 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                         struct hmap *local_datapaths,
+                         struct shash *lport_to_iface,
+                         struct sset *local_lports,
+-                        struct sset *local_lport_ids)
++                        struct sset *local_lport_ids,
++                        const struct sbrec_port_binding **vpbs,
++                        size_t *n_vpbs, size_t *n_allocated_vpbs)
+ {
+     const struct ovsrec_interface *iface_rec
+         = shash_find_data(lport_to_iface, binding_rec->logical_port);
+@@ -576,22 +583,11 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
+             }
+         } else if (binding_rec->chassis == chassis_rec) {
+             if (!strcmp(binding_rec->type, "virtual")) {
+-                /* pinctrl module takes care of binding the ports
+-                 * of type 'virtual'.
+-                 * Release such ports if their virtual parents are no
+-                 * longer claimed by this chassis. */
+-                const struct sbrec_port_binding *parent
+-                    = lport_lookup_by_name(sbrec_port_binding_by_name,
+-                                        binding_rec->virtual_parent);
+-                if (!parent || parent->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);
+-                    sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
++                if (*n_vpbs == *n_allocated_vpbs) {
++                    vpbs = x2nrealloc(vpbs, n_allocated_vpbs, sizeof *vpbs);
+                 }
++                vpbs[(*n_vpbs)] = binding_rec;
++                (*n_vpbs)++;
+             } else {
+                 VLOG_INFO("Releasing lport %s from this chassis.",
+                           binding_rec->logical_port);
+@@ -610,6 +606,30 @@ consider_local_datapath(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                          vif_chassis);
+         }
+     }
++    return vpbs;
++}
++
++static void
++consider_local_virtual_port(struct ovsdb_idl_index *sbrec_port_binding_by_name,
++                            const struct sbrec_chassis *chassis_rec,
++                            const struct sbrec_port_binding *binding_rec)
++{
++    /* pinctrl module takes care of binding the ports of type 'virtual'.
++     * Release such ports if their virtual parents are no longer claimed by
++     * this chassis.
++     */
++    const struct sbrec_port_binding *parent =
++        lport_lookup_by_name(sbrec_port_binding_by_name,
++                             binding_rec->virtual_parent);
++    if (!parent || parent->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);
++        sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
++    }
+ }
+ 
+ static void
+@@ -707,20 +727,41 @@ binding_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                             &egress_ifaces);
+     }
+ 
++    /* Array to store pointers to local virtual ports. It is populated by
++     * consider_local_datapath.
++     */
++    const struct sbrec_port_binding **vpbs = NULL;
++    size_t n_vpbs = 0;
++    size_t n_allocated_vpbs = 0;
++
+     /* 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. */
++     * directly connected logical ports and children of those ports.
++     * Virtual ports are just added to vpbs array and will be processed
++     * later. This is special case for virtual ports is needed in order to
++     * make sure we update the virtual_parent port bindings first.
++     */
+     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);
+-
+-    }
++        vpbs =
++            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,
++                                    vpbs, &n_vpbs, &n_allocated_vpbs);
++    }
++
++    /* Now also update the virtual ports in case their parent ports were
++     * updated above.
++     */
++    for (size_t i = 0; i < n_vpbs; i++) {
++        consider_local_virtual_port(sbrec_port_binding_by_name, chassis_rec,
++                                    vpbs[i]);
++    }
++    free(vpbs);
+ 
+     add_ovs_bridge_mappings(ovs_table, bridge_table, &bridge_mappings);
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-controller-Don-t-monitor-connection-table-column.patch b/SOURCES/0001-ovn-controller-Don-t-monitor-connection-table-column.patch
new file mode 100644
index 0000000..f1a544a
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Don-t-monitor-connection-table-column.patch
@@ -0,0 +1,57 @@
+From 179483f38d04680464b5b0972d21ac64654157c7 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Tue, 24 Dec 2019 18:03:20 +0530
+Subject: [PATCH 1/2] ovn-controller: Don't monitor connection table columns
+
+ovn-controller doesn't need to know any changes to the connection
+table row. This patch omits alerts for the Connection table
+columns.
+
+In a large scale deployment like 1000 chassis, this can cause lot of
+CPU cycle wastages as ovsdb-server has to send out updates to all
+the ovn-controller connections.
+
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream commit c80e014d4aaeb47f5967ed8730306fe3c16841c9)
+
+Change-Id: I5ee2ca975fba7e36c0a6e87213edff64e479874a
+---
+ ovn/controller/ovn-controller.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index c8470ce4e..ec6f15783 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -1828,7 +1828,6 @@ main(int argc, char *argv[])
+     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);
+@@ -1836,6 +1835,18 @@ main(int argc, char *argv[])
+     ovsdb_idl_omit(ovnsb_idl_loop.idl,
+                    &sbrec_ha_chassis_group_col_external_ids);
+ 
++    /* We don't want to monitor Connection table at all. So omit all the
++     * columns. */
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_external_ids);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_inactivity_probe);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_is_connected);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_max_backoff);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_other_config);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_read_only);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_role);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_status);
++    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_connection_col_target);
++
+     update_sb_monitors(ovnsb_idl_loop.idl, NULL, NULL, NULL);
+ 
+     stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch b/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch
new file mode 100644
index 0000000..c059281
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch
@@ -0,0 +1,104 @@
+From 6c0f9dbf5d22fe7fdbe9bf2a05d2bed00881cff8 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Tue, 31 Mar 2020 13:47:04 +0200
+Subject: [PATCH] ovn-controller: Fix potential segfault with "virtual"
+ port bindings.
+
+Even though ovn-controller tries to set port_binding->chassis to NULL
+every time port_binding->virtual_parent is set to NULL for bindings of
+type="virtual", there's no way to enforce that an operator doesn't
+manually clear the "virtual_parent" column in the Southbound database.
+
+In such scenario ovn-controller would crash because of trying to
+dereference the NULL port_binding->virtual_parent column.
+
+Add an extra check and release "virtual" port bindings that have
+"virtual_parent" NULL.
+
+Reported-at: https://bugzilla.redhat.com/1818844
+CC: Numan Siddique <nusiddiq@redhat.com>
+Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+(cherry picked from upstream commit 5b3e9879be2b6c9b07ed5c9e073f1c24080a49f7)
+
+Change-Id: Icced69c2871d43316ec06f71f78024939c863225
+---
+ ovn/controller/binding.c | 26 +++++++++++++++-----------
+ tests/ovn.at             | 18 ++++++++++++++++++
+ 2 files changed, 33 insertions(+), 11 deletions(-)
+
+diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
+index 1ae9151..bf7b5ff 100644
+--- a/ovn/controller/binding.c
++++ b/ovn/controller/binding.c
+@@ -614,22 +614,26 @@ consider_local_virtual_port(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                             const struct sbrec_chassis *chassis_rec,
+                             const struct sbrec_port_binding *binding_rec)
+ {
++    if (binding_rec->virtual_parent) {
++        const struct sbrec_port_binding *parent =
++            lport_lookup_by_name(sbrec_port_binding_by_name,
++                                 binding_rec->virtual_parent);
++        if (parent && parent->chassis == chassis_rec) {
++            return;
++        }
++    }
++
+     /* pinctrl module takes care of binding the ports of type 'virtual'.
+      * Release such ports if their virtual parents are no longer claimed by
+      * this chassis.
+      */
+-    const struct sbrec_port_binding *parent =
+-        lport_lookup_by_name(sbrec_port_binding_by_name,
+-                             binding_rec->virtual_parent);
+-    if (!parent || parent->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);
+-        sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
++    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);
++    sbrec_port_binding_set_virtual_parent(binding_rec, NULL);
+ }
+ 
+ static void
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 0cd0985..94e5ecd 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14755,6 +14755,24 @@ AT_CHECK([cat lflows.txt], [0], [dnl
+   table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
+ ])
+ 
++# Forcibly clear virtual_parent. ovn-controller should release the binding
++# gracefully.
++pb_uuid=$(ovn-sbctl --bare --columns _uuid find port_binding logical_port=sw0-vir)
++ovn-sbctl clear port_binding $pb_uuid virtual_parent
++
++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
++logical_port=sw0-vir) = x])
++
++# From sw0-p0 resend GARP for 10.0.0.10. hv1 should reclaim sw0-vir
++# and sw0-p1 should be its virtual_parent.
++send_garp 1 1 $eth_src $eth_dst $spa $tpa
++
++OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
++logical_port=sw0-vir) = x$hv1_ch_uuid], [0], [])
++
++AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \
++logical_port=sw0-vir) = xsw0-p1])
++
+ # From sw0-p3 send GARP for 10.0.0.10. hv1 should claim sw0-vir
+ # and sw0-p3 should be its virtual_parent.
+ eth_src=505400000005
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch b/SOURCES/0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch
new file mode 100644
index 0000000..2c5a5e8
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch
@@ -0,0 +1,292 @@
+From 34b015a4b613329641e9aa4adf0bdd002e3e9177 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Fri, 22 Nov 2019 17:13:21 +0100
+Subject: [PATCH ovn 1/4] ovn-controller: Refactor I-P engine_run() tracking.
+
+This commit simplifies the logic of calling engine_run and engine_need_run in
+order to reduce the number of external variables required to track the result
+of the last engine execution.
+
+The engine code is also refactored a bit and the engine_run() function is
+split in different functions that handle computing/recomputing a node.
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit 2a4965c0e187db0c4218556ed9b06f988e88cb62)
+
+Change-Id: Ib5524493c871794acc166a6cc437b70abd470ed9
+---
+ ovn/controller/ovn-controller.c |  33 ++++++-----
+ ovn/lib/inc-proc-eng.c          | 120 ++++++++++++++++++++++++++--------------
+ ovn/lib/inc-proc-eng.h          |   7 ++-
+ 3 files changed, 103 insertions(+), 57 deletions(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index f2378ab..ad5b067 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -1905,7 +1905,6 @@ main(int argc, char *argv[])
+                              NULL);
+ 
+     uint64_t engine_run_id = 0;
+-    uint64_t old_engine_run_id = 0;
+     bool engine_run_done = true;
+ 
+     unsigned int ovs_cond_seqno = UINT_MAX;
+@@ -1915,10 +1914,11 @@ main(int argc, char *argv[])
+     exiting = false;
+     restart = false;
+     while (!exiting) {
++        engine_run_id++;
++
+         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
+@@ -2010,12 +2010,12 @@ main(int argc, char *argv[])
+                             if (engine_run_done) {
+                                 engine_set_abort_recompute(true);
+                                 engine_run_done = engine_run(&en_flow_output,
+-                                                             ++engine_run_id);
++                                                             engine_run_id);
+                             }
+                         } else {
+                             engine_set_abort_recompute(false);
+                             engine_run_done = true;
+-                            engine_run(&en_flow_output, ++engine_run_id);
++                            engine_run(&en_flow_output, engine_run_id);
+                         }
+                     }
+                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
+@@ -2060,17 +2060,20 @@ main(int argc, char *argv[])
+                 }
+ 
+             }
+-            if (old_engine_run_id == engine_run_id || !engine_run_done) {
+-                if (!engine_run_done || 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);
+-                }
++            if (engine_need_run(&en_flow_output, engine_run_id)) {
++                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 if (!engine_run_done) {
++                VLOG_DBG("engine was aborted, force recompute next time: "
++                         "br_int %p, chassis %p", br_int, chassis);
++                engine_set_force_recompute(true);
++                poll_immediate_wake();
++            } else if (!engine_has_run(&en_flow_output, engine_run_id)) {
++                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);
+             }
+diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c
+index 1064a08..ff07ad9 100644
+--- a/ovn/lib/inc-proc-eng.c
++++ b/ovn/lib/inc-proc-eng.c
+@@ -129,14 +129,68 @@ engine_ovsdb_node_add_index(struct engine_node *node, const char *name,
+ }
+ 
+ bool
+-engine_run(struct engine_node *node, uint64_t run_id)
++engine_has_run(struct engine_node *node, uint64_t run_id)
++{
++    return node->run_id == run_id;
++}
++
++/* Do a full recompute (or at least try). If we're not allowed then
++ * mark the node as "aborted".
++ */
++static bool
++engine_recompute(struct engine_node *node, bool forced, bool allowed)
++{
++    VLOG_DBG("node: %s, recompute (%s)", node->name,
++             forced ? "forced" : "triggered");
++
++    if (!allowed) {
++        VLOG_DBG("node: %s, recompute aborted", node->name);
++        return false;
++    }
++
++    node->run(node);
++    VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
++    return true;
++}
++
++/* Return true if the node could be computed, false otherwise. */
++static bool
++engine_compute(struct engine_node *node, bool recompute_allowed)
++{
++    for (size_t i = 0; i < node->n_inputs; i++) {
++        /* If the input node data changed call its change handler. */
++        if (node->inputs[i].node->changed) {
++            VLOG_DBG("node: %s, handle change for input %s",
++                     node->name, node->inputs[i].node->name);
++
++            /* If the input change can't be handled incrementally, run
++             * the node handler.
++             */
++            if (!node->inputs[i].change_handler(node)) {
++                VLOG_DBG("node: %s, can't handle change for input %s, "
++                         "fall back to recompute",
++                         node->name, node->inputs[i].node->name);
++                return engine_recompute(node, false, recompute_allowed);
++            }
++        }
++    }
++
++    return true;
++}
++
++bool engine_run(struct engine_node *node, uint64_t run_id)
+ {
+     if (node->run_id == run_id) {
++        /* The node was already updated in this run (could be input for
++         * multiple other nodes). Stop processing.
++         */
+         return true;
+     }
+-    node->run_id = run_id;
+ 
++    /* Initialize the node for this run. */
++    node->run_id = run_id;
+     node->changed = false;
++
+     if (!node->n_inputs) {
+         node->run(node);
+         VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
+@@ -150,59 +204,45 @@ engine_run(struct engine_node *node, uint64_t run_id)
+     }
+ 
+     bool need_compute = false;
+-    bool need_recompute = false;
+ 
+     if (engine_force_recompute) {
+-        need_recompute = true;
+-    } else {
+-        for (size_t i = 0; i < node->n_inputs; i++) {
+-            if (node->inputs[i].node->changed) {
+-                need_compute = true;
+-                if (!node->inputs[i].change_handler) {
+-                    need_recompute = true;
+-                    break;
+-                }
+-            }
+-        }
++        return engine_recompute(node, true, !engine_abort_recompute);
+     }
+ 
+-    if (need_recompute) {
+-        VLOG_DBG("node: %s, recompute (%s)", node->name,
+-                 engine_force_recompute ? "forced" : "triggered");
+-        if (engine_abort_recompute) {
+-            VLOG_DBG("node: %s, recompute aborted", node->name);
+-            return false;
+-        }
+-        node->run(node);
+-    } else if (need_compute) {
+-        for (size_t i = 0; i < node->n_inputs; i++) {
+-            if (node->inputs[i].node->changed) {
+-                VLOG_DBG("node: %s, handle change for input %s",
+-                         node->name, node->inputs[i].node->name);
+-                if (!node->inputs[i].change_handler(node)) {
+-                    VLOG_DBG("node: %s, can't handle change for input %s, "
+-                             "fall back to recompute",
+-                             node->name, node->inputs[i].node->name);
+-                    if (engine_abort_recompute) {
+-                        VLOG_DBG("node: %s, recompute aborted", node->name);
+-                        return false;
+-                    }
+-                    node->run(node);
+-                    break;
+-                }
++    /* If any of the inputs updated data but there is no change_handler, then
++     * recompute the current node too.
++     */
++    for (size_t i = 0; i < node->n_inputs; i++) {
++        if (node->inputs[i].node->changed) {
++            need_compute = true;
++
++            /* Trigger a recompute if we don't have a change handler. */
++            if (!node->inputs[i].change_handler) {
++                return engine_recompute(node, false, !engine_abort_recompute);
+             }
+         }
+     }
+ 
++    if (need_compute) {
++        /* If we couldn't compute the node we either aborted or triggered
++         * a full recompute. In any case, stop processing.
++         */
++        return engine_compute(node, !engine_abort_recompute);
++    }
++
+     VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
+     return true;
+ }
+ 
+ bool
+-engine_need_run(struct engine_node *node)
++engine_need_run(struct engine_node *node, uint64_t run_id)
+ {
+     size_t i;
+ 
++    if (node->run_id == run_id) {
++        return false;
++    }
++
+     if (!node->n_inputs) {
+         node->run(node);
+         VLOG_DBG("input node: %s, changed: %d", node->name, node->changed);
+@@ -210,7 +250,7 @@ engine_need_run(struct engine_node *node)
+     }
+ 
+     for (i = 0; i < node->n_inputs; i++) {
+-        if (engine_need_run(node->inputs[i].node)) {
++        if (engine_need_run(node->inputs[i].node, run_id)) {
+             return true;
+         }
+     }
+diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h
+index c3d7b5e..a4151ca 100644
+--- a/ovn/lib/inc-proc-eng.h
++++ b/ovn/lib/inc-proc-eng.h
+@@ -130,9 +130,9 @@ bool engine_run(struct engine_node *, uint64_t run_id);
+  * terminates. */
+ void engine_cleanup(struct engine_node *);
+ 
+-/* Check if engine needs to run, i.e. any change to be processed. */
++/* Check if engine needs to run but didn't. */
+ bool
+-engine_need_run(struct engine_node *);
++engine_need_run(struct engine_node *, uint64_t run_id);
+ 
+ /* Get the input node with <name> for <node> */
+ struct engine_node * engine_get_input(const char *input_name,
+@@ -159,6 +159,9 @@ const struct engine_context * engine_get_context(void);
+ 
+ void engine_set_context(const struct engine_context *);
+ 
++/* Return true if the engine has run for 'node' in the 'run_id' iteration. */
++bool engine_has_run(struct engine_node *node, uint64_t run_id);
++
+ struct ed_ovsdb_index {
+     const char *name;
+     struct ovsdb_idl_index *index;
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch b/SOURCES/0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch
new file mode 100644
index 0000000..d641bb0
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch
@@ -0,0 +1,105 @@
+From ab959077c3dce4bd856f3d6bf633779a41dccdff Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Mon, 9 Dec 2019 09:12:18 +0100
+Subject: [PATCH ovn] ovn-controller: Run I-P engine even when no SB txn is
+ available.
+
+If the last ovn-controller main loop run started a transaction to the
+Southbound DB and the transaction is still in progress, ovn-controller
+will not call engine_run(). In case there were changes to the DB,
+engine_need_run() would return true which would trigger an immediate
+forced recompute in the next processing loop iteration.
+
+However, there are scenarios when updates can be processed incrementally
+even if no Southbound DB transaction is available. One example, often
+seen in the field, is when the MAC_Binding table is updated. Currently
+such an update received while a transaction is still committing would
+trigger a forced recompute.
+
+This patch performs only incremental processing when the SB DB txn
+is NULL, which means executing change_handler() methods
+only, without calling the run() methods of the I-P engine nodes.
+Assuming that some change handlers need to update the DB and that
+some don't, if the SB DB txn is NULL:
+- if the incoming change doesn't trigger a SB DB update (currently true
+  for all existing change handlers) then the change handler might
+  complete without aborting if it doesn't trigger a run() of the node.
+- if the incoming change tries to perform a SB DB update, the change
+  handler should return false (SB DB txn is NULL) triggering the engine
+  to call run() and abort computation.
+
+CC: Han Zhou <hzhou@ovn.org>
+CC: Numan Siddique <numans@ovn.org>
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit e2ab60e3a7c60f3adb8da40e4d1cfeb890d6f80e)
+
+Change-Id: Ifdc87b925fc3e9c8b3c22d9b853ac27f2480f388
+---
+ ovn/controller/ovn-controller.c |  8 ++++++++
+ ovn/lib/inc-proc-eng.h          | 15 ++++++++++++++-
+ 2 files changed, 22 insertions(+), 1 deletion(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index cb23bc8..c8470ce 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -2059,6 +2059,14 @@ main(int argc, char *argv[])
+                         } else {
+                             engine_run(true);
+                         }
++                    } else {
++                        /* Even if there's no SB DB transaction available,
++                         * try to run the engine so that we can handle any
++                         * incremental changes that don't require a recompute.
++                         * If a recompute is required, the engine will abort,
++                         * triggerring a full run in the next iteration.
++                         */
++                        engine_run(false);
+                     }
+                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
+                                    time_msec());
+diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h
+index 9cc99a3..c62f3bc 100644
+--- a/ovn/lib/inc-proc-eng.h
++++ b/ovn/lib/inc-proc-eng.h
+@@ -84,6 +84,10 @@ struct engine_node_input {
+      * evaluated against all the other inputs. Returns:
+      *  - true: if change can be handled
+      *  - false: if change cannot be handled (indicating full recompute needed)
++     * A change handler can also call engine_get_context() but it must make
++     * sure the txn pointers returned by it are non-NULL. In case the change
++     * handler needs to use the txn pointers returned by engine_get_context(),
++     * and the pointers are NULL, the change handler MUST return false.
+      */
+     bool (*change_handler)(struct engine_node *node, void *data);
+ };
+@@ -133,6 +137,9 @@ struct engine_node {
+ 
+     /* Fully processes all inputs of this node and regenerates the data
+      * of this node. The pointer to the node's data is passed as argument.
++     * 'run' handlers can also call engine_get_context() and the
++     * implementation guarantees that the txn pointers returned
++     * engine_get_context() are not NULL and valid.
+      */
+     void (*run)(struct engine_node *node, void *data);
+ 
+@@ -189,7 +196,13 @@ void engine_add_input(struct engine_node *node, struct engine_node *input,
+  * iteration, and the change can't be tracked across iterations */
+ void engine_set_force_recompute(bool val);
+ 
+-const struct engine_context * engine_get_context(void);
++/* Return the current engine_context. The values in the context can be NULL
++ * if the engine is run with allow_recompute == false in the current
++ * iteration.
++ * Therefore, it is the responsibility of the caller to check the context
++ * values when called from change handlers.
++ */
++const struct engine_context *engine_get_context(void);
+ 
+ void engine_set_context(const struct engine_context *);
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch b/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch
new file mode 100644
index 0000000..83769dd
--- /dev/null
+++ b/SOURCES/0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch
@@ -0,0 +1,90 @@
+From 33fae9ee62a0f3c1ff951cde90d7330650681c32 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 2 Apr 2020 10:35:32 +0200
+Subject: [PATCH] ovn-controller: Skip vport bindings done through OVS
+ external_ids:iface-id.
+
+Port bindings of type "virtual" should not have an associated OVS port
+in the integration bridge. If this is the case, it's a misconfig and
+ovn-controller should ignore it.
+
+If such a situation is detected, ovn-controller will also log a warning
+message to inform the user about the wrong configuration.
+
+Reported-at: https://bugzilla.redhat.com/1818844
+CC: Numan Siddique <nusiddiq@redhat.com>
+Fixes: 054f4c85c413 ("Add a new logical switch port type - 'virtual'")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+(cherry picked from upstream commit 523b1f5f45682bd6dd454281a97a09c3f429c457)
+
+Change-Id: I65979e1f218f02dd24ba8625e516b766a03fd081
+---
+ ovn/controller/binding.c | 12 ++++++++++++
+ tests/ovn.at             | 20 ++++++++++++++++++++
+ 2 files changed, 32 insertions(+)
+
+diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
+index bf7b5ff..c71731a 100644
+--- a/ovn/controller/binding.c
++++ b/ovn/controller/binding.c
+@@ -436,6 +436,18 @@ is_our_chassis(const struct sbrec_chassis *chassis_rec,
+     const struct ovsrec_interface *iface_rec
+         = shash_find_data(lport_to_iface, binding_rec->logical_port);
+ 
++    /* Ports of type "virtual" should never be explicitly bound to an OVS
++     * port in the integration bridge. If that's the case, ignore the binding
++     * and log a warning.
++     */
++    if (iface_rec && !strcmp(binding_rec->type, "virtual")) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
++        VLOG_WARN_RL(&rl,
++                     "Virtual port %s should not be bound to OVS port %s",
++                     binding_rec->logical_port, iface_rec->name);
++        return false;
++    }
++
+     bool our_chassis = false;
+     if (iface_rec
+         || (binding_rec->parent_port && binding_rec->parent_port[0] &&
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 94e5ecd..d74a8b0 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14639,6 +14639,11 @@ ovs-vsctl -- add-port br-int hv1-vif2 -- \
+     options:tx_pcap=hv1/vif2-tx.pcap \
+     options:rxq_pcap=hv1/vif2-rx.pcap \
+     ofport-request=2
++ovs-vsctl -- add-port br-int hv1-vif3 -- \
++    set interface hv1-vif3 \
++    options:tx_pcap=hv1/vif3-tx.pcap \
++    options:rxq_pcap=hv1/vif3-rx.pcap \
++    ofport-request=3
+ 
+ sim_add hv2
+ as hv2
+@@ -14732,6 +14737,21 @@ logical_port=sw0-vir) = x], [0], [])
+ AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \
+ logical_port=sw0-vir) = x])
+ 
++# Try to bind sw0-vir directly to an OVS port. This should be ignored by
++# ovn-controller.
++as hv1
++ovs-vsctl set interface hv1-vif3 external-ids:iface-id=sw0-vir
++
++AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding \
++logical_port=sw0-vir) = x], [0], [])
++
++# Cleanup hv1-vif3.
++as hv1
++ovs-vsctl del-port hv1-vif3
++
++AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding \
++logical_port=sw0-vir) = x], [0], [])
++
+ # From sw0-p0 send GARP for 10.0.0.10. hv1 should claim sw0-vir
+ # and sw0-p1 should be its virtual_parent.
+ eth_src=505400000003
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch b/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch
new file mode 100644
index 0000000..d0eb4d4
--- /dev/null
+++ b/SOURCES/0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch
@@ -0,0 +1,169 @@
+From b58d7ac109938ceb88ff4d7197a94c6c2af38c63 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Mon, 17 Feb 2020 11:23:45 +0530
+Subject: [PATCH] ovn-ctl: Provide the option to configure inactive probe from
+ standby to active.
+
+Recently ovsdb-server supported an unixctl command -
+ovsdb-server/set-active-ovsdb-server-probe-interval to configure inactive probe
+interval from standby connection to the active. This patch provides the
+option to configure this from ovn-ctl and the pacemaker OVN OCF script.
+
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Acked-by: Han Zhou <hzhou@ovn.org>
+
+(cherry-picked from upstream OVN repo commit 9991e91e7d592cef533b57af4405405ed5b41866)
+Conflicts:
+	ovn/utilities/ovn-ctl
+
+Change-Id: I83a8f6e8d86836ed31946efc4aad5c07027f6d08
+---
+ ovn/utilities/ovn-ctl           |  6 ++++++
+ ovn/utilities/ovn-ctl.8.xml     |  8 ++++++--
+ ovn/utilities/ovndb-servers.ocf | 23 +++++++++++++++++++----
+ 3 files changed, 31 insertions(+), 6 deletions(-)
+
+diff --git a/ovn/utilities/ovn-ctl b/ovn/utilities/ovn-ctl
+index 65f03e28d..e2c5e5b0e 100755
+--- a/ovn/utilities/ovn-ctl
++++ b/ovn/utilities/ovn-ctl
+@@ -63,6 +63,7 @@ demote_ovnnb() {
+     if test -e $ovnnb_active_conf_file; then
+         ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/set-active-ovsdb-server `cat $ovnnb_active_conf_file`
+         ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/connect-active-ovsdb-server
++        ovs-appctl -t $OVN_RUNDIR/ovnnb_db.ctl ovsdb-server/set-active-ovsdb-server-probe-interval $DB_NB_PROBE_INTERVAL_TO_ACTIVE
+     else
+         echo >&2 "$0: active server details not set"
+         exit 1
+@@ -77,6 +78,7 @@ demote_ovnsb() {
+     if test -e $ovnsb_active_conf_file; then
+         ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/set-active-ovsdb-server `cat $ovnsb_active_conf_file`
+         ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/connect-active-ovsdb-server
++        ovs-appctl -t $OVN_RUNDIR/ovnsb_db.ctl ovsdb-server/set-active-ovsdb-server-probe-interval $DB_SB_PROBE_INTERVAL_TO_ACTIVE
+     else
+         echo >&2 "$0: active server details not set"
+         exit 1
+@@ -454,6 +456,7 @@ set_defaults () {
+     DB_NB_SYNC_FROM_PROTO=tcp
+     DB_NB_SYNC_FROM_ADDR=
+     DB_NB_SYNC_FROM_PORT=6641
++    DB_NB_PROBE_INTERVAL_TO_ACTIVE=60000
+ 
+     DB_SB_SOCK=$OVN_RUNDIR/ovnsb_db.sock
+     DB_SB_PID=$OVN_RUNDIR/ovnsb_db.pid
+@@ -463,6 +466,7 @@ set_defaults () {
+     DB_SB_SYNC_FROM_PROTO=tcp
+     DB_SB_SYNC_FROM_ADDR=
+     DB_SB_SYNC_FROM_PORT=6642
++    DB_SB_PROBE_INTERVAL_TO_ACTIVE=60000
+ 
+     DB_NB_SCHEMA=$datadir/ovn-nb.ovsschema
+     DB_SB_SCHEMA=$datadir/ovn-sb.ovsschema
+@@ -628,10 +632,12 @@ File location options:
+   --db-nb-sync-from-port=PORT OVN Northbound active db tcp port (default: $DB_NB_SYNC_FROM_PORT)
+   --db-nb-sync-from-proto=PROTO OVN Northbound active db transport (default: $DB_NB_SYNC_FROM_PROTO)
+   --db-nb-create-insecure-remote=yes|no Create ptcp OVN Northbound remote (default: $DB_NB_CREATE_INSECURE_REMOTE)
++  --db-nb-probe-interval-to-active Active probe interval from standby to active ovsdb-server remote (default: $DB_NB_PROBE_INTERVAL_TO_ACTIVE)
+   --db-sb-sync-from-addr=ADDR OVN Southbound active db tcp address (default: $DB_SB_SYNC_FROM_ADDR)
+   --db-sb-sync-from-port=ADDR OVN Southbound active db tcp port (default: $DB_SB_SYNC_FROM_PORT)
+   --db-sb-sync-from-proto=PROTO OVN Southbound active db transport (default: $DB_SB_SYNC_FROM_PROTO)
+   --db-sb-create-insecure-remote=yes|no Create ptcp OVN Southbound remote (default: $DB_SB_CREATE_INSECURE_REMOTE)
++  --db-sb-probe-interval-to-active Active probe interval from standby to active ovsdb-server remote (default: $DB_SB_PROBE_INTERVAL_TO_ACTIVE)
+   --db-nb-cluster-local-addr=ADDR OVN_Northbound cluster local address \
+   (default: $DB_NB_CLUSTER_LOCAL_ADDR)
+   --db-nb-cluster-local-port=PORT OVN_Northbound cluster local tcp port \
+diff --git a/ovn/utilities/ovn-ctl.8.xml b/ovn/utilities/ovn-ctl.8.xml
+index c5294d794..f0b3d6c87 100644
+--- a/ovn/utilities/ovn-ctl.8.xml
++++ b/ovn/utilities/ovn-ctl.8.xml
+@@ -94,6 +94,10 @@
+     <p><code>--db-sb-cluster-remote-port=<var>PORT NUMBER</var></code></p>
+     <p><code>--db-sb-cluster-remote-proto=<var>PROTO (tcp/ssl)</var></code></p>
+ 
++    <h1> Probe interval options </h1>
++    <p><code>--db-nb-probe-interval-to-active=<var>Time in milliseconds</var></code></p>
++    <p><code>--db-sb-probe-interval-to-active=<var>Time in milliseconds</var></code></p>
++
+     <h1>Configuration files</h1>
+     <p>Following are the optional configuration files. If present, it should be located in the etc dir</p>
+ 
+@@ -150,8 +154,8 @@
+     <h2>Promote and demote ovsdb servers</h2>
+     <p><code># ovn-ctl promote_ovnnb</code></p>
+     <p><code># ovn-ctl promote_ovnsb</code></p>
+-    <p><code># ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 demote_ovnnb</code></p>
+-    <p><code># ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 demote_ovnsb</code></p>
++    <p><code># ovn-ctl --db-nb-sync-from-addr=x.x.x.x --db-nb-sync-from-port=6641 --db-nb-probe-interval-to-active=60000 demote_ovnnb</code></p>
++    <p><code># ovn-ctl --db-sb-sync-from-addr=x.x.x.x --db-sb-sync-from-port=6642 --db-sb-probe-interval-to-active=60000 demote_ovnsb</code></p>
+ 
+     <h2>Creating a clustered db on 3 nodes with IPs x.x.x.x, y.y.y.y and z.z.z.z</h2>
+     <h3>Starting OVN ovsdb servers and ovn-northd on the node with IP x.x.x.x</h3>
+diff --git a/ovn/utilities/ovndb-servers.ocf b/ovn/utilities/ovndb-servers.ocf
+index cd4742668..1737b192e 100755
+--- a/ovn/utilities/ovndb-servers.ocf
++++ b/ovn/utilities/ovndb-servers.ocf
+@@ -9,6 +9,7 @@
+ : ${SB_MASTER_PROTO_DEFAULT="tcp"}
+ : ${MANAGE_NORTHD_DEFAULT="no"}
+ : ${INACTIVE_PROBE_DEFAULT="5000"}
++: ${INACTIVE_PROBE_TO_MASTER_DEFAULT="60000"}
+ : ${LISTEN_ON_MASTER_IP_ONLY_DEFAULT="yes"}
+ : ${NB_SSL_KEY_DEFAULT="/etc/openvswitch/ovnnb-privkey.pem"}
+ : ${NB_SSL_CERT_DEFAULT="/etc/openvswitch/ovnnb-cert.pem"}
+@@ -27,6 +28,7 @@ SB_MASTER_PORT=${OCF_RESKEY_sb_master_port:-${SB_MASTER_PORT_DEFAULT}}
+ SB_MASTER_PROTO=${OCF_RESKEY_sb_master_protocol:-${SB_MASTER_PROTO_DEFAULT}}
+ MANAGE_NORTHD=${OCF_RESKEY_manage_northd:-${MANAGE_NORTHD_DEFAULT}}
+ INACTIVE_PROBE=${OCF_RESKEY_inactive_probe_interval:-${INACTIVE_PROBE_DEFAULT}}
++INACTIVE_PROBE_TO_MASTER=${OCF_RESKEY_inactive_probe_interval_to_master:-${INACTIVE_PROBE_TO_MASTER_DEFAULT}}
+ NB_PRIVKEY=${OCF_RESKEY_ovn_nb_db_privkey:-${NB_SSL_KEY_DEFAULT}}
+ NB_CERT=${OCF_RESKEY_ovn_nb_db_cert:-${NB_SSL_CERT_DEFAULT}}
+ NB_CACERT=${OCF_RESKEY_ovn_nb_db_cacert:-${NB_SSL_CACERT_DEFAULT}}
+@@ -135,6 +137,15 @@ ovsdb_server_metadata() {
+   <content type="string" />
+   </parameter>
+ 
++  <parameter name="inactive_probe_interval_to_master" unique="1">
++  <longdesc lang="en">
++  Inactive probe interval to use for the connection from standby
++  ovsdb-server to master ovsdb-server.
++  </longdesc>
++  <shortdesc lang="en">Set inactive probe interval to master</shortdesc>
++  <content type="string" />
++  </parameter>
++
+   <parameter name="listen_on_master_ip_only" unique="1">
+   <longdesc lang="en">
+   If set to yes, the OVNDBs will listen on master IP. Otherwise, it will
+@@ -266,10 +277,12 @@ inactivity_probe=$INACTIVE_PROBE -- set SB_Global . connections=@conn_uuid
+         ocf_log debug "ovndb_server: Connecting to the new master ${OCF_RESKEY_CRM_meta_notify_promote_uname}"
+         ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \
+                                 --db-nb-sync-from-port=${NB_MASTER_PORT} \
+-                                --db-nb-sync-from-proto=${NB_MASTER_PROTO}
++                                --db-nb-sync-from-proto=${NB_MASTER_PROTO} \
++                                --db-nb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
+         ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \
+                                 --db-sb-sync-from-port=${SB_MASTER_PORT} \
+-                                --db-sb-sync-from-proto=${SB_MASTER_PROTO}
++                                --db-sb-sync-from-proto=${SB_MASTER_PROTO} \
++                                --db-sb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
+     fi
+ }
+ 
+@@ -596,10 +609,12 @@ ovsdb_server_demote() {
+         # being demoted. Sync to the surviving one
+         ${OVN_CTL} demote_ovnnb --db-nb-sync-from-addr=${MASTER_IP} \
+                                 --db-nb-sync-from-port=${NB_MASTER_PORT} \
+-                                --db-nb-sync-from-proto=${NB_MASTER_PROTO}
++                                --db-nb-sync-from-proto=${NB_MASTER_PROTO} \
++                                --db-nb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
+         ${OVN_CTL} demote_ovnsb --db-sb-sync-from-addr=${MASTER_IP} \
+                                 --db-sb-sync-from-port=${SB_MASTER_PORT} \
+-                                --db-sb-sync-from-proto=${SB_MASTER_PROTO}
++                                --db-sb-sync-from-proto=${SB_MASTER_PROTO} \
++                                --db-sb-probe-interval-to-active=${INACTIVE_PROBE_TO_MASTER}
+ 
+     else
+         # For completeness, should never be called
+-- 
+2.25.1
+
diff --git a/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch b/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch
new file mode 100644
index 0000000..e9204b7
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch
@@ -0,0 +1,194 @@
+From 35a77738b9032480c3983ff93080cd1cada85fad Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Thu, 12 Mar 2020 15:58:38 +0530
+Subject: [PATCH] ovn-northd: Add lflows to by pass the svc monitor packets
+ from conntrack.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The commit [1] added lflows to by pass the service monitor health check
+packets from conntrack. But it missed out adding in the ingress pre_acl
+and egress pre_acl of logical switch pipeline.
+
+This patch adds these missing lflows. It also enhanced the system lb health
+check tests to add the acls to test this scenario.
+
+[1] - bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline)
+Fixes: bb9f2b9ce56c("ovn-northd: Consider load balancer active backends in router pipeline)
+
+Change-Id: I591f98dbb945ea63acf0d1f37f0bb09f43e205c7
+Reported-by: Maciej Józefczyk <mjozefcz@redhat.com>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Maciej Jozefczyk <mjozefcz@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml | 22 +++++++++++++++++++++-
+ ovn/northd/ovn-northd.c     | 15 ++++++++++++++-
+ tests/ovn.at                | 22 ++++++++++++++++++++++
+ tests/system-ovn.at         | 22 ++++++++++++++++++++++
+ 4 files changed, 79 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index c09e5ff44..f4d6c8f08 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -283,6 +283,16 @@
+       priority-110 flow is added to skip over stateful ACLs.
+     </p>
+ 
++    <p>
++      This table also has a priority-110 flow with the match
++      <code>eth.dst == <var>E</var></code> for all logical switch
++      datapaths to move traffic to the next table. Where <var>E</var>
++      is the service monitor mac defined in the
++      <ref column="options:svc_monitor_mac" table="NB_Global"
++      db="OVN_Northbound"/> colum of <ref table="NB_Global"
++      db="OVN_Northbound"/> table.
++    </p>
++
+     <h3>Ingress Table 4: Pre-LB</h3>
+ 
+     <p>
+@@ -310,7 +320,7 @@
+ 
+     <p>
+       This table also has a priority-110 flow with the match
+-      <code>eth.src == <var>E</var></code> for all logical switch
++      <code>eth.dst == <var>E</var></code> for all logical switch
+       datapaths to move traffic to the next table. Where <var>E</var>
+       is the service monitor mac defined in the
+       <ref column="options:svc_monitor_mac" table="NB_Global"
+@@ -1195,6 +1205,16 @@ output;
+      <code>to-lport</code> traffic.
+     </p>
+ 
++    <p>
++      This table also has a priority-110 flow with the match
++      <code>eth.src == <var>E</var></code> for all logical switch
++      datapaths to move traffic to the next table. Where <var>E</var>
++      is the service monitor mac defined in the
++      <ref column="options:svc_monitor_mac" table="NB_Global"
++      db="OVN_Northbound"/> colum of <ref table="NB_Global"
++      db="OVN_Northbound"/> table.
++    </p>
++
+     <h3>Egress Table 2: Pre-stateful</h3>
+ 
+     <p>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index abe5508c3..c2ac5ce07 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -4502,6 +4502,16 @@ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 0, "1", "next;");
+     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 0, "1", "next;");
+ 
++    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
++    ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_ACL, 110, svc_check_match,
++                  "next;");
++    free(svc_check_match);
++
++    svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
++    ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_ACL, 110, svc_check_match,
++                  "next;");
++    free(svc_check_match);
++
+     /* If there are any stateful ACL rules in this datapath, we must
+      * send all IP packets through the conntrack action, which handles
+      * defragmentation, in order to match L4 headers. */
+@@ -4672,9 +4682,12 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+                   "nd || nd_rs || nd_ra", "next;");
+ 
+     /* Do not send service monitor packets to conntrack. */
+-    char *svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
++    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
+                   svc_check_match, "next;");
++    free(svc_check_match);
++
++    svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
+     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
+                   svc_check_match, "next;");
+     free(svc_check_match);
+diff --git a/tests/ovn.at b/tests/ovn.at
+index dd94776e6..9fb6e4f98 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -17032,12 +17032,34 @@ ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
+ ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+ ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+ 
++# Create port group and ACLs for sw0 ports.
++ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2
++ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
++ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
++
++ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
++ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
++
+ # Create the second logical switch with one port
+ ovn-nbctl ls-add sw1
+ ovn-nbctl lsp-add sw1 sw1-p1
+ ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
+ ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
+ 
++# Create port group and ACLs for sw1 ports.
++ovn-nbctl pg-add pg1_drop sw1-p1
++ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop
++ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop
++
++ovn-nbctl pg-add pg1 sw1-p1
++ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip4" allow-related
++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
++
+ # Create a logical router and attach both logical switches
+ ovn-nbctl lr-add lr0
+ ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+diff --git a/tests/system-ovn.at b/tests/system-ovn.at
+index 7d1c65d85..22e9a5926 100644
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -2552,12 +2552,34 @@ ovn-nbctl lsp-add sw0 sw0-p2
+ ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+ ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+ 
++# Create port group and ACLs for sw0 ports.
++ovn-nbctl pg-add pg0_drop sw0-p1 sw0-p2
++ovn-nbctl acl-add pg0_drop from-lport 1001 "inport == @pg0_drop && ip" drop
++ovn-nbctl acl-add pg0_drop to-lport 1001 "outport == @pg0_drop && ip" drop
++
++ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
++ovn-nbctl acl-add pg0 from-lport 1002 "inport == @pg0 && ip4" allow-related
++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
++ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
++
+ # Create the second logical switch with one port
+ ovn-nbctl ls-add sw1
+ ovn-nbctl lsp-add sw1 sw1-p1
+ ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
+ ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
+ 
++# Create port group and ACLs for sw1 ports.
++ovn-nbctl pg-add pg1_drop sw1-p1
++ovn-nbctl acl-add pg1_drop from-lport 1001 "inport == @pg1_drop && ip" drop
++ovn-nbctl acl-add pg1_drop to-lport 1001 "outport == @pg1_drop && ip" drop
++
++ovn-nbctl pg-add pg1 sw1-p1
++ovn-nbctl acl-add pg1 from-lport 1002 "inport == @pg1 && ip4" allow-related
++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && icmp4" allow-related
++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && tcp && tcp.dst == 80" allow-related
++ovn-nbctl acl-add pg1 to-lport 1002 "outport == @pg1 && ip4 && ip4.src == 0.0.0.0/0 && udp && udp.dst == 80" allow-related
++
+ # Create a logical router and attach both logical switches
+ ovn-nbctl lr-add lr0
+ ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch b/SOURCES/0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch
new file mode 100644
index 0000000..4affee2
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch
@@ -0,0 +1,700 @@
+From afab54226927711c719e8012c43333273f581ac7 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Sat, 1 Feb 2020 16:23:43 +0530
+Subject: [PATCH] ovn-northd: Address scale issues with DNAT flows.
+
+When the commit [1] added Distributed NAT support in OVN, it didn't address
+the requirement of making East/West NAT traffic distributed. The E/W NAT
+traffic was still centralized. Later a couple of patches [2], addressed this
+requirement. But the approach taken in [2] resulted in a lot of logical flows
+as number of dnat_and_snat entries increase, as reported in @Reported-at.
+
+This patch
+  - reverts the approch taken in [2].
+  - removing the flows which does the NAT direct (REGBIT_NAT_REDIRECT) to
+    the gateway chassis.
+  - and to solve the E/W centralized NAT it does the following:
+     * Since for each NAT entry we know the MAC binding to be used for the
+       external_ip - either the external_mac if set or the MAC of the
+       distributed gateway router port, this patch adds the flows in the
+       S_ROUTER_IN_ARP_RESOLVE stage to set the eth.dst to the MAC if the
+       IP destination is external_ip.
+     * The existing flows in the S_ROUTER_OUT_EGR_LOOP are now added by additional
+       match -  is_chassis_resident('P') - where 'P' is logical_port of the NAT entry
+       if set, otherwise it is the chassis resident port of distributed router port.
+       With this additional match, the packet will be loopbacked to apply the unSNAT/DNAT
+       rules on the relevant chassis.
+
+Suppose if a logical port 'P' with IP 'A' has a dnat_and_snat entry with external_mac/logical_port
+set, and if the packet's IP destination is one of the DNAT IP - then the packet will be sent out
+of the local chassis, since eth.dst is resolved in the S_ROUTER_IN_ARP_RESOLVE stage.
+If the external_mac/logical_port is not in NAT entry, then the packet will be redirected to
+the gateway chassis.
+
+With this patch, for the logical resource reported in @Reported-at, the number of logical
+flows come down to around 45k from 650k.
+
+[1] - ceacd9d49316("ovn: distributed NAT flows")
+
+[2] - 551e3d989557("OVN: fix DVR Floating IP support")
+      8244c6b6bd88("OVN: do not distribute traffic for local FIP")
+
+Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-January/049714.html
+Reported-by: Daniel Alvarez Sanchez <dalvarez@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Tested-By: Daniel Alvarez Sanchez <dalvarez@redhat.com>
+Acked-By: Daniel Alvarez Sanchez <dalvarez@redhat.com>
+
+(cherry-picked from upstream commit 2dc7869436de32205f60128172196b3a207ab265)
+Conflicts:
+	ovn/northd/ovn-northd.c
+
+Change-Id: I7684c7f5114ba7c800293e843d5d4b856dedbb96
+---
+ ovn/northd/ovn-northd.8.xml | 191 ++++++++------------------
+ ovn/northd/ovn-northd.c     | 263 +++++-------------------------------
+ tests/ovn-northd.at         |   8 +-
+ 3 files changed, 98 insertions(+), 364 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index d94d9aef9..a42a67c19 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1487,6 +1487,24 @@ next;
+     </p>
+ 
+     <ul>
++      <li>
++        <p>
++          For each NAT entry of a distributed logical router  (with
++          distributed gateway router port) of type <code>snat</code>,
++          a priorirty-120 flow with the match <code>inport == <var>P</var>
++          &amp;&amp; ip4.src == <var>A</var></code> advances the packet to
++          the next pipeline, where <var>P</var> is the distributed logical
++          router port and <var>A</var> is the <code>external_ip</code> set
++          in the NAT entry. If <var>A</var> is an IPv6 address, then
++          <code>ip6.src</code> is used for the match.
++        </p>
++
++        <p>
++          The above flow is required to handle the routing of the East/west NAT
++          traffic.
++        </p>
++      </li>
++
+       <li>
+         <p>
+           L3 admission control: A priority-100 flow drops packets that match
+@@ -1977,21 +1995,6 @@ icmp6 {
+           <code>redirect-chassis</code>.
+         </p>
+ 
+-        <p>
+-          For each configuration in the OVN Northbound database, that asks
+-          to change the source IP address of a packet from <var>A</var> to
+-          <var>B</var>, a priority-50 flow matches
+-          <code>ip &amp;&amp; ip4.dst == <var>B</var></code> or
+-          <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
+-          with an action
+-          <code>REGBIT_NAT_REDIRECT = 1; next;</code>.  This flow is for
+-          east/west traffic to a NAT destination IPv4/IPv6 address.  By
+-          setting the <code>REGBIT_NAT_REDIRECT</code> flag, in the
+-          ingress table <code>Gateway Redirect</code> this will trigger a
+-          redirect to the instance of the gateway port on the
+-          <code>redirect-chassis</code>.
+-        </p>
+-
+         <p>
+           A priority-0 logical flow with match <code>1</code> has actions
+           <code>next;</code>.
+@@ -2147,20 +2150,6 @@ icmp6 {
+           <code>redirect-chassis</code>.
+         </p>
+ 
+-        <p>
+-          For each configuration in the OVN Northbound database, that asks
+-          to change the destination IP address of a packet from <var>A</var> to
+-          <var>B</var>, a priority-50 flow matches <code>ip &amp;&amp;
+-          ip4.dst == <var>B</var></code> or <code>ip &amp;&amp;
+-          ip6.dst == <var>B</var></code> with an action
+-          <code>REGBIT_NAT_REDIRECT = 1; next;</code>.  This flow is for
+-          east/west traffic to a NAT destination IPv4/IPv6 address.  By
+-          setting the <code>REGBIT_NAT_REDIRECT</code> flag, in the
+-          ingress table <code>Gateway Redirect</code> this will trigger a
+-          redirect to the instance of the gateway port on the
+-          <code>redirect-chassis</code>.
+-        </p>
+-
+         <p>
+           A priority-0 logical flow with match <code>1</code> has actions
+           <code>next;</code>.
+@@ -2285,54 +2274,6 @@ output;
+         </p>
+       </li>
+ 
+-      <li>
+-        <p>
+-          For distributed logical routers where one of the logical router
+-          ports specifies a <code>redirect-chassis</code>, a priority-400
+-          logical flow for each ip source/destination couple that matches the
+-          <code>dnat_and_snat</code> NAT rules configured. These flows will
+-          allow to properly forward traffic to the external connections if
+-          available and avoid sending it through the tunnel.
+-          Assuming the two following NAT rules have been configured:
+-        </p>
+-
+-        <pre>
+-external_ip{0,1} = <var>EIP{0,1}</var>;
+-external_mac{0,1} = <var>MAC{0,1}</var>;
+-logical_ip{0,1} = <var>LIP{0,1}</var>;
+-        </pre>
+-
+-        <p>
+-            the following action will be applied:
+-        </p>
+-
+-        <pre>
+-eth.dst = <var>MAC0</var>;
+-eth.src = <var>MAC1</var>;
+-reg0 = ip4.dst; /* xxreg0 = ip6.dst; in the IPv6 case */
+-reg1 = <var>EIP1</var>; /* xxreg1 in the IPv6 case */
+-outport = <code>redirect-chassis-port</code>;
+-<code>REGBIT_DISTRIBUTED_NAT = 1; next;</code>.
+-        </pre>
+-
+-        <p>
+-            Morover a priority-400 logical flow is configured for each
+-            <code>dnat_and_snat</code> NAT rule configured in order to
+-            not send traffic for local FIP through the overlay tunnels
+-            but manage it in the local hypervisor
+-        </p>
+-      </li>
+-
+-      <li>
+-        <p>
+-          For distributed logical routers where one of the logical router
+-          ports specifies a <code>redirect-chassis</code>, a priority-300
+-          logical flow with match <code>REGBIT_NAT_REDIRECT == 1</code> has
+-          actions <code>ip.ttl--; next;</code>.  The <code>outport</code>
+-          will be set later in the Gateway Redirect table.
+-        </p>
+-      </li>
+-
+       <li>
+         <p>
+           IPv4 routing table.  For each route to IPv4 network <var>N</var> with
+@@ -2427,23 +2368,6 @@ next;
+         </p>
+       </li>
+ 
+-      <li>
+-        <p>
+-          For distributed logical routers where one of the logical router
+-          ports specifies a <code>redirect-chassis</code>, a priority-400
+-          logical flow with match <code>REGBIT_DISTRIBUTED_NAT == 1</code>
+-          has action <code>next;</code>
+-        </p>
+-        <p>
+-          For distributed logical routers where one of the logical router
+-          ports specifies a <code>redirect-chassis</code>, a priority-200
+-          logical flow with match <code>REGBIT_NAT_REDIRECT == 1</code> has
+-          actions <code>eth.dst = <var>E</var>; next;</code>, where
+-          <var>E</var> is the ethernet address of the router's distributed
+-          gateway port.
+-        </p>
+-      </li>
+-
+       <li>
+         <p>
+           Static MAC bindings.  MAC bindings can be known statically based on
+@@ -2518,6 +2442,35 @@ next;
+         </p>
+       </li>
+ 
++      <li>
++        <p>
++          Static MAC bindings from NAT entries.  MAC bindings can also be known
++          for the entries in the <code>NAT</code> table. Below flows are
++          programmed for distributed logical routers i.e with a distributed
++          router port.
++        </p>
++
++        <p>
++          For each row in the <code>NAT</code> table with IPv4 address
++          <var>A</var> in the <ref column="external_ip"
++          table="NAT" db="OVN_Northbound"/> column of
++          <ref table="NAT" db="OVN_Northbound"/> table, a priority-100
++          flow with the match <code>outport === <var>P</var> &amp;&amp;
++          reg0 == <var>A</var></code> has actions <code>eth.dst = <var>E</var>;
++          next;</code>, where <code>P</code> is the distributed logical router
++          port, <var>E</var> is the Ethernet address if set in the
++          <ref column="external_mac" table="NAT" db="OVN_Northbound"/> column
++          of <ref table="NAT" db="OVN_Northbound"/> table for of type
++          <code>dnat_and_snat</code>, otherwise the Ethernet address of the
++          distributed logical router port.
++        </p>
++
++        <p>
++          For IPv6 NAT entries, same flows are added, but using the register
++          <code>xxreg0</code> for the match.
++        </p>
++      </li>
++
+       <li>
+         <p>
+           Dynamic MAC bindings.  These flows resolve MAC-to-IP bindings
+@@ -2640,20 +2593,6 @@ icmp4 {
+     </p>
+ 
+     <ul>
+-      <li>
+-        A priority-300 logical flow with match
+-        <code>REGBIT_DISTRIBUTED_NAT == 1</code> has action
+-        <code>next;</code>
+-      </li>
+-      <li>
+-        A priority-200 logical flow with match
+-        <code>REGBIT_NAT_REDIRECT == 1</code> has actions
+-        <code>outport = <var>CR</var>; next;</code>, where <var>CR</var>
+-        is the <code>chassisredirect</code> port representing the instance
+-        of the logical router distributed gateway port on the
+-        <code>redirect-chassis</code>.
+-      </li>
+-
+       <li>
+         A priority-150 logical flow with match
+         <code>outport == <var>GW</var> &amp;&amp;
+@@ -2945,19 +2884,6 @@ nd_ns {
+       ports specifies a <code>redirect-chassis</code>.
+     </p>
+ 
+-    <p>
+-      Earlier in the ingress pipeline, some east-west traffic was
+-      redirected to the <code>chassisredirect</code> port, based on
+-      flows in the <code>UNSNAT</code> and <code>DNAT</code> ingress
+-      tables setting the <code>REGBIT_NAT_REDIRECT</code> flag, which
+-      then triggered a match to a flow in the
+-      <code>Gateway Redirect</code> ingress table.  The intention was
+-      not to actually send traffic out the distributed gateway port
+-      instance on the <code>redirect-chassis</code>.  This traffic was
+-      sent to the distributed gateway port instance in order for DNAT
+-      and/or SNAT processing to be applied.
+-    </p>
+-
+     <p>
+       While UNDNAT and SNAT processing have already occurred by this
+       point, this traffic needs to be forced through egress loopback on
+@@ -2973,23 +2899,20 @@ nd_ns {
+ 
+     <ul>
+       <li>
+-        <p>
+-          For each <code>dnat_and_snat</code> NAT rule couple in the
+-          OVN Northbound database on a distributed router,
+-          a priority-200 logical with match
+-          <code>ip4.dst == <var>external_ip0</var> &amp;&amp;
+-          ip4.src == <var>external_ip1</var></code>, has action
+-          <code>next;</code>
+-        </p>
+-
+         <p>
+           For each NAT rule in the OVN Northbound database on a
+           distributed router, a priority-100 logical flow with match
+           <code>ip4.dst == <var>E</var> &amp;&amp;
+-          outport == <var>GW</var></code>, where <var>E</var> is the
+-          external IP address specified in the NAT rule, and <var>GW</var>
+-          is the logical router distributed gateway port, with the
+-          following actions:
++          outport == <var>GW</var> &amp;&amp;
++          is_chassis_resident(<var>P</var>)</code>, where <var>E</var> is the
++          external IP address specified in the NAT rule, <var>GW</var>
++          is the logical router distributed gateway port. For dnat_and_snat
++          NAT rule, <var>P</var> is the logical port specified in the NAT rule.
++          If <ref column="logical_port"
++          table="NAT" db="OVN_Northbound"/> column of
++          <ref table="NAT" db="OVN_Northbound"/> table is NOT set, then
++          <var>P</var> is the <code>chassisredirect port</code> of
++          <var>GW</var> with the following actions:
+         </p>
+ 
+         <pre>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 58213742d..e2df94f4b 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -205,17 +205,16 @@ enum ovn_stage {
+ #define REGBIT_ND_RA_OPTS_RESULT "reg0[5]"
+ 
+ /* Register definitions for switches and routers. */
+-#define REGBIT_NAT_REDIRECT     "reg9[0]"
++
+ /* Indicate that this packet has been recirculated using egress
+  * loopback.  This allows certain checks to be bypassed, such as a
+  * logical router dropping packets with source IP address equals
+  * one of the logical router's own IP addresses. */
+-#define REGBIT_EGRESS_LOOPBACK  "reg9[1]"
+-#define REGBIT_DISTRIBUTED_NAT  "reg9[2]"
++#define REGBIT_EGRESS_LOOPBACK  "reg9[0]"
+ /* Register to store the result of check_pkt_larger action. */
+-#define REGBIT_PKT_LARGER        "reg9[3]"
+-#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]"
+-#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]"
++#define REGBIT_PKT_LARGER        "reg9[1]"
++#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[2]"
++#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[3]"
+ 
+ #define FLAGBIT_NOT_VXLAN "flags[1] == 0"
+ 
+@@ -6599,128 +6598,6 @@ build_routing_policy_flow(struct hmap *lflows, struct ovn_datapath *od,
+     ds_destroy(&actions);
+ }
+ 
+-static void
+-add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
+-{
+-    struct ds actions = DS_EMPTY_INITIALIZER;
+-    struct ds match = DS_EMPTY_INITIALIZER;
+-
+-    if (!op->od->l3dgw_port) {
+-        return;
+-    }
+-
+-    if (!op->peer || !op->peer->od->nbs) {
+-        return;
+-    }
+-
+-    for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
+-        const struct nbrec_nat *nat = op->od->nbr->nat[i];
+-        bool found = false;
+-        struct eth_addr mac;
+-
+-        if (strcmp(nat->type, "dnat_and_snat") ||
+-                !nat->external_mac ||
+-                !eth_addr_from_string(nat->external_mac, &mac) ||
+-                !nat->external_ip || !nat->logical_port) {
+-            continue;
+-        }
+-
+-        const struct ovn_datapath *peer_dp = op->peer->od;
+-        for (size_t j = 0; j < peer_dp->nbs->n_ports; j++) {
+-            if (!strcmp(peer_dp->nbs->ports[j]->name, nat->logical_port)) {
+-                found = true;
+-                break;
+-            }
+-        }
+-        if (!found) {
+-            continue;
+-        }
+-
+-        /* Determine if we need to create IPv4 or IPv6 flows */
+-        ovs_be32 ip;
+-        struct in6_addr ipv6;
+-        int family = AF_INET;
+-        if (!ip_parse(nat->external_ip, &ip) || !ip) {
+-            family = AF_INET6;
+-            if (!ipv6_parse(nat->external_ip, &ipv6)) {
+-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+-                VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
+-                             "for router %s", nat->external_ip, op->key);
+-                /* We'll create IPv6 flows anyway, but the address
+-                 * is probably bogus ... */
+-            }
+-        }
+-
+-        ds_put_format(&match, "inport == %s && "
+-                      "ip%s.src == %s && ip%s.dst == %s",
+-                       op->json_key,
+-                       family == AF_INET ? "4" : "6",
+-                       nat->logical_ip,
+-                       family == AF_INET ? "4" : "6",
+-                       nat->external_ip);
+-        ds_put_format(&actions, "outport = %s; eth.dst = %s; "
+-                      REGBIT_DISTRIBUTED_NAT" = 1; "
+-                      REGBIT_NAT_REDIRECT" = 0; next;",
+-                      op->od->l3dgw_port->json_key,
+-                      nat->external_mac);
+-        ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
+-                      ds_cstr(&match), ds_cstr(&actions));
+-        ds_clear(&match);
+-        ds_clear(&actions);
+-
+-        for (size_t j = 0; j < op->od->nbr->n_nat; j++) {
+-            const struct nbrec_nat *nat2 = op->od->nbr->nat[j];
+-            struct eth_addr mac2;
+-
+-            if (nat == nat2 || strcmp(nat2->type, "dnat_and_snat") ||
+-                    !nat2->external_mac ||
+-                    !eth_addr_from_string(nat2->external_mac, &mac2) ||
+-                    !nat2->external_ip) {
+-                continue;
+-            }
+-
+-            family = AF_INET;
+-            if (!ip_parse(nat2->external_ip, &ip) || !ip) {
+-                family = AF_INET6;
+-                if (!ipv6_parse(nat2->external_ip, &ipv6)) {
+-                    static struct vlog_rate_limit rl =
+-                        VLOG_RATE_LIMIT_INIT(5, 1);
+-                    VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
+-                                 "for router %s", nat2->external_ip, op->key);
+-                    /* We'll create IPv6 flows anyway, but the address
+-                     * is probably bogus ... */
+-                }
+-            }
+-
+-            ds_put_format(&match, "inport == %s && "
+-                          "ip%s.src == %s && ip%s.dst == %s",
+-                          op->json_key,
+-                          family == AF_INET ? "4" : "6",
+-                          nat->logical_ip,
+-                          family == AF_INET ? "4" : "6",
+-                          nat2->external_ip);
+-            ds_put_format(&actions, "outport = %s; "
+-                          "eth.src = %s; eth.dst = %s; "
+-                          "%sreg0 = ip%s.dst; %sreg1 = %s; "
+-                          REGBIT_DISTRIBUTED_NAT" = 1; "
+-                          REGBIT_NAT_REDIRECT" = 0; next;",
+-                          op->od->l3dgw_port->json_key,
+-                          op->od->l3dgw_port->lrp_networks.ea_s,
+-                          nat2->external_mac,
+-                          family == AF_INET ? "" : "xx",
+-                          family == AF_INET ? "4" : "6",
+-                          family == AF_INET ? "" : "xx",
+-                          nat->external_ip);
+-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
+-                          ds_cstr(&match), ds_cstr(&actions));
+-            ds_clear(&match);
+-            ds_clear(&actions);
+-        }
+-    }
+-    ds_destroy(&match);
+-    ds_destroy(&actions);
+-}
+-
+ static void
+ add_route(struct hmap *lflows, const struct ovn_port *op,
+           const char *lrp_addr_s, const char *network_s, int plen,
+@@ -8144,17 +8021,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
+                                   ds_cstr(&match), ds_cstr(&actions));
+-
+-                    /* Traffic received on other router ports must be
+-                     * redirected to the central instance of the l3dgw_port
+-                     * for NAT processing. */
+-                    ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip%s.dst == %s",
+-                                  is_v6 ? "6" : "4",
+-                                  nat->external_ip);
+-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
+-                                  ds_cstr(&match),
+-                                  REGBIT_NAT_REDIRECT" = 1; next;");
+                 }
+             }
+ 
+@@ -8220,18 +8086,33 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
+                                   ds_cstr(&match), ds_cstr(&actions));
++                }
++            }
+ 
+-                    /* Traffic received on other router ports must be
+-                     * redirected to the central instance of the l3dgw_port
+-                     * for NAT processing. */
++            /* ARP resolve for NAT IPs. */
++            if (od->l3dgw_port) {
++                if (!strcmp(nat->type, "snat")) {
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip%s.dst == %s",
+-                                  is_v6 ? "6" : "4",
+-                                  nat->external_ip);
+-                    ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
+-                                  ds_cstr(&match),
+-                                  REGBIT_NAT_REDIRECT" = 1; next;");
++                    ds_put_format(
++                        &match, "inport == %s && %s == %s",
++                        od->l3dgw_port->json_key,
++                        is_v6 ? "ip6.src" : "ip4.src", nat->external_ip);
++                    ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 120,
++                                  ds_cstr(&match), "next;");
+                 }
++
++                ds_clear(&match);
++                ds_put_format(
++                    &match, "outport == %s && %s == %s",
++                    od->l3dgw_port->json_key,
++                    is_v6 ? "xxreg0" : "reg0", nat->external_ip);
++                ds_clear(&actions);
++                ds_put_format(
++                    &actions, "eth.dst = %s; next;",
++                    distributed ? nat->external_mac :
++                    od->l3dgw_port->lrp_networks.ea_s);
++                ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 100,
++                                ds_cstr(&match), ds_cstr(&actions));
+             }
+ 
+             /* Egress UNDNAT table: It is for already established connections'
+@@ -8378,49 +8259,19 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+              * ingress pipeline with inport = outport. */
+             if (od->l3dgw_port) {
+                 /* Distributed router. */
+-                if (!strcmp(nat->type, "dnat_and_snat") &&
+-                        nat->external_mac && nat->external_ip &&
+-                        eth_addr_from_string(nat->external_mac, &mac)) {
+-                    for (int j = 0; j < od->nbr->n_nat; j++) {
+-                        const struct nbrec_nat *nat2 = od->nbr->nat[j];
+-
+-                        if (nat2 == nat ||
+-                            strcmp(nat2->type, "dnat_and_snat") ||
+-                            !nat2->external_mac || !nat2->external_ip) {
+-                            continue;
+-                        }
+-
+-                        ds_clear(&match);
+-                        ds_put_format(&match, "is_chassis_resident(\"%s\") && "
+-                                      "ip%s.src == %s && ip%s.dst == %s",
+-                                      nat->logical_port,
+-                                      is_v6 ? "6" : "4", nat2->external_ip,
+-                                      is_v6 ? "6" : "4", nat->external_ip);
+-                        ds_clear(&actions);
+-                        ds_put_format(&actions,
+-                                      "inport = outport; outport = \"\"; "
+-                                      "flags = 0; flags.loopback = 1; "
+-                                      REGBIT_EGRESS_LOOPBACK" = 1; "
+-                                      "next(pipeline=ingress, table=0); ");
+-                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 300,
+-                                      ds_cstr(&match),  ds_cstr(&actions));
+-
+-                        ds_clear(&match);
+-                        ds_put_format(&match,
+-                                      "ip%s.src == %s && ip%s.dst == %s",
+-                                      is_v6 ? "6" : "4", nat2->external_ip,
+-                                      is_v6 ? "6" : "4", nat->external_ip);
+-                        ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
+-                                      ds_cstr(&match), "next;");
+-                        ds_clear(&match);
+-                    }
+-                }
+-
+                 ds_clear(&match);
+                 ds_put_format(&match, "ip%s.dst == %s && outport == %s",
+                               is_v6 ? "6" : "4",
+                               nat->external_ip,
+                               od->l3dgw_port->json_key);
++                if (!distributed) {
++                    ds_put_format(&match, " && is_chassis_resident(%s)",
++                                  od->l3redirect_port->json_key);
++                } else {
++                    ds_put_format(&match, " && is_chassis_resident(\"%s\")",
++                                  nat->logical_port);
++                }
++
+                 ds_clear(&actions);
+                 ds_put_format(&actions,
+                               "clone { ct_clear; "
+@@ -8491,40 +8342,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             * we can do it here, saving a future re-circulation. */
+             ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
+                           "ip", "flags.loopback = 1; ct_dnat;");
+-        } else {
+-            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 400,
+-                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
+-
+-            /* For NAT on a distributed router, add flows to Ingress
+-             * IP Routing table, Ingress ARP Resolution table, and
+-             * Ingress Gateway Redirect Table that are not specific to a
+-             * NAT rule. */
+-
+-            /* The highest priority IN_IP_ROUTING rule matches packets
+-             * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages),
+-             * with action "ip.ttl--; next;".  The IN_GW_REDIRECT table
+-             * will take care of setting the outport. */
+-            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 300,
+-                          REGBIT_NAT_REDIRECT" == 1", "ip.ttl--; next;");
+-
+-            /* The highest priority IN_ARP_RESOLVE rule matches packets
+-             * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages),
+-             * then sets eth.dst to the distributed gateway port's
+-             * ethernet address. */
+-            ds_clear(&actions);
+-            ds_put_format(&actions, "eth.dst = %s; next;",
+-                          od->l3dgw_port->lrp_networks.ea_s);
+-            ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 200,
+-                          REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions));
+-
+-            /* The highest priority IN_GW_REDIRECT rule redirects packets
+-             * with REGBIT_NAT_REDIRECT (set in DNAT or UNSNAT stages) to
+-             * the central instance of the l3dgw_port for NAT processing. */
+-            ds_clear(&actions);
+-            ds_put_format(&actions, "outport = %s; next;",
+-                          od->l3redirect_port->json_key);
+-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200,
+-                          REGBIT_NAT_REDIRECT" == 1", ds_cstr(&actions));
+         }
+ 
+         /* Load balancing and packet defrag are only valid on
+@@ -8720,9 +8537,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
+-        /* create logical flows for DVR floating IPs */
+-        add_distributed_nat_routes(lflows, op);
+-
+         for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+             add_route(lflows, op, op->lrp_networks.ipv4_addrs[i].addr_s,
+                       op->lrp_networks.ipv4_addrs[i].network_s,
+@@ -9252,9 +9066,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+         if (od->l3dgw_port && od->l3redirect_port) {
+-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 300,
+-                          REGBIT_DISTRIBUTED_NAT" == 1", "next;");
+-
+             /* For traffic with outport == l3dgw_port, if the
+              * packet did not match any higher priority redirect
+              * rule, then the traffic is redirected to the central
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index da566f900..3e4120ac5 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -990,7 +990,7 @@ echo "CR-LRP UUID is: " $uuid
+ # IPV4
+ ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
+ 
+-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
+ wc -l`])
+ 
+ AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
+@@ -1008,7 +1008,7 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [0
+ ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
+ 
+ ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
+-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
+ wc -l`])
+ 
+ AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
+@@ -1027,7 +1027,7 @@ ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
+ # IPV6
+ ovn-nbctl lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
+ 
+-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
+ wc -l`])
+ 
+ AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
+@@ -1045,7 +1045,7 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [0
+ ovn-nbctl lr-nat-del R1 dnat_and_snat  fd01::1
+ ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
+ 
+-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
+ wc -l`])
+ 
+ AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch b/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
new file mode 100644
index 0000000..dcf519d
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
@@ -0,0 +1,126 @@
+From 90a9a77f60b039ff404737d116dc16aeff84944c Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Thu, 19 Mar 2020 16:52:17 +0530
+Subject: [PATCH] ovn-northd: Don't add arp responder flows for lports with
+ 'unknown' address.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If a logical port has 'unknown' address, it means it can send and receive
+packet with any IP and MAC and generally port security is not set for
+such logical ports. If an lport has addresses set to - ["MAC1 IP1", unknown],
+right now we add arp responder flows for IP1 and respond MAC1 in the arp
+response. But it's possible that the VIF of the logical port can use the IP1
+with a different MAC. This patch supports this usecase. When another logical port
+sends ARP request for IP1, the VIF of the logical port will anyway respond.
+
+Reported-by: Maciej Józefczyk <mjozefcz@redhat.com>
+Acked-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml |  5 +++--
+ ovn/northd/ovn-northd.c     | 13 ++++++++-----
+ tests/ovn.at                | 16 ++++++++++++----
+ 3 files changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index f4d6c8f08..5f9809951 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -656,8 +656,9 @@ output;
+ 
+         <p>
+           These flows are omitted for logical ports (other than router ports or
+-          <code>localport</code> ports) that are down and for logical ports of
+-          type <code>virtual</code>.
++          <code>localport</code> ports) that are down, for logical ports of
++          type <code>virtual</code> and for logical ports with 'unknown'
++          address set.
+         </p>
+       </li>
+ 
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index c2ac5ce07..d734d36ee 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -1113,7 +1113,7 @@ struct ovn_port {
+ 
+     bool derived; /* Indicates whether this is an additional port
+                    * derived from nbsp or nbrp. */
+-
++    bool has_unknown; /* If the addresses have 'unknown' defined. */
+     /* The port's peer:
+      *
+      *     - A switch port S of type "router" has a router port R as a peer,
+@@ -2026,8 +2026,11 @@ join_logical_ports(struct northd_context *ctx,
+                 op->lsp_addrs
+                     = xmalloc(sizeof *op->lsp_addrs * nbsp->n_addresses);
+                 for (size_t j = 0; j < nbsp->n_addresses; j++) {
+-                    if (!strcmp(nbsp->addresses[j], "unknown")
+-                        || !strcmp(nbsp->addresses[j], "router")) {
++                    if (!strcmp(nbsp->addresses[j], "unknown")) {
++                        op->has_unknown = true;
++                        continue;
++                    }
++                    if (!strcmp(nbsp->addresses[j], "router")) {
+                         continue;
+                     }
+                     if (is_dynamic_lsp_address(nbsp->addresses[j])) {
+@@ -5834,7 +5837,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         } else {
+             /*
+              * Add ARP/ND reply flows if either the
+-             *  - port is up or
++             *  - port is up and it doesn't have 'unknown' address defined or
+              *  - port type is router or
+              *  - port type is localport
+              */
+@@ -5843,7 +5846,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+                 continue;
+             }
+ 
+-            if (lsp_is_external(op->nbsp)) {
++            if (lsp_is_external(op->nbsp) || op->has_unknown) {
+                 continue;
+             }
+ 
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 9fb6e4f98..da016be9e 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -1715,11 +1715,13 @@ for is in 1 2 3; do
+                 sip=`ip_to_hex 192 168 0 $is$js`
+                 tip=`ip_to_hex 192 168 0 $id$jd`
+                 tip_unknown=`ip_to_hex 11 11 11 11`
++                reply_ha=;
+                 if test $d != $s; then
+-                    reply_ha=f000000000$d
+-                else
+-                    reply_ha=
++                    if test $jd != 1; then
++                        reply_ha=f000000000$d
++                    fi
+                 fi
++
+                 test_arp $s f000000000$s $sip $tip $reply_ha               #9
+                 test_arp $s f000000000$s $sip $tip_unknown                 #10
+ 
+@@ -2158,7 +2160,13 @@ for s in 1 2 3; do
+         sip=192.168.0.$s
+         tip=192.168.0.$d
+         tip_unknown=11.11.11.11
+-        if test $d != $s; then reply_ha=f0:00:00:00:00:0$d; else reply_ha=; fi
++        reply_ha=;
++        if test $d != $s; then
++            if test $d != 1; then
++                reply_ha=f0:00:00:00:00:0$d;
++            fi
++        fi
++
+         test_arp $s f0:00:00:00:00:0$s $sip $tip $reply_ha                 #9
+         test_arp $s f0:00:00:00:00:0$s $sip $tip_unknown                   #10
+ 
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-ovn-northd-Fix-IP-local-multicast-flooding.patch b/SOURCES/0001-ovn-northd-Fix-IP-local-multicast-flooding.patch
new file mode 100644
index 0000000..828c0a4
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Fix-IP-local-multicast-flooding.patch
@@ -0,0 +1,162 @@
+From 635df521886b802dd7d4dce0da45fda8759e9d1f Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 27 Feb 2020 10:15:59 +0100
+Subject: [PATCH] ovn-northd: Fix IP local multicast flooding.
+
+Skip IGMP entries learned for local multicast groups when generating
+logical flows. We still allow ovn-controller to learn them as
+it might be useful information for administrators to see that hosts
+register for the groups even though they are not expected to send JOIN
+messages for this range.
+
+Note: The upstream OVN master patch doesn't apply cleanly because OVN
+2.12 doesn't support MLD. The conflict is however easy to solve and
+involves removing the IPv6 specific code.
+
+Fixes: ddc64665b678 ("OVN: Add ovn-northd IGMP support")
+Reported-by: Lucas Alvares Gomes <lmartins@redhat.com>
+Reported-at: https://bugzilla.redhat.com/1803008
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+(cherry picked from OVN commit 755ffada2a66416173d5f1e09672909d40f87fd1)
+
+Conflicts:
+	ovn/northd/ovn-northd.c
+	tests/ovn.at
+Signed-off-by: Ben Pfaff <blp@ovn.org>
+
+(cherry picked from upstream commit 616e3c592af8dcf77a40af67042c4c417f4d550a)
+
+Conflicts:
+	ovn/northd/ovn-northd.c
+
+Change-Id: Iedfaa16b10521cbaa3a0e0b373476aa7ac5903fd
+---
+ ovn/northd/ovn-northd.c |  9 +++++++++
+ tests/ovn.at            | 48 +++++++++++++++++++++++++++++++++++++++---------
+ 2 files changed, 48 insertions(+), 9 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 7cb3773..1d21f8c 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6309,6 +6309,15 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         struct mcast_switch_info *mcast_sw_info =
+             &igmp_group->datapath->mcast_info.sw;
+ 
++        /* RFC 4541, section 2.1.2, item 2: Skip groups in the 224.0.0.X
++         * range.
++         */
++        ovs_be32 group_address =
++            in6_addr_get_mapped_ipv4(&igmp_group->address);
++        if (ip_is_local_multicast(group_address)) {
++            continue;
++        }
++
+         if (mcast_sw_info->active_flows >= mcast_sw_info->table_size) {
+             continue;
+         }
+diff --git a/tests/ovn.at b/tests/ovn.at
+index d74a8b0..d6e9b65 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -15293,7 +15293,7 @@ ovn-nbctl set Logical_Switch sw1       \
+     other_config:mcast_snoop="true"
+ 
+ # No IGMP query should be generated by sw1 (mcast_querier="false").
+-truncate -s 0 expected
++> expected
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+ OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected])
+ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+@@ -15313,14 +15313,14 @@ send_igmp_v3_report hv2-vif1 hv2 000000000002 $(ip_to_hex 10 0 0 2) f9f9 \
+ 
+ # Check that the IGMP Group is learned on both hv.
+ OVS_WAIT_UNTIL([
+-    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c`
+     test "${total_entries}" = "2"
+ ])
+ 
+ # Send traffic and make sure it gets forwarded only on the two ports that
+ # joined.
+-truncate -s 0 expected
+-truncate -s 0 expected_empty
++> expected
++> expected_empty
+ send_ip_multicast_pkt hv1-vif2 hv1 \
+     000000000001 01005e000144 \
+     $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
+@@ -15345,15 +15345,15 @@ send_igmp_v3_report hv1-vif1 hv1 \
+ 
+ # Check IGMP_Group table on both HV.
+ OVS_WAIT_UNTIL([
+-    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c`
+     test "${total_entries}" = "1"
+ ])
+ 
+ # Send traffic and make sure it gets forwarded only on the port that joined.
+ as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+ as hv2 reset_pcap_file hv2-vif1 hv2/vif1
+-truncate -s 0 expected
+-truncate -s 0 expected_empty
++> expected
++> expected_empty
+ send_ip_multicast_pkt hv1-vif2 hv1 \
+     000000000001 01005e000144 \
+     $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
+@@ -15373,10 +15373,40 @@ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
+ # Flush IGMP groups.
+ ovn-sbctl ip-multicast-flush sw1
+ OVS_WAIT_UNTIL([
+-    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c`
+     test "${total_entries}" = "0"
+ ])
+ 
++# Check that traffic for 224.0.0.X is flooded even if some hosts register for
++# it.
++# Inject IGMP Join for 224.0.0.42 on sw1-p11.
++send_igmp_v3_report hv1-vif1 hv1 \
++    000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
++    $(ip_to_hex 224 0 0 42) 04 f9d3 \
++    /dev/null
++
++# Check that the IGMP Group is learned.
++OVS_WAIT_UNTIL([
++    total_entries=`ovn-sbctl find IGMP_Group | grep "224.0.0.42" -c`
++    test "${total_entries}" = "1"
++])
++
++# Send traffic and make sure it gets flooded to all ports.
++as hv1 reset_pcap_file hv1-vif1 hv1/vif1
++as hv1 reset_pcap_file hv1-vif2 hv1/vif2
++as hv2 reset_pcap_file hv2-vif1 hv2/vif1
++as hv2 reset_pcap_file hv2-vif2 hv2/vif2
++> expected
++send_ip_multicast_pkt hv1-vif2 hv1 \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 224 0 0 42) 1e f989 11 \
++    e518e518000a4b540000 \
++    expected
++
++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected])
++
+ # Enable IGMP snooping and querier on sw2 and set query interval to minimum.
+ ovn-nbctl set Logical_Switch sw2 \
+     other_config:mcast_snoop="true" \
+@@ -15386,7 +15416,7 @@ ovn-nbctl set Logical_Switch sw2 \
+     other_config:mcast_ip4_src="20.0.0.254"
+ 
+ # Wait for 1 query interval (1 sec) and check that two queries are generated.
+-truncate -s 0 expected
++> expected
+ store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected
+ store_igmp_v3_query 0000000002fe $(ip_to_hex 20 0 0 254) 84dd expected
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch b/SOURCES/0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch
new file mode 100644
index 0000000..6941bae
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch
@@ -0,0 +1,146 @@
+From adcdbd9a4ead77037f5d0e6fe9dbc64bf55dce0b Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Wed, 16 Oct 2019 15:06:41 +0200
+Subject: [PATCH ovn] ovn-northd: Fix IP multicast flooding to mrouter.
+
+OVN logical flow "drop" actions can't be combined with other actions.
+Commit 79308138891a created such a scenario if a logical switch has
+mcast_snoop=true, mcast_flood_unregistered=false and is connected to a
+logical router with mcast_relay=enabled.
+
+To fix the issue we now explicitly add a drop flow for unregistered IP
+multicast traffic in a logical switch if mcast_snoop=true,
+mcast_flood_unregistered=false and the switch doesn't have any ports
+with mcast_flood=true and isn't connected to a router with
+mcast_relay=true.
+
+Fixes: 79308138891a ("ovn-northd: Add static IP multicast flood configuration")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml | 13 +++++++++++++
+ ovn/northd/ovn-northd.c     |  8 +++++++-
+ tests/ovn.at                | 50 ++++++++++++++++++++++++++++++++++++++++++++++---
+ 3 files changed, 67 insertions(+), 4 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 937702e..b5dfcd1 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -992,6 +992,19 @@ output;
+       </li>
+ 
+       <li>
++        A priority-80 flow that drops all unregistered IP multicast traffic
++        if <ref column="other_config" table="Logical_Switch"/>
++        <code>:mcast_snoop='true'</code> and
++        <ref column="other_config" table="Logical_Switch"/>
++        <code>:mcast_flood_unregistered='false'</code> and the switch is
++        not connected to a logical router that has
++        <ref column="options" table="Logical_Router"/>
++        <code>:mcast_relay='true'</code> and the switch doesn't have any
++        logical port with <ref column="options" table="Logical_Switch_Port"/>
++        <code>:mcast_flood='true'</code>.
++      </li>
++
++      <li>
+         A priority-70 flow that outputs all packets with an Ethernet broadcast
+         or multicast <code>eth.dst</code> to the <code>MC_FLOOD</code>
+         multicast group.
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index e41c9d7..d0844dd 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5661,7 +5661,13 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                 if (mcast_sw_info->flood_static) {
+                     ds_put_cstr(&actions, "outport =\""MC_STATIC"\"; output;");
+-                } else {
++                }
++
++                /* Explicitly drop the traffic if relay or static flooding
++                 * is not configured.
++                 */
++                if (!mcast_sw_info->flood_relay &&
++                        !mcast_sw_info->flood_static) {
+                     ds_put_cstr(&actions, "drop;");
+                 }
+ 
+diff --git a/tests/ovn.at b/tests/ovn.at
+index df00517..d141367 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -16306,7 +16306,7 @@ sleep 1
+ OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
+ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected])
+ 
+-# Dissable IGMP querier on sw2.
++# Disable IGMP querier on sw2.
+ ovn-nbctl set Logical_Switch sw2 \
+     other_config:mcast_querier="false"
+ 
+@@ -16357,6 +16357,50 @@ send_igmp_v3_report hv2-vif3 hv2 \
+     000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
+     $(ip_to_hex 239 0 1 68) 04 e9b9 \
+     /dev/null
++
++# Check that the IGMP Group is learned by all switches.
++OVS_WAIT_UNTIL([
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    test "${total_entries}" = "2"
++])
++
++# Send traffic from sw3 and make sure it is relayed by rtr.
++# to ports that joined.
++truncate -s 0 expected_routed_sw1
++truncate -s 0 expected_routed_sw2
++truncate -s 0 expected_empty
++
++as hv1 reset_pcap_file hv1-vif1 hv1/vif1
++as hv1 reset_pcap_file hv1-vif2 hv1/vif2
++as hv1 reset_pcap_file hv1-vif3 hv1/vif3
++as hv1 reset_pcap_file hv1-vif4 hv1/vif4
++as hv2 reset_pcap_file hv2-vif1 hv2/vif1
++as hv2 reset_pcap_file hv2-vif2 hv2/vif2
++as hv2 reset_pcap_file hv2-vif3 hv2/vif3
++as hv2 reset_pcap_file hv2-vif4 hv2/vif4
++
++send_ip_multicast_pkt hv2-vif4 hv2 \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000
++store_ip_multicast_pkt \
++    000000000100 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed_sw1
++store_ip_multicast_pkt \
++    000000000200 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed_sw2
++
++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1])
++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2])
++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
++
+ # Inject IGMP Join for 239.0.1.68 on sw3-p1.
+ send_igmp_v3_report hv1-vif4 hv1 \
+     000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
+@@ -16369,8 +16413,8 @@ OVS_WAIT_UNTIL([
+     test "${total_entries}" = "3"
+ ])
+ 
+-# Send traffic from sw3 and make sure it is relayed by rtr.
+-# and ports that joined.
++# Send traffic from sw3 and make sure it is relayed by rtr
++# to ports that joined.
+ truncate -s 0 expected_routed_sw1
+ truncate -s 0 expected_routed_sw2
+ truncate -s 0 expected_switched
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch b/SOURCES/0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch
new file mode 100644
index 0000000..7fdaeec
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch
@@ -0,0 +1,86 @@
+From f5fc57969797fcea4910f67560db12c9a3b69b77 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 16 Jan 2020 16:37:14 +0100
+Subject: [PATCH 1/2] ovn-northd: Fix Pre-LB logical flows with IPv4 and IPv6.
+
+When both IPv4 and IPv6 load balancers were configured, the logical flows
+that send packets that need to be load balanced to conntrack (for
+defragmentation) were incorrectly trying to match on IPv6 fields.
+
+Fix this issue by using two address sets, one for IPv4 and the other for
+IPv6.
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.c | 29 +++++++++++++++++------------
+ 1 file changed, 17 insertions(+), 12 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 655d6240a..b71ae7a94 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -4639,13 +4639,14 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;");
+     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;");
+ 
+-    struct sset all_ips = SSET_INITIALIZER(&all_ips);
++    struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
++    struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
+     bool vip_configured = false;
+-    int addr_family = AF_INET;
+     for (int i = 0; i < od->nbs->n_load_balancer; i++) {
+         struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
+         struct smap *vips = &lb->vips;
+         struct smap_node *node;
++        int addr_family = AF_INET;
+ 
+         SMAP_FOR_EACH (node, vips) {
+             vip_configured = true;
+@@ -4659,8 +4660,10 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+                 continue;
+             }
+ 
+-            if (!sset_contains(&all_ips, ip_address)) {
+-                sset_add(&all_ips, ip_address);
++            if (addr_family == AF_INET) {
++                sset_add(&all_ips_v4, ip_address);
++            } else {
++                sset_add(&all_ips_v6, ip_address);
+             }
+ 
+             build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
+@@ -4679,20 +4682,22 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+     /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
+      * packet to conntrack for defragmentation. */
+     const char *ip_address;
+-    SSET_FOR_EACH(ip_address, &all_ips) {
+-        char *match;
++    SSET_FOR_EACH (ip_address, &all_ips_v4) {
++        char *match = xasprintf("ip && ip4.dst == %s", ip_address);
++        ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
++                      100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;");
++        free(match);
++    }
+ 
+-        if (addr_family == AF_INET) {
+-            match = xasprintf("ip && ip4.dst == %s", ip_address);
+-        } else {
+-            match = xasprintf("ip && ip6.dst == %s", ip_address);
+-        }
++    SSET_FOR_EACH (ip_address, &all_ips_v6) {
++        char *match = xasprintf("ip && ip6.dst == %s", ip_address);
+         ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB,
+                       100, match, REGBIT_CONNTRACK_DEFRAG" = 1; next;");
+         free(match);
+     }
+ 
+-    sset_destroy(&all_ips);
++    sset_destroy(&all_ips_v4);
++    sset_destroy(&all_ips_v6);
+ 
+     if (vip_configured) {
+         ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB,
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch b/SOURCES/0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch
new file mode 100644
index 0000000..1812981
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch
@@ -0,0 +1,199 @@
+From 913179b88c7b0e191a5cad635efb65e661726f09 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Tue, 12 Nov 2019 08:57:08 -0800
+Subject: [PATCH ovn 1/3] ovn-northd: Fix get_router_load_balancer_ips() for
+ mixed address families.
+
+Function get_router_load_balancer_ips() was incorrectly returning a
+single address_family even though the IP set could contain a mix of
+IPv4 and IPv6 addresses.
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry-picked from upstream commit d0ef9f0ac9b9e78a8a54e31ca62a66c640c0346a)
+---
+ ovn/northd/ovn-northd.c | 126 +++++++++++++++++++++++++++---------------------
+ 1 file changed, 72 insertions(+), 54 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index d94276b..81c313f 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -2184,7 +2184,7 @@ ip_address_and_port_from_lb_key(const char *key, char **ip_address,
+ 
+ static void
+ get_router_load_balancer_ips(const struct ovn_datapath *od,
+-                             struct sset *all_ips, int *addr_family)
++                             struct sset *all_ips_v4, struct sset *all_ips_v6)
+ {
+     if (!od->nbr) {
+         return;
+@@ -2199,13 +2199,21 @@ get_router_load_balancer_ips(const struct ovn_datapath *od,
+             /* node->key contains IP:port or just IP. */
+             char *ip_address = NULL;
+             uint16_t port;
++            int addr_family;
+ 
+             ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+-                                            addr_family);
++                                            &addr_family);
+             if (!ip_address) {
+                 continue;
+             }
+ 
++            struct sset *all_ips;
++            if (addr_family == AF_INET) {
++                all_ips = all_ips_v4;
++            } else {
++                all_ips = all_ips_v6;
++            }
++
+             if (!sset_contains(all_ips, ip_address)) {
+                 sset_add(all_ips, ip_address);
+             }
+@@ -2299,17 +2307,22 @@ get_nat_addresses(const struct ovn_port *op, size_t *n)
+         }
+     }
+ 
+-    /* A set to hold all load-balancer vips. */
+-    struct sset all_ips = SSET_INITIALIZER(&all_ips);
+-    int addr_family;
+-    get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
++    /* Two sets to hold all load-balancer vips. */
++    struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
++    struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
++    get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6);
+ 
+     const char *ip_address;
+-    SSET_FOR_EACH (ip_address, &all_ips) {
++    SSET_FOR_EACH (ip_address, &all_ips_v4) {
+         ds_put_format(&c_addresses, " %s", ip_address);
+         central_ip_address = true;
+     }
+-    sset_destroy(&all_ips);
++    SSET_FOR_EACH (ip_address, &all_ips_v6) {
++        ds_put_format(&c_addresses, " %s", ip_address);
++        central_ip_address = true;
++    }
++    sset_destroy(&all_ips_v4);
++    sset_destroy(&all_ips_v6);
+ 
+     if (central_ip_address) {
+         /* Gratuitous ARP for centralized NAT rules on distributed gateway
+@@ -6893,61 +6906,66 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+ 
+         /* A set to hold all load-balancer vips that need ARP responses. */
+-        struct sset all_ips = SSET_INITIALIZER(&all_ips);
+-        int addr_family;
+-        get_router_load_balancer_ips(op->od, &all_ips, &addr_family);
++        struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
++        struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
++        get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6);
+ 
+         const char *ip_address;
+-        SSET_FOR_EACH(ip_address, &all_ips) {
++        SSET_FOR_EACH (ip_address, &all_ips_v4) {
+             ds_clear(&match);
+-            if (addr_family == AF_INET) {
+-                ds_put_format(&match,
+-                              "inport == %s && arp.tpa == %s && arp.op == 1",
+-                              op->json_key, ip_address);
+-            } else {
+-                ds_put_format(&match,
+-                              "inport == %s && nd_ns && nd.target == %s",
+-                              op->json_key, ip_address);
+-            }
++            ds_put_format(&match,
++                          "inport == %s && arp.tpa == %s && arp.op == 1",
++                          op->json_key, ip_address);
+ 
+             ds_clear(&actions);
+-            if (addr_family == AF_INET) {
+-                ds_put_format(&actions,
+-                "eth.dst = eth.src; "
+-                "eth.src = %s; "
+-                "arp.op = 2; /* ARP reply */ "
+-                "arp.tha = arp.sha; "
+-                "arp.sha = %s; "
+-                "arp.tpa = arp.spa; "
+-                "arp.spa = %s; "
+-                "outport = %s; "
+-                "flags.loopback = 1; "
+-                "output;",
+-                op->lrp_networks.ea_s,
+-                op->lrp_networks.ea_s,
+-                ip_address,
+-                op->json_key);
+-            } else {
+-                ds_put_format(&actions,
+-                "nd_na { "
+-                "eth.src = %s; "
+-                "ip6.src = %s; "
+-                "nd.target = %s; "
+-                "nd.tll = %s; "
+-                "outport = inport; "
+-                "flags.loopback = 1; "
+-                "output; "
+-                "};",
+-                op->lrp_networks.ea_s,
+-                ip_address,
+-                ip_address,
+-                op->lrp_networks.ea_s);
+-            }
++            ds_put_format(&actions,
++                          "eth.dst = eth.src; "
++                          "eth.src = %s; "
++                          "arp.op = 2; /* ARP reply */ "
++                          "arp.tha = arp.sha; "
++                          "arp.sha = %s; "
++                          "arp.tpa = arp.spa; "
++                          "arp.spa = %s; "
++                          "outport = %s; "
++                          "flags.loopback = 1; "
++                          "output;",
++                          op->lrp_networks.ea_s,
++                          op->lrp_networks.ea_s,
++                          ip_address,
++                          op->json_key);
++
+             ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                           ds_cstr(&match), ds_cstr(&actions));
+         }
+ 
+-        sset_destroy(&all_ips);
++        SSET_FOR_EACH (ip_address, &all_ips_v6) {
++            ds_clear(&match);
++            ds_put_format(&match,
++                          "inport == %s && nd_ns && nd.target == %s",
++                          op->json_key, ip_address);
++
++            ds_clear(&actions);
++            ds_put_format(&actions,
++                          "nd_na { "
++                          "eth.src = %s; "
++                          "ip6.src = %s; "
++                          "nd.target = %s; "
++                          "nd.tll = %s; "
++                          "outport = inport; "
++                          "flags.loopback = 1; "
++                          "output; "
++                          "};",
++                          op->lrp_networks.ea_s,
++                          ip_address,
++                          ip_address,
++                          op->lrp_networks.ea_s);
++
++            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
++                          ds_cstr(&match), ds_cstr(&actions));
++        }
++
++        sset_destroy(&all_ips_v4);
++        sset_destroy(&all_ips_v6);
+ 
+         /* A gateway router can have 2 SNAT IP addresses to force DNATed and
+          * LBed traffic respectively to be SNATed.  In addition, there can be
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch b/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch
new file mode 100644
index 0000000..38e1dd9
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch
@@ -0,0 +1,321 @@
+From 8f0056c923bbd0af9256ef7735d070e128fa77df Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Tue, 24 Mar 2020 11:03:29 +0100
+Subject: [PATCH 1/2] ovn-northd: Forward ARP requests on localnet ports.
+
+Commit 32f5ebb06226 limited the ARP/ND broadcast domain but in scenarios
+where ARP responder flows are installed only on chassis that own the
+associated logical ports ARP requests should still be forwarded on
+localnet ports because the router pipeline should be executed on the
+chassis that owns the logical port. Only that chassis will reply to the
+ARP/ND request.
+
+Reported-by: Michael Plato <michael.plato@tu-berlin.de>
+Reported-at: https://mail.openvswitch.org/pipermail/ovs-discuss/2020-March/049856.html
+Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+(cherry picked from upstream commit d2ab98463f299e67a9f9a31e8b7c42680b8645cf)
+
+Change-Id: I1c7cd9341b249ba754a698ac205a10ddc10d2dc2
+---
+ ovn/northd/ovn-northd.8.xml |   3 +-
+ ovn/northd/ovn-northd.c     |   6 +-
+ tests/ovn.at                | 169 ++++++++++++++++++++++++++++++++++++++++----
+ 3 files changed, 162 insertions(+), 16 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 5f98099..ad17323 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1104,7 +1104,8 @@ output;
+         Priority-75 flows for each IP address/VIP/NAT address owned by a
+         router port connected to the switch. These flows match ARP requests
+         and ND packets for the specific IP addresses.  Matched packets are
+-        forwarded only to the router that owns the IP address.
++        forwarded only to the router that owns the IP address and, if
++        present, to the localnet port of the logical switch.
+       </li>
+ 
+       <li>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index d734d36..7cb3773 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5625,8 +5625,12 @@ build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
+     ds_put_cstr(&match, "}");
+ 
+     /* Send a the packet only to the router pipeline and skip flooding it
+-     * in the broadcast domain.
++     * in the broadcast domain (except for the localnet port).
+      */
++    if (od->localnet_port) {
++        ds_put_format(&actions, "clone { outport = %s; output; }; ",
++                      od->localnet_port->json_key);
++    }
+     ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
+                   ds_cstr(&match), ds_cstr(&actions));
+diff --git a/tests/ovn.at b/tests/ovn.at
+index da016be..551c399 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -15856,13 +15856,14 @@ net_add n1
+ sim_add hv1
+ as hv1
+ ovs-vsctl add-br br-phys
++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
+ ovn_attach n1 br-phys 192.168.0.1
+ 
+-ovs-vsctl -- add-port br-int hv1-vif1 -- \
+-    set interface hv1-vif1 external-ids:iface-id=sw-agg-ext \
+-    options:tx_pcap=hv1/vif1-tx.pcap \
+-    options:rxq_pcap=hv1/vif1-rx.pcap \
+-    ofport-request=1
++sim_add hv2
++as hv2
++ovs-vsctl add-br br-phys
++ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++ovn_attach n1 br-phys 192.168.0.2
+ 
+ # One Aggregation Switch connected to two Logical networks (routers).
+ ovn-nbctl ls-add sw-agg
+@@ -15878,18 +15879,66 @@ ovn-nbctl lsp-add sw-agg sw-rtr2                   \
+     -- lsp-set-addresses sw-rtr2 00:00:00:00:02:00 \
+     -- lsp-set-options sw-rtr2 router-port=rtr2-sw
+ 
+-# Configure L3 interface IPv4 & IPv6 on both routers
++# Localnet port on the Aggregation Switch.
++ovn-nbctl lsp-add sw-agg sw-agg-ln
++ovn-nbctl lsp-set-addresses sw-agg-ln unknown
++ovn-nbctl lsp-set-type sw-agg-ln localnet
++ovn-nbctl lsp-set-options sw-agg-ln network_name=phys
++
++# Configure L3 interface IPv4 & IPv6 on both routers.
+ ovn-nbctl lr-add rtr1
+ ovn-nbctl lrp-add rtr1 rtr1-sw 00:00:00:00:01:00 10.0.0.1/24 10::1/64
+ 
++ovn-nbctl lrp-add rtr1 rtr1-sw1 00:00:01:00:00:00 20.0.0.1/24 20::1/64
++
+ ovn-nbctl lr-add rtr2
+ ovn-nbctl lrp-add rtr2 rtr2-sw 00:00:00:00:02:00 10.0.0.2/24 10::2/64
+ 
++# Configure router gateway ports.
++ovn-nbctl lrp-set-gateway-chassis rtr1-sw hv1 20
++ovn-nbctl lrp-set-gateway-chassis rtr2-sw hv1 20
++
++# One private network behind rtr1 with two VMs.
++ovn-nbctl ls-add sw1
++ovn-nbctl lsp-add sw1 sw1-p1 \
++    -- lsp-set-addresses sw1-p1 00:00:00:01:00:00
++ovn-nbctl lsp-add sw1 sw1-p2 \
++    -- lsp-set-addresses sw1-p2 00:00:00:02:00:00
++ovn-nbctl lsp-add sw1 sw1-rtr1                       \
++    -- lsp-set-type sw1-rtr1 router                  \
++    -- lsp-set-addresses sw1-rtr1 00:00:01:00:00:00  \
++    -- lsp-set-options sw1-rtr1 router-port=rtr1-sw1
++
++# Bind a "VM" connected to sw-agg on hv1.
++as hv1
++ovs-vsctl -- add-port br-int hv1-vif0 -- \
++    set interface hv1-vif0 external-ids:iface-id=sw-agg-ext \
++    options:tx_pcap=hv1/vif0-tx.pcap \
++    options:rxq_pcap=hv1/vif0-rx.pcap \
++    ofport-request=1
++
++# Bind a "VM" connected to sw1 on hv1.
++as hv1
++ovs-vsctl -- add-port br-int hv1-vif1 -- \
++    set interface hv1-vif1 external-ids:iface-id=sw1-p1 \
++    options:tx_pcap=hv1/vif1-tx.pcap \
++    options:rxq_pcap=hv1/vif1-rx.pcap \
++    ofport-request=2
++
++# Bind a "VM" connected to sw1 on hv2.
++as hv2
++ovs-vsctl -- add-port br-int hv1-vif2 -- \
++    set interface hv1-vif2 external-ids:iface-id=sw1-p2 \
++    options:tx_pcap=hv1/vif2-tx.pcap \
++    options:rxq_pcap=hv1/vif2-rx.pcap \
++    ofport-request=3
++
+ OVN_POPULATE_ARP
+ ovn-nbctl --wait=hv sync
+ 
+ sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg)
+ sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg)
++r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1)
+ 
+ r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1)
+ r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2)
+@@ -15898,9 +15947,10 @@ mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${s
+ mc_key=$(printf "%04x" $mc_key)
+ 
+ match_sw_metadata="metadata=0x${sw_dp_key}"
++match_r1_metadata="metadata=0x${r1_dp_key}"
+ 
+ # Inject ARP request for first router owned IP address.
+-send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1)
++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1)
+ 
+ # Verify that the ARP request is sent only to rtr1.
+ match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1"
+@@ -15929,7 +15979,7 @@ OVS_WAIT_UNTIL([
+ # Inject ND_NS for ofirst router owned IP address.
+ src_ipv6=00100000000000000000000000000254
+ dst_ipv6=00100000000000000000000000000001
+-send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
+ 
+ # Verify that the ND_NS is sent only to rtr1.
+ match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1"
+@@ -15966,7 +16016,7 @@ ovn-nbctl lr-lb-add rtr2 lb2-v6
+ ovn-nbctl --wait=hv sync
+ 
+ # Inject ARP request for first router owned VIP address.
+-send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11)
++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11)
+ 
+ # Verify that the ARP request is sent only to rtr1.
+ match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1"
+@@ -15995,7 +16045,7 @@ OVS_WAIT_UNTIL([
+ # Inject ND_NS for first router owned VIP address.
+ src_ipv6=00100000000000000000000000000254
+ dst_ipv6=00100000000000000000000000000011
+-send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
+ 
+ # Verify that the ND_NS is sent only to rtr1.
+ match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11"
+@@ -16019,14 +16069,21 @@ OVS_WAIT_UNTIL([
+     test "0" = "${pkts_flooded}"
+ ])
+ 
+-# Configure NAT on both routers
++# Configure NAT on both routers.
+ ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1
+ ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::111 42::1
+ ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10.0.0.222 42.42.42.2
+ ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10::222 42::2
+ 
++# Configure FIP1 and FIP2 on rtr1 for sw1-p1 and sw1-p2.
++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.121 20.0.0.11 sw1-p1 00:00:00:01:00:00
++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::121 20::11 sw1-p1 00:00:00:01:00:00
++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.122 20.0.0.12 sw1-p2 00:00:00:02:00:00
++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::122 20::12 sw1-p2 00:00:00:02:00:00
++ovn-nbctl --wait=hv sync
++
+ # Inject ARP request for first router owned NAT address.
+-send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)
++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)
+ 
+ # Verify that the ARP request is sent only to rtr1.
+ match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1"
+@@ -16052,10 +16109,50 @@ OVS_WAIT_UNTIL([
+     test "0" = "${pkts_flooded}"
+ ])
+ 
++# Inject ARP request for FIP1.
++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121)
++
++# Verify that the ARP request is replied to from hv1 and not hv2.
++match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.121,arp_op=1"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep n_packets=1 -c)
++    test "1" = "${pkts_on_rtr1}"
++])
++
++as hv2
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep n_packets=1 -c)
++    test "0" = "${pkts_on_rtr1}"
++])
++
++# Inject ARP request for FIP2.
++send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 122)
++
++# Verify that the ARP request is replied to from hv2 and not hv1.
++match_arp_req="priority=90.*${match_r1_metadata}.*arp_tpa=10.0.0.122,arp_op=1"
++
++as hv2
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep n_packets=1 -c)
++    test "1" = "${pkts_on_rtr1}"
++])
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep n_packets=1 -c)
++    test "0" = "${pkts_on_rtr1}"
++])
++
+ # Inject ND_NS for first router owned IP address.
+ src_ipv6=00100000000000000000000000000254
+ dst_ipv6=00100000000000000000000000000111
+-send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
+ 
+ # Verify that the ND_NS is sent only to rtr1.
+ match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111"
+@@ -16079,7 +16176,51 @@ OVS_WAIT_UNTIL([
+     test "0" = "${pkts_flooded}"
+ ])
+ 
+-OVN_CLEANUP([hv1])
++# Inject ND_NS for FIP1.
++src_ipv6=00100000000000000000000000000254
++dst_ipv6=00100000000000000000000000000121
++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72dd
++
++# Verify that the ND_NS is replied to from hv1 and not hv2.
++match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::121"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
++    test "1" = "${pkts_on_rtr1}"
++])
++
++as hv2
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
++    test "0" = "${pkts_on_rtr1}"
++])
++
++# Inject ND_NS for FIP2.
++src_ipv6=00100000000000000000000000000254
++dst_ipv6=00100000000000000000000000000122
++send_nd_ns 1 0 ${src_mac} ${src_ipv6} ${dst_ipv6} 72db
++
++# Verify that the ND_NS is replied to from hv2 and not hv1.
++match_nd_ns="priority=90.*${match_r1_metadata}.*icmp_type=135.*nd_target=10::122"
++
++as hv2
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
++    test "1" = "${pkts_on_rtr1}"
++])
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_on_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep n_packets=1 -c)
++    test "0" = "${pkts_on_rtr1}"
++])
++
++OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+ 
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch b/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch
new file mode 100644
index 0000000..41013b4
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch
@@ -0,0 +1,73 @@
+From 32972260b50f39d493cc42a78d9648ed668c2aec Mon Sep 17 00:00:00 2001
+Message-Id: <32972260b50f39d493cc42a78d9648ed668c2aec.1590584789.git.lorenzo.bianconi@redhat.com>
+From: Han Zhou <hzhou@ovn.org>
+Date: Tue, 5 May 2020 23:16:09 -0700
+Subject: [PATCH ovn] ovn-northd: Remove useless flow for GW_REDIRECT.
+
+Remove the flow in lr_in_gw_redirect stage:
+
+        A priority-150 logical flow with match
+        <code>outport == <var>GW</var> &amp;&amp;
+        eth.dst == 00:00:00:00:00:00</code> has actions
+        <code>outport = <var>CR</var>; next;</code>, where
+        <var>GW</var> is the logical router distributed gateway
+        port and <var>CR</var> is the <code>chassisredirect</code>
+        port representing the instance of the logical router
+        distributed gateway port on the
+        <code>redirect-chassis</code>.
+
+The commit c0bf32d ("Manage ARP process locally in a DVR scenario") updated
+the priority-100 flow in this stage to priority 200, which makes this
+priority-150 flow useless, because whatever packets matching this flow
+would also match the priority-50 flow.
+
+Cc: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Tested-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Acked-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ ovn/northd/ovn-northd.8.xml | 12 ------------
+ ovn/northd/ovn-northd.c     | 11 -----------
+ 2 files changed, 23 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -2662,18 +2662,6 @@ icmp4 {
+ 
+     <ul>
+       <li>
+-        A priority-150 logical flow with match
+-        <code>outport == <var>GW</var> &amp;&amp;
+-        eth.dst == 00:00:00:00:00:00</code> has actions
+-        <code>outport = <var>CR</var>; next;</code>, where
+-        <var>GW</var> is the logical router distributed gateway
+-        port and <var>CR</var> is the <code>chassisredirect</code>
+-        port representing the instance of the logical router
+-        distributed gateway port on the
+-        <code>redirect-chassis</code>.
+-      </li>
+-
+-      <li>
+         For each NAT rule in the OVN Northbound database that can
+         be handled in a distributed manner, a priority-200 logical
+         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -9196,16 +9196,6 @@ build_lrouter_flows(struct hmap *datapat
+                           od->l3redirect_port->json_key);
+             ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
+                           ds_cstr(&match), ds_cstr(&actions));
+-
+-            /* If the Ethernet destination has not been resolved,
+-             * redirect to the central instance of the l3dgw_port.
+-             * Such traffic will be replaced by an ARP request or ND
+-             * Neighbor Solicitation in the ARP request ingress
+-             * table, before being redirected to the central instance.
+-             */
+-            ds_put_format(&match, " && eth.dst == 00:00:00:00:00:00");
+-            ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 150,
+-                          ds_cstr(&match), ds_cstr(&actions));
+         }
+ 
+         /* Packets are allowed by default. */
diff --git a/SOURCES/0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch b/SOURCES/0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch
new file mode 100644
index 0000000..ffbfd40
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch
@@ -0,0 +1,53 @@
+From 67d348798cddef1d48f1249c9dfdc0b8d9693371 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 1 Aug 2019 13:26:20 +0200
+Subject: [PATCH 1/4] ovn-northd: Use HMAP_FOR_EACH when adding multicast
+ lflows
+
+There's no need to use HMAP_FOR_EACH_SAFE when we add the logical flows
+that correspond to IGMP groups as we don't modify the hashmap in the
+loop.
+
+Also, we should reinitialize match and actions only if the flow will be
+added.
+
+Fixes: ddc64665b678 ("OVN: Add ovn-northd IGMP support")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Numan Siddique <nusiddiq@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/northd/ovn-northd.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index eea9144d0..d81cfd893 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5429,12 +5429,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+     /* Ingress table 17: Add IP multicast flows learnt from IGMP
+      * (priority 90). */
+-    struct ovn_igmp_group *igmp_group, *next_igmp_group;
+-
+-    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, igmp_groups) {
+-        ds_clear(&match);
+-        ds_clear(&actions);
++    struct ovn_igmp_group *igmp_group;
+ 
++    HMAP_FOR_EACH (igmp_group, hmap_node, igmp_groups) {
+         if (!igmp_group->datapath) {
+             continue;
+         }
+@@ -5446,6 +5443,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+         mcast_info->active_flows++;
+ 
++        ds_clear(&match);
++        ds_clear(&actions);
++
+         ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ",
+                       igmp_group->mcgroup.name);
+         ds_put_format(&actions, "outport = \"%s\"; output; ",
+-- 
+2.21.0
+
diff --git a/SOURCES/0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch b/SOURCES/0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch
new file mode 100644
index 0000000..1dd9243
--- /dev/null
+++ b/SOURCES/0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch
@@ -0,0 +1,68 @@
+From e02f2e47383097b737bb9a1cf3a371268c0693da Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 7 Nov 2019 13:19:19 +0100
+Subject: [PATCH ovn] ovn-northd: Validate dnat_and_snat
+ external_mac/logical_ip.
+
+When dnat_and_snat NAT rules are configured, if the user tries to set
+external_mac in the NAT rule record without setting logical_ip
+ovn-northd crashes as there's no validation in place.
+
+Add checks for valid ethernet address in NAT.external_mac and for
+non-null NAT.logical_ip where applicable.
+
+Reported-by: Daniel Alvarez Sanchez <dalvarez@redhat.com>
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1769709
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index c23c270..2f0f501 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6032,9 +6032,12 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
+     for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
+         const struct nbrec_nat *nat = op->od->nbr->nat[i];
+         bool found = false;
++        struct eth_addr mac;
+ 
+         if (strcmp(nat->type, "dnat_and_snat") ||
+-            !nat->external_mac  || !nat->external_ip) {
++                !nat->external_mac ||
++                !eth_addr_from_string(nat->external_mac, &mac) ||
++                !nat->external_ip || !nat->logical_port) {
+             continue;
+         }
+ 
+@@ -6083,10 +6086,14 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
+ 
+         for (size_t j = 0; j < op->od->nbr->n_nat; j++) {
+             const struct nbrec_nat *nat2 = op->od->nbr->nat[j];
++            struct eth_addr mac2;
+ 
+             if (nat == nat2 || strcmp(nat2->type, "dnat_and_snat") ||
+-                !nat2->external_mac || !nat2->external_ip)
++                    !nat2->external_mac ||
++                    !eth_addr_from_string(nat2->external_mac, &mac2) ||
++                    !nat2->external_ip) {
+                 continue;
++            }
+ 
+             family = AF_INET;
+             if (!ip_parse(nat2->external_ip, &ip) || !ip) {
+@@ -7785,7 +7792,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             if (od->l3dgw_port) {
+                 /* Distributed router. */
+                 if (!strcmp(nat->type, "dnat_and_snat") &&
+-                    nat->external_mac && nat->external_ip) {
++                        nat->external_mac && nat->external_ip &&
++                        eth_addr_from_string(nat->external_mac, &mac)) {
+                     for (int j = 0; j < od->nbr->n_nat; j++) {
+                         const struct nbrec_nat *nat2 = od->nbr->nat[j];
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch b/SOURCES/0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
new file mode 100644
index 0000000..80d0f39
--- /dev/null
+++ b/SOURCES/0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
@@ -0,0 +1,217 @@
+From 5d7f0f78f295f9cb02c7d90341e587f0a1cf4173 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Fri, 5 Jun 2020 14:00:29 +0530
+Subject: [PATCH 1/2] ovsdb idl: Try committing the pending txn in
+ ovsdb_idl_loop_run.
+
+The function ovsdb_idl_loop_run(), after calling ovsdb_idl_run(),
+returns a transaction object (of type 'struct ovsdb_idl_txn').
+The returned transaction object can be NULL if there is a pending
+transaction (loop->committing_txn) in the idl loop object.
+
+Normally the clients of idl library, first call ovsdb_idl_loop_run(),
+then do their own processing and create any idl transactions during
+this processing and then finally call ovsdb_idl_loop_commit_and_wait().
+
+If ovsdb_idl_loop_run() returns NULL transaction object, then much
+of the processing done by the client gets wasted as in the case
+of ovn-controller.
+
+The client (in this case ovn-controller), can skip the processing
+and instead call ovsdb_idl_loop_commit_and_wait() if the transaction
+oject is NULL. But ovn-controller uses IDL tracking and it may
+loose the tracked changes in that run.
+
+This patch tries to improve this scenario, by checking if the
+pending transaction can be committed in the ovsdb_idl_loop_run()
+itself and if the pending transaction is cleared (because of the
+response messages from ovsdb-server due to a transaction message
+in the previous run), ovsdb_idl_loop_run() can return a valid
+transaction object.
+
+CC: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Ben Pfaff <blp@ovn.org>
+---
+ lib/ovsdb-idl.c | 143 +++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 93 insertions(+), 50 deletions(-)
+
+diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
+index 190143f36..fd62470ff 100644
+--- a/lib/ovsdb-idl.c
++++ b/lib/ovsdb-idl.c
+@@ -385,6 +385,8 @@ static void ovsdb_idl_send_cond_change(struct ovsdb_idl *idl);
+ static void ovsdb_idl_destroy_indexes(struct ovsdb_idl_table *);
+ static void ovsdb_idl_add_to_indexes(const struct ovsdb_idl_row *);
+ static void ovsdb_idl_remove_from_indexes(const struct ovsdb_idl_row *);
++static int ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
++                                         bool *may_need_wakeup);
+ 
+ static void
+ ovsdb_idl_db_init(struct ovsdb_idl_db *db, const struct ovsdb_idl_class *class,
+@@ -5331,6 +5333,12 @@ struct ovsdb_idl_txn *
+ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
+ {
+     ovsdb_idl_run(loop->idl);
++
++    /* See if we can commit the loop->committing_txn. */
++    if (loop->committing_txn) {
++        ovsdb_idl_try_commit_loop_txn(loop, NULL);
++    }
++
+     loop->open_txn = (loop->committing_txn
+                       || ovsdb_idl_get_seqno(loop->idl) == loop->skip_seqno
+                       ? NULL
+@@ -5338,6 +5346,87 @@ ovsdb_idl_loop_run(struct ovsdb_idl_loop *loop)
+     return loop->open_txn;
+ }
+ 
++/* Attempts to commit the current transaction, if one is open.
++ *
++ * If a transaction was open, in this or a previous iteration of the main loop,
++ * and had not before finished committing (successfully or unsuccessfully), the
++ * return value is one of:
++ *
++ *  1: The transaction committed successfully (or it did not change anything in
++ *     the database).
++ *  0: The transaction failed.
++ * -1: The commit is still in progress.
++ *
++ * Thus, the return value is -1 if the transaction is in progress and otherwise
++ * true for success, false for failure.
++ *
++ * (In the corner case where the IDL sends a transaction to the database and
++ * the database commits it, and the connection between the IDL and the database
++ * drops before the IDL receives the message confirming the commit, this
++ * function can return 0 even though the transaction succeeded.)
++ */
++static int
++ovsdb_idl_try_commit_loop_txn(struct ovsdb_idl_loop *loop,
++                              bool *may_need_wakeup)
++{
++    if (!loop->committing_txn) {
++        /* Not a meaningful return value: no transaction was in progress. */
++        return 1;
++    }
++
++    int retval;
++    struct ovsdb_idl_txn *txn = loop->committing_txn;
++
++    enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
++    if (status != TXN_INCOMPLETE) {
++        switch (status) {
++        case TXN_TRY_AGAIN:
++            /* We want to re-evaluate the database when it's changed from
++             * the contents that it had when we started the commit.  (That
++             * might have already happened.) */
++            loop->skip_seqno = loop->precommit_seqno;
++            if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno
++                && may_need_wakeup) {
++                *may_need_wakeup = true;
++            }
++            retval = 0;
++            break;
++
++        case TXN_SUCCESS:
++            /* Possibly some work on the database was deferred because no
++             * further transaction could proceed.  Wake up again. */
++            retval = 1;
++            loop->cur_cfg = loop->next_cfg;
++            if (may_need_wakeup) {
++                *may_need_wakeup =  true;
++            }
++            break;
++
++        case TXN_UNCHANGED:
++            retval = 1;
++            loop->cur_cfg = loop->next_cfg;
++            break;
++
++        case TXN_ABORTED:
++        case TXN_NOT_LOCKED:
++        case TXN_ERROR:
++            retval = 0;
++            break;
++
++        case TXN_UNCOMMITTED:
++        case TXN_INCOMPLETE:
++        default:
++            OVS_NOT_REACHED();
++        }
++        ovsdb_idl_txn_destroy(txn);
++        loop->committing_txn = NULL;
++    } else {
++        retval = -1;
++    }
++
++    return retval;
++}
++
+ /* Attempts to commit the current transaction, if one is open, and sets up the
+  * poll loop to wake up when some more work might be needed.
+  *
+@@ -5368,57 +5457,11 @@ ovsdb_idl_loop_commit_and_wait(struct ovsdb_idl_loop *loop)
+         loop->precommit_seqno = ovsdb_idl_get_seqno(loop->idl);
+     }
+ 
+-    struct ovsdb_idl_txn *txn = loop->committing_txn;
+-    int retval;
+-    if (txn) {
+-        enum ovsdb_idl_txn_status status = ovsdb_idl_txn_commit(txn);
+-        if (status != TXN_INCOMPLETE) {
+-            switch (status) {
+-            case TXN_TRY_AGAIN:
+-                /* We want to re-evaluate the database when it's changed from
+-                 * the contents that it had when we started the commit.  (That
+-                 * might have already happened.) */
+-                loop->skip_seqno = loop->precommit_seqno;
+-                if (ovsdb_idl_get_seqno(loop->idl) != loop->skip_seqno) {
+-                    poll_immediate_wake();
+-                }
+-                retval = 0;
+-                break;
+-
+-            case TXN_SUCCESS:
+-                /* Possibly some work on the database was deferred because no
+-                 * further transaction could proceed.  Wake up again. */
+-                retval = 1;
+-                loop->cur_cfg = loop->next_cfg;
+-                poll_immediate_wake();
+-                break;
+-
+-            case TXN_UNCHANGED:
+-                retval = 1;
+-                loop->cur_cfg = loop->next_cfg;
+-                break;
+-
+-            case TXN_ABORTED:
+-            case TXN_NOT_LOCKED:
+-            case TXN_ERROR:
+-                retval = 0;
+-                break;
+-
+-            case TXN_UNCOMMITTED:
+-            case TXN_INCOMPLETE:
+-            default:
+-                OVS_NOT_REACHED();
+-            }
+-            ovsdb_idl_txn_destroy(txn);
+-            loop->committing_txn = NULL;
+-        } else {
+-            retval = -1;
+-        }
+-    } else {
+-        /* Not a meaningful return value: no transaction was in progress. */
+-        retval = 1;
++    bool may_need_wakeup = false;
++    int retval = ovsdb_idl_try_commit_loop_txn(loop, &may_need_wakeup);
++    if (may_need_wakeup) {
++        poll_immediate_wake();
+     }
+-
+     ovsdb_idl_wait(loop->idl);
+ 
+     return retval;
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch b/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch
new file mode 100644
index 0000000..32a5402
--- /dev/null
+++ b/SOURCES/0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch
@@ -0,0 +1,97 @@
+From 1ba92ed2f401b8288481134279d7843908621c86 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Fri, 17 Apr 2020 11:45:56 +0530
+Subject: [PATCH] pinctrl: Handle service monitors even if the lport doesn't
+ have IPv4 addresses set.
+
+If a logical port is only configured with MAC address(es) in the
+Logical_Switch_Port.addresses, pinctrl is ignoring the service monitors
+configured for that logical port. This patch allows that.
+
+Change-Id: I0351395a88cd0d27ee3e18e1b2af7d9e0f0483b3
+Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=1801058
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream ovn commit 71c6157575f4ac52484fa8703395301bd2189903)
+---
+ ovn/controller/pinctrl.c |  5 +++++
+ tests/ovn.at             | 31 +++++++++++++++++++++++++++++++
+ tests/system-ovn.at      |  4 ++--
+ 3 files changed, 38 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 1824347c2..8bc99df4e 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -4935,6 +4935,11 @@ sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
+ 
+             if (mac_found) {
+                 break;
++            } else if (!laddrs.n_ipv4_addrs) {
++                /* IPv4 address(es) are not configured. Use the first mac. */
++                ea = laddrs.ea;
++                mac_found = true;
++                break;
+             }
+         }
+ 
+diff --git a/tests/ovn.at b/tests/ovn.at
+index d6e9b6581..c06337147 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -17345,5 +17345,36 @@ AT_CHECK([cat lflows.txt], [0], [dnl
+   table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;)
+ ])
+ 
++# Delete sw0-p1
++ovn-nbctl lsp-del sw0-p1
++
++OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l)])
++
++# Add back sw0-p1 but without any IP address.
++ovn-nbctl lsp-add sw0 sw0-p1
++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" -- \
++lsp-set-port-security sw0-p1 "50:54:00:00:00:03"
++
++OVS_WAIT_UNTIL([test 2 = $(ovn-sbctl --bare --columns status find \
++service_monitor | grep offline | wc -l)])
++
++ovn-nbctl lsp-del sw0-p1
++ovn-nbctl lsp-del sw1-p1
++OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l)])
++
++# Add back sw0-p1 but without any address set.
++ovn-nbctl lsp-add sw0 sw0-p1
++
++OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l)])
++
++OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \
++service_monitor | grep offline | wc -l)])
++
++OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \
++service_monitor | grep online | wc -l)])
++
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+diff --git a/tests/system-ovn.at b/tests/system-ovn.at
+index 22e9a5926..d863c828d 100644
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -2545,8 +2545,8 @@ start_daemon ovn-controller
+ ovn-nbctl ls-add sw0
+ 
+ ovn-nbctl lsp-add sw0 sw0-p1
+-ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
+-ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03"
++ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03"
+ 
+ ovn-nbctl lsp-add sw0 sw0-p2
+ ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
+-- 
+2.25.1
+
diff --git a/SOURCES/0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch b/SOURCES/0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
new file mode 100644
index 0000000..624c064
--- /dev/null
+++ b/SOURCES/0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
@@ -0,0 +1,401 @@
+From 4fb306df98419d45889dff6a5b4fb0c21f237609 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Fri, 29 May 2020 23:29:37 +0530
+Subject: [PATCH] pinctrl: Support DHCPRELEASE and DHCPINFORM in native OVN
+ dhcp responder.
+
+Right now we ignore these dhcp packets. This patch adds the support
+as per RFC 2131.
+
+Change-Id: I57091f18212b93e3a366a97120f2a54009fde1d4
+Acked-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream ovn master commit e008a4d46020a778b8f1f85b9dfd7c9e9b6fde21)
+---
+ ovn/controller/pinctrl.c | 125 +++++++++++++++++++++++++++++++--------
+ ovn/lib/ovn-l7.h         |  12 ++++
+ tests/ovn.at             | 117 +++++++++++++++++++++++++++++++++++-
+ 3 files changed, 227 insertions(+), 27 deletions(-)
+
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 51a0f0224..b9e115a39 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -979,11 +979,13 @@ static void
+ pinctrl_handle_put_dhcp_opts(
+     struct rconn *swconn,
+     struct dp_packet *pkt_in, struct ofputil_packet_in *pin,
+-    struct ofpbuf *userdata, struct ofpbuf *continuation)
++    struct flow *in_flow, struct ofpbuf *userdata,
++    struct ofpbuf *continuation)
+ {
+     enum ofp_version version = rconn_get_version(swconn);
+     enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
+     struct dp_packet *pkt_out_ptr = NULL;
++    struct ofpbuf *dhcp_inform_reply_buf = NULL;
+     uint32_t success = 0;
+ 
+     /* Parse result field. */
+@@ -1107,22 +1109,15 @@ pinctrl_handle_put_dhcp_opts(
+         VLOG_WARN_RL(&rl, "Missing DHCP message type");
+         goto exit;
+     }
+-    if (*in_dhcp_msg_type != DHCP_MSG_DISCOVER &&
+-        *in_dhcp_msg_type != DHCP_MSG_REQUEST) {
+-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+-        VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type);
+-        goto exit;
+-    }
+ 
+-    uint8_t msg_type;
+-    if (*in_dhcp_msg_type == DHCP_MSG_DISCOVER) {
++    struct ofpbuf *reply_dhcp_opts_ptr = userdata;
++    uint8_t msg_type = 0;
++
++    switch (*in_dhcp_msg_type) {
++    case DHCP_MSG_DISCOVER:
+         msg_type = DHCP_MSG_OFFER;
+-    } else {
+-        /* This is a DHCPREQUEST. If the client has requested an IP that
+-         * does not match the offered IP address, reply with a NAK. The
+-         * requested IP address may be supplied either via Requested IP Address
+-         * (opt 50) or via ciaddr, depending on the client's state.
+-         */
++        break;
++    case DHCP_MSG_REQUEST: {
+         msg_type = DHCP_MSG_ACK;
+         if (request_ip != *offer_ip) {
+             static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
+@@ -1131,12 +1126,81 @@ pinctrl_handle_put_dhcp_opts(
+                          IP_ARGS(*offer_ip));
+             msg_type = DHCP_MSG_NAK;
+         }
++        break;
++    }
++    case OVN_DHCP_MSG_RELEASE: {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
++        const struct eth_header *l2 = dp_packet_eth(pkt_in);
++        VLOG_INFO_RL(&rl, "DHCPRELEASE "ETH_ADDR_FMT " "IP_FMT"",
++                     ETH_ADDR_ARGS(l2->eth_src),
++                     IP_ARGS(in_dhcp_data->ciaddr));
++        break;
++    }
++    case OVN_DHCP_MSG_INFORM: {
++        /* RFC 2131 section 3.4.
++         * Remove all the offer ip related dhcp options and
++         * all the time related dhcp options.
++         * Loop through the dhcp option defined in the userdata buffer
++         * and copy all the options into dhcp_inform_reply_buf skipping
++         * the not required ones.
++         * */
++        msg_type = DHCP_MSG_ACK;
++        in_dhcp_ptr = userdata->data;
++        end = (const char *)userdata->data + userdata->size;
++
++        /* The buf size cannot be greater > userdata->size. */
++        dhcp_inform_reply_buf = ofpbuf_new(userdata->size);
++
++        reply_dhcp_opts_ptr = dhcp_inform_reply_buf;
++        while (in_dhcp_ptr < end) {
++            const struct dhcp_opt_header *in_dhcp_opt =
++                (const struct dhcp_opt_header *)in_dhcp_ptr;
++
++            switch (in_dhcp_opt->code) {
++            case OVN_DHCP_OPT_CODE_NETMASK:
++            case OVN_DHCP_OPT_CODE_LEASE_TIME:
++            case OVN_DHCP_OPT_CODE_T1:
++            case OVN_DHCP_OPT_CODE_T2:
++                break;
++            default:
++                /* Copy the dhcp option to reply_dhcp_opts_ptr. */
++                ofpbuf_put(reply_dhcp_opts_ptr, in_dhcp_opt,
++                           in_dhcp_opt->len + sizeof *in_dhcp_opt);
++                break;
++            }
++
++            in_dhcp_ptr += sizeof *in_dhcp_opt;
++            if (in_dhcp_ptr > end) {
++                break;
++            }
++            in_dhcp_ptr += in_dhcp_opt->len;
++            if (in_dhcp_ptr > end) {
++                break;
++            }
++        }
++
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(20, 40);
++        VLOG_INFO_RL(&rl, "DHCPINFORM from "ETH_ADDR_FMT " "IP_FMT"",
++                     ETH_ADDR_ARGS(in_flow->dl_src),
++                     IP_ARGS(in_flow->nw_src));
++
++        break;
++    }
++    default: {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl, "Invalid DHCP message type: %d", *in_dhcp_msg_type);
++        goto exit;
++    }
++    }
++
++    if (!msg_type) {
++        goto exit;
+     }
+ 
+     /* Frame the DHCP reply packet
+-     * Total DHCP options length will be options stored in the userdata +
+-     * 16 bytes. Note that the DHCP options stored in userdata are not included
+-     * in DHCPNAK messages.
++     * Total DHCP options length will be options stored in the
++     * reply_dhcp_opts_ptr + 16 bytes. Note that the DHCP options stored in
++     * reply_dhcp_opts_ptr are not included in DHCPNAK messages.
+      *
+      * --------------------------------------------------------------
+      *| 4 Bytes (dhcp cookie) | 3 Bytes (option type) | DHCP options |
+@@ -1146,7 +1210,7 @@ pinctrl_handle_put_dhcp_opts(
+      */
+     uint16_t new_l4_size = UDP_HEADER_LEN + DHCP_HEADER_LEN + 16;
+     if (msg_type != DHCP_MSG_NAK) {
+-        new_l4_size += userdata->size;
++        new_l4_size += reply_dhcp_opts_ptr->size;
+     }
+     size_t new_packet_size = pkt_in->l4_ofs + new_l4_size;
+ 
+@@ -1171,12 +1235,18 @@ pinctrl_handle_put_dhcp_opts(
+     struct dhcp_header *dhcp_data = dp_packet_put(
+         &pkt_out, dp_packet_pull(pkt_in, DHCP_HEADER_LEN), DHCP_HEADER_LEN);
+     dhcp_data->op = DHCP_OP_REPLY;
+-    dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip;
++
++    if (*in_dhcp_msg_type != OVN_DHCP_MSG_INFORM) {
++        dhcp_data->yiaddr = (msg_type == DHCP_MSG_NAK) ? 0 : *offer_ip;
++    } else {
++        dhcp_data->yiaddr = 0;
++    }
++
+     dp_packet_put(&pkt_out, &magic_cookie, sizeof(ovs_be32));
+ 
+     uint16_t out_dhcp_opts_size = 12;
+     if (msg_type != DHCP_MSG_NAK) {
+-      out_dhcp_opts_size += userdata->size;
++      out_dhcp_opts_size += reply_dhcp_opts_ptr->size;
+     }
+     uint8_t *out_dhcp_opts = dp_packet_put_zeros(&pkt_out,
+                                                  out_dhcp_opts_size);
+@@ -1187,8 +1257,9 @@ pinctrl_handle_put_dhcp_opts(
+     out_dhcp_opts += 3;
+ 
+     if (msg_type != DHCP_MSG_NAK) {
+-      memcpy(out_dhcp_opts, userdata->data, userdata->size);
+-      out_dhcp_opts += userdata->size;
++        memcpy(out_dhcp_opts, reply_dhcp_opts_ptr->data,
++               reply_dhcp_opts_ptr->size);
++        out_dhcp_opts += reply_dhcp_opts_ptr->size;
+     }
+ 
+     /* Padding */
+@@ -1236,6 +1307,10 @@ exit:
+     if (pkt_out_ptr) {
+         dp_packet_uninit(pkt_out_ptr);
+     }
++
++    if (dhcp_inform_reply_buf) {
++        ofpbuf_delete(dhcp_inform_reply_buf);
++    }
+ }
+ 
+ static bool
+@@ -1936,8 +2011,8 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
+         break;
+ 
+     case ACTION_OPCODE_PUT_DHCP_OPTS:
+-        pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &userdata,
+-                                     &continuation);
++        pinctrl_handle_put_dhcp_opts(swconn, &packet, &pin, &headers,
++                                     &userdata, &continuation);
+         break;
+ 
+     case ACTION_OPCODE_ND_NA:
+diff --git a/ovn/lib/ovn-l7.h b/ovn/lib/ovn-l7.h
+index c43218224..f81acb0f4 100644
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -34,6 +34,14 @@ struct gen_opts_map {
+ 
+ #define DHCP_BROADCAST_FLAG 0x8000
+ 
++/* These are not defined in ovs/lib/dhcp.h and hence defined here with
++ * OVN_DHCP_OPT_CODE_<opt_name>.
++ */
++#define OVN_DHCP_OPT_CODE_NETMASK      1
++#define OVN_DHCP_OPT_CODE_LEASE_TIME   51
++#define OVN_DHCP_OPT_CODE_T1           58
++#define OVN_DHCP_OPT_CODE_T2           59
++
+ #define DHCP_OPTION(NAME, CODE, TYPE) \
+     {.name = NAME, .code = CODE, .type = TYPE}
+ 
+@@ -161,6 +169,10 @@ struct dhcp_opt6_header {
+     ovs_be16 size;
+ };
+ 
++/* These are not defined in ovs/lib/dhcp.h, hence defining here. */
++#define OVN_DHCP_MSG_RELEASE        7
++#define OVN_DHCP_MSG_INFORM         8
++
+ /* Supported DHCPv6 Message Types */
+ #define DHCPV6_MSG_TYPE_SOLICIT     1
+ #define DHCPV6_MSG_TYPE_ADVT        2
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 0c545b0b2..203c1d80f 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -4639,6 +4639,12 @@ test_dhcp() {
+     done
+     if test $offer_ip != 0; then
+         local srv_mac=$1 srv_ip=$2 dhcp_reply_type=$3 expected_dhcp_opts=$4
++        local offered_ip=$offer_ip
++        if [[ "$dhcp_type" == "08" ]]; then
++            # DHCP ACK for DHCP INFORM should not have any offer ip.
++            offered_ip=00000000
++        fi
++
+         # total IP length will be the IP length of the request packet
+         # (which is 272 in our case) + 8 (padding bytes) + (expected_dhcp_opts / 2)
+         ip_len=`expr 280 + ${#expected_dhcp_opts} / 2`
+@@ -4654,7 +4660,7 @@ test_dhcp() {
+         if test $dhcp_reply_type = 06; then
+             reply=${reply}00000000
+         else
+-            reply=${reply}${offer_ip}
++            reply=${reply}${offered_ip}
+         fi
+         # next server ip address, relay agent ip address, client mac address
+         reply=${reply}0000000000000000${src_mac}
+@@ -4794,7 +4800,7 @@ rm -f 2.expected
+ ciaddr=`ip_to_hex 0 0 0 0`
+ offer_ip=0
+ request_ip=0
+-test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 0 1 1
++test_dhcp 2 f00000000002 09 0 $ciaddr $offer_ip $request_ip 0 1 1
+ 
+ # NXT_RESUMEs should be 4.
+ OVS_WAIT_UNTIL([test 4 = `cat ofctl_monitor*.log | grep -c NXT_RESUME`])
+@@ -4964,6 +4970,113 @@ AT_CHECK([cat 1.packets | cut -c -48], [0], [expout])
+ cat 1.expected | cut -c 53- > expout
+ AT_CHECK([cat 1.packets | cut -c 53-], [0], [expout])
+ 
++reset_pcap_file hv1-vif1 hv1/vif1
++reset_pcap_file hv1-vif2 hv1/vif2
++rm -f 1.expected
++rm -f 2.expected
++
++# Send DHCPRELEASE.
++offer_ip=0
++server_ip=`ip_to_hex 10 0 0 1`
++ciaddr=`ip_to_hex 10 0 0 6`
++request_ip=0
++expected_dhcp_opts=0
++test_dhcp 2 f00000000002 07 0 $ciaddr $offer_ip $request_ip 0 ff1000000001
++
++# NXT_RESUMEs should be 10.
++OVS_WAIT_UNTIL([test 10 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
++
++# There is no reply for this. Check for the INFO log in ovn-controller.log
++AT_CHECK([test 1 = $(cat hv1/ovn-controller.log | \
++grep "DHCPRELEASE f0:00:00:00:00:02 10.0.0.6" -c)])
++
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
++AT_CHECK([cat 2.packets], [0], [])
++
++reset_pcap_file hv1-vif1 hv1/vif1
++reset_pcap_file hv1-vif2 hv1/vif2
++rm -f 1.expected
++rm -f 2.expected
++
++# Send DHCPINFORM
++offer_ip=`ip_to_hex 10 0 0 6`
++server_ip=`ip_to_hex 10 0 0 1`
++ciaddr=$offer_ip
++request_ip=0
++src_ip=$offer_ip
++dst_ip=$server_ip
++# In the expected_dhcp_opts we should not see 330400000e10 which is
++# dhcp lease time option and 0104ffffff00 which is subnet mask option.
++expected_dhcp_opts=03040a00000136040a000001
++test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
++
++# NXT_RESUMEs should be 11.
++OVS_WAIT_UNTIL([test 11 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
++
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
++cat 2.expected | cut -c -48 > expout
++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
++# Skipping the IPv4 checksum.
++cat 2.expected | cut -c 53- > expout
++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
++
++# Now add the dhcp option T1 to the dhcp options.
++ovn-nbctl set dhcp_options ${d1} options:T1=4000
++
++reset_pcap_file hv1-vif1 hv1/vif1
++reset_pcap_file hv1-vif2 hv1/vif2
++rm -f 1.expected
++rm -f 2.expected
++
++# Send DHCPREQUEST to make sure that T1 is in the reply dhcp options.
++offer_ip=`ip_to_hex 10 0 0 6`
++server_ip=`ip_to_hex 10 0 0 1`
++ciaddr=$offer_ip
++request_ip=0
++src_ip=$offer_ip
++dst_ip=$server_ip
++# In the expected_dhcp_opts we should not see 330400000e10 which is
++# dhcp lease time option.
++expected_dhcp_opts=3a0400000fa0330400000e100104ffffff0003040a00000136040a000001
++test_dhcp 2 f00000000002 03 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
++
++# NXT_RESUMEs should be 12.
++OVS_WAIT_UNTIL([test 12 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
++
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
++cat 2.expected | cut -c -48 > expout
++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
++# Skipping the IPv4 checksum.
++cat 2.expected | cut -c 53- > expout
++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
++
++reset_pcap_file hv1-vif1 hv1/vif1
++reset_pcap_file hv1-vif2 hv1/vif2
++rm -f 1.expected
++rm -f 2.expected
++
++# Now send DHCPINFORM again.
++offer_ip=`ip_to_hex 10 0 0 6`
++server_ip=`ip_to_hex 10 0 0 1`
++ciaddr=00000000
++request_ip=0
++src_ip=$offer_ip
++dst_ip=$server_ip
++# In the expected_dhcp_opts we should not see 330400000e10 which is
++# dhcp lease time option and 0104ffffff00 which is subnet mask option.
++expected_dhcp_opts=03040a00000136040a000001
++test_dhcp 2 f00000000002 08 0 $ciaddr $offer_ip $request_ip 1 $src_ip $dst_ip ff1000000001 $server_ip 05 $expected_dhcp_opts
++
++# NXT_RESUMEs should be 13.
++OVS_WAIT_UNTIL([test 13 = $(cat ofctl_monitor*.log | grep -c NXT_RESUME)])
++
++$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
++cat 2.expected | cut -c -48 > expout
++AT_CHECK([cat 2.packets | cut -c -48], [0], [expout])
++# Skipping the IPv4 checksum.
++cat 2.expected | cut -c 53- > expout
++AT_CHECK([cat 2.packets | cut -c 53-], [0], [expout])
++
+ OVN_CLEANUP([hv1])
+ 
+ AT_CLEANUP
+-- 
+2.26.2
+
diff --git a/SOURCES/0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch b/SOURCES/0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch
new file mode 100644
index 0000000..96479f2
--- /dev/null
+++ b/SOURCES/0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch
@@ -0,0 +1,65 @@
+From b4ddab6aa4d08a04eb7b55337b3fbdcc30b916a6 Mon Sep 17 00:00:00 2001
+Message-Id: <b4ddab6aa4d08a04eb7b55337b3fbdcc30b916a6.1583175283.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Thu, 13 Feb 2020 17:49:37 +0100
+Subject: [PATCH 1/2] pinctrl: fix IP buffering with connection-tracking
+
+Whenever we need to reinject an IP packet buffered during L2 address
+resolution we need to preserve ovs ofport in order to let ovs
+connection tracking to properly SNAT/DNAT the packet.
+Do not overwrite the MFF_IN_PORT in consider_port_binding routine
+
+Suggested-by: Numan Siddique <numans@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/physical.c | 1 -
+ ovn/controller/pinctrl.c  | 5 ++++-
+ 2 files changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
+index 8f8a5ba27..95cbaba49 100644
+--- a/ovn/controller/physical.c
++++ b/ovn/controller/physical.c
+@@ -893,7 +893,6 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+         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;
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 57e450c26..01a1bcbe0 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -548,6 +548,7 @@ set_actions_and_enqueue_msg(struct rconn *swconn,
+ 
+ struct buffer_info {
+     struct ofpbuf ofpacts;
++    ofp_port_t ofp_port;
+     struct dp_packet *p;
+ };
+ 
+@@ -618,6 +619,8 @@ buffered_push_packet(struct buffered_packets *bp,
+     ofpbuf_init(&bi->ofpacts, 4096);
+ 
+     reload_metadata(&bi->ofpacts, md);
++    bi->ofp_port = md->flow.in_port.ofp_port;
++
+     struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&bi->ofpacts);
+     resubmit->in_port = OFPP_CONTROLLER;
+     resubmit->table_id = OFTABLE_REMOTE_OUTPUT;
+@@ -652,7 +655,7 @@ buffered_send_packets(struct rconn *swconn, struct buffered_packets *bp,
+             .ofpacts = bi->ofpacts.data,
+             .ofpacts_len = bi->ofpacts.size,
+         };
+-        match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
++        match_set_in_port(&po.flow_metadata, bi->ofp_port);
+         queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
+ 
+         ofpbuf_uninit(&bi->ofpacts);
+-- 
+2.24.1
+
diff --git a/SOURCES/0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch b/SOURCES/0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch
new file mode 100644
index 0000000..53fde8d
--- /dev/null
+++ b/SOURCES/0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch
@@ -0,0 +1,58 @@
+From 600a018ea1136e184b421c86da170b35d05e949f Mon Sep 17 00:00:00 2001
+Message-Id: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com>
+From: Han Zhou <hzhou@ovn.org>
+Date: Wed, 30 Oct 2019 12:38:34 -0700
+Subject: [PATCH 1/3] pinctrl.c: Fix maybe-uninitialized warnings with old GCC
+ versions.
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There are warnings with older GCC versions (e.g. 4.9.2):
+===
+../controller/pinctrl.c: In function ‘ipv6_ra_send’:
+../controller/pinctrl.c:2393:13: error: ‘r1’ may be used uninitialized in this function [-Werror=maybe-uninitialized]
+             memcpy(&dnssl[i], t1, strlen(t1));
+             ^
+../controller/pinctrl.c:2383:20: note: ‘r1’ was declared here
+         char *t1, *r1;
+                    ^
+===
+This is not a real problem but compile fails because of it. This patch
+fixes it by initializing the pointers to NULL, to avoid the warnings
+with older GCC.
+
+CC: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Fixes: 5a12a940f63a ("Add DNSSL support to OVN")
+Acked-by: Numan Siddique <nusiddiq@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ ovn/controller/pinctrl.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index ae1b8e1ca..04ccb25a8 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2422,7 +2422,7 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
+     size_t prev_l4_size = dp_packet_l4_size(b);
+     size_t size = sizeof(struct ovs_nd_dnssl);
+     struct ip6_hdr *nh = dp_packet_l3(b);
+-    char *t0, *r0, dnssl[255] = {};
++    char *t0, *r0 = NULL, dnssl[255] = {};
+     int i = 0;
+ 
+     /* Multiple DNS Search List must be 'comma' separated
+@@ -2433,7 +2433,7 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
+      */
+     for (t0 = strtok_r(dnssl_list, ",", &r0); t0;
+          t0 = strtok_r(NULL, ",", &r0)) {
+-        char *t1, *r1;
++        char *t1, *r1 = NULL;
+ 
+         size += strlen(t0) + 2;
+         if (size > sizeof(dnssl)) {
+-- 
+2.21.1
+
diff --git a/SOURCES/0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch b/SOURCES/0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch
new file mode 100644
index 0000000..da199ca
--- /dev/null
+++ b/SOURCES/0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch
@@ -0,0 +1,294 @@
+From 5dcb7ee1b36aa4a906f09a95479586f917d893c8 Mon Sep 17 00:00:00 2001
+Message-Id: <5dcb7ee1b36aa4a906f09a95479586f917d893c8.1575382494.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <d7b024a24ee249c3ce6bf593499fdb6a79d78706.1575382494.git.lorenzo.bianconi@redhat.com>
+References: <d7b024a24ee249c3ce6bf593499fdb6a79d78706.1575382494.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Thu, 28 Nov 2019 14:48:36 +0200
+Subject: [PATCH 2/2] Add support for Route Info Option in RA - RFC 4191
+
+Introduce support for Route Info Option sent in Router
+Advertisement according to RFC 4191. Route Info Option are
+configured providing route_info in ipv6_ra_configs column of
+Logical_Router_Port table. route_info is a comma separated string
+where each field provides PRF and prefix for a given route
+(e.g: HIGH-aef1::11/48,LOW-aef2::11/96)
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/pinctrl.c | 80 ++++++++++++++++++++++++++++++++++++++++
+ ovn/lib/ovn-l7.h         | 12 ++++++
+ ovn/northd/ovn-northd.c  |  6 +++
+ ovn/ovn-nb.xml           | 14 +++++++
+ tests/ovn.at             | 32 ++++++++++------
+ 5 files changed, 133 insertions(+), 11 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2199,6 +2199,7 @@ struct ipv6_ra_config {
+     struct in6_addr rdnss;
+     bool has_rdnss;
+     struct ds dnssl;
++    struct ds route_info;
+ };
+ 
+ struct ipv6_ra_state {
+@@ -2221,6 +2222,7 @@ ipv6_ra_config_delete(struct ipv6_ra_con
+     if (config) {
+         destroy_lport_addresses(&config->prefixes);
+         ds_destroy(&config->dnssl);
++        ds_destroy(&config->route_info);
+         free(config);
+     }
+ }
+@@ -2260,6 +2262,7 @@ ipv6_ra_update_config(const struct sbrec
+     config->mtu = smap_get_int(&pb->options, "ipv6_ra_mtu", ND_MTU_DEFAULT);
+     config->la_flags = IPV6_ND_RA_OPT_PREFIX_ON_LINK;
+     ds_init(&config->dnssl);
++    ds_init(&config->route_info);
+ 
+     const char *address_mode = smap_get(&pb->options, "ipv6_ra_address_mode");
+     if (!address_mode) {
+@@ -2317,6 +2320,11 @@ ipv6_ra_update_config(const struct sbrec
+         ds_put_buffer(&config->dnssl, dnssl, strlen(dnssl));
+     }
+ 
++    const char *route_info = smap_get(&pb->options, "ipv6_ra_route_info");
++    if (route_info) {
++        ds_put_buffer(&config->route_info, route_info, strlen(route_info));
++    }
++
+     return config;
+ 
+ fail:
+@@ -2424,6 +2432,74 @@ packet_put_ra_dnssl_opt(struct dp_packet
+                                                       prev_l4_size + size));
+ }
+ 
++static void
++packet_put_ra_route_info_opt(struct dp_packet *b, ovs_be32 lifetime,
++                             char *route_list)
++{
++    size_t prev_l4_size = dp_packet_l4_size(b);
++    struct ip6_hdr *nh = dp_packet_l3(b);
++    char *t0, *r0 = NULL;
++    size_t size = 0;
++
++    for (t0 = strtok_r(route_list, ",", &r0); t0;
++         t0 = strtok_r(NULL, ",", &r0)) {
++        struct ovs_nd_route_info nd_rinfo;
++        char *t1, *r1 = NULL;
++        int index;
++
++        for (t1 = strtok_r(t0, "-", &r1), index = 0; t1;
++             t1 = strtok_r(NULL, "-", &r1), index++) {
++
++            nd_rinfo.type = ND_OPT_ROUTE_INFO;
++            nd_rinfo.route_lifetime = lifetime;
++
++            switch (index) {
++            case 0:
++                if (!strcmp(t1, "HIGH")) {
++                    nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_HIGH;
++                } else if (!strcmp(t1, "LOW")) {
++                    nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_LOW;
++                } else {
++                    nd_rinfo.flags = IPV6_ND_RA_OPT_PRF_NORMAL;
++                }
++                break;
++            case 1: {
++                struct lport_addresses route;
++                uint8_t plen;
++
++                if (!extract_ip_addresses(t1, &route)) {
++                    return;
++                }
++                if (!route.n_ipv6_addrs) {
++                    destroy_lport_addresses(&route);
++                    return;
++                }
++
++                nd_rinfo.prefix_len = route.ipv6_addrs->plen;
++                plen = DIV_ROUND_UP(nd_rinfo.prefix_len, 64);
++                nd_rinfo.len = 1 + plen;
++                dp_packet_put(b, &nd_rinfo, sizeof(struct ovs_nd_route_info));
++                dp_packet_put(b, &route.ipv6_addrs->network, plen * 8);
++                size += sizeof(struct ovs_nd_route_info) + plen * 8;
++
++                destroy_lport_addresses(&route);
++                index = 0;
++                break;
++            }
++            default:
++                return;
++            }
++        }
++    }
++
++    nh->ip6_plen = htons(prev_l4_size + size);
++    struct ovs_ra_msg *ra = dp_packet_l4(b);
++    ra->icmph.icmp6_cksum = 0;
++    uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
++    ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra,
++                                                      prev_l4_size + size));
++}
++
+ /* Called with in the pinctrl_handler thread context. */
+ static long long int
+ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra)
+@@ -2463,6 +2539,10 @@ ipv6_ra_send(struct rconn *swconn, struc
+         packet_put_ra_dnssl_opt(&packet, htonl(0xffffffff),
+                                 ra->config->dnssl.string);
+     }
++    if (ra->config->route_info.length) {
++        packet_put_ra_route_info_opt(&packet, htonl(0xffffffff),
++                                     ra->config->route_info.string);
++    }
+ 
+     uint64_t ofpacts_stub[4096 / 8];
+     struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
+--- a/ovn/lib/ovn-l7.h
++++ b/ovn/lib/ovn-l7.h
+@@ -242,6 +242,18 @@ struct ovs_nd_dnssl {
+ };
+ BUILD_ASSERT_DECL(ND_DNSSL_OPT_LEN == sizeof(struct ovs_nd_dnssl));
+ 
++/* Route Information option RFC 4191 */
++#define ND_OPT_ROUTE_INFO       24
++#define ND_ROUTE_INFO_OPT_LEN    8
++struct ovs_nd_route_info {
++    u_int8_t type;  /* ND_OPT_ROUTE_INFO */
++    u_int8_t len;   /* 1, 2 or 3 */
++    u_int8_t prefix_len;
++    u_int8_t flags;
++    ovs_be32 route_lifetime;
++};
++BUILD_ASSERT_DECL(ND_ROUTE_INFO_OPT_LEN == sizeof(struct ovs_nd_route_info));
++
+ #define DHCPV6_DUID_LL      3
+ #define DHCPV6_HW_TYPE_ETH  1
+ 
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6763,6 +6763,12 @@ copy_ra_to_sb(struct ovn_port *op, const
+         smap_add(&options, "ipv6_ra_prf", prf);
+     }
+ 
++    const char *route_info = smap_get(&op->nbrp->ipv6_ra_configs,
++                                      "route_info");
++    if (route_info) {
++        smap_add(&options, "ipv6_ra_route_info", route_info);
++    }
++
+     sbrec_port_binding_set_options(op->sb, &options);
+     smap_destroy(&options);
+ }
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -1871,6 +1871,20 @@
+         </ul>
+       </column>
+ 
++      <column name="ipv6_ra_configs" key="route_info">
++        Route Info is used to configure Route Info Option sent in Router
++        Advertisment according to RFC 4191. Route Info is a comma
++        separated string where each field provides PRF and prefix for a
++        given route (e.g: HIGH-aef1::11/48,LOW-aef2::11/96)
++        Possible PRF values are:
++
++        <ul>
++          <li>HIGH: mapped to 0x01 in RA PRF field</li>
++          <li>MEDIUM: mapped to 0x00 in RA PRF field</li>
++          <li>LOW: mapped to 0x11 in RA PRF field</li>
++        </ul>
++      </column>
++
+       <column name="ipv6_ra_configs" key="mtu">
+         The recommended MTU for the link. Default is 0, which means no MTU
+         Option will be included in RA packet replied by ovn-controller.
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -11417,14 +11417,15 @@ construct_expected_ra() {
+     local ra_mo=$2
+     local rdnss=$3
+     local dnssl=$4
+-    local ra_prefix_la=$5
++    local route_info=$5
++    local ra_prefix_la=$6
+ 
+     local slla=0101${src_mac}
+     local mtu_opt=""
+     if test $mtu != 0; then
+         mtu_opt=05010000${mtu}
+     fi
+-    shift 5
++    shift 6
+ 
+     local prefix=""
+     while [[ $# -gt 0 ]] ; do
+@@ -11442,8 +11443,12 @@ construct_expected_ra() {
+     if test $dnssl != 0; then
+         dnssl_opt=1f030000ffffffff${dnssl}
+     fi
++    local route_info_opt=""
++    if test $route_info != 0; then
++        route_info_opt=${route_info}
++    fi
+ 
+-    local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}${dnssl_opt}
++    local ra=ff${ra_mo}ffff0000000000000000${slla}${mtu_opt}${prefix}${rdnss_opt}${dnssl_opt}${route_info_opt}
+     local icmp=8600XXXX${ra}
+ 
+     local ip_len=$(expr ${#icmp} / 2)
+@@ -11478,38 +11483,43 @@ ra_test() {
+ }
+ 
+ # Baseline test with no MTU
+-ra_test 0 00 0 0 c0 40 aef00000000000000000000000000000
++ra_test 0 00 0 0 0 c0 40 aef00000000000000000000000000000
+ 
+ # Now make sure an MTU option makes it
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:mtu=1500
+-ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000
++ra_test 000005dc 00 0 0 0 c0 40 aef00000000000000000000000000000
+ 
+ # Now test for multiple network prefixes
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw networks='aef0\:\:1/64 fd0f\:\:1/48'
+-ra_test 000005dc 00 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 00 0 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # Test PRF for default gw
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="LOW"
+-ra_test 000005dc 18 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 18 0 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # Now test for RDNSS
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:rdnss='aef0::11'
+ dns_addr=aef00000000000000000000000000011
+-ra_test 000005dc 18 $dns_addr 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 18 $dns_addr 0 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # Now test for DNSSL
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:dnssl="aa.bb.cc"
+ ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:router_preference="HIGH"
+ dnssl=02616102626202636300000000000000
+-ra_test 000005dc 08 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 08 $dns_addr $dnssl 0 c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++
++# Now test Route Info option
++ovn-nbctl --wait=hv set Logical_Router_port ro-sw ipv6_ra_configs:route_info="HIGH-aef1::11/48,LOW-aef2::11/96"
++route_info=18023008ffffffffaef100000000000018036018ffffffffaef20000000000000000000000000000
++ra_test 000005dc 08 $dns_addr $dnssl $route_info c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ ## Test a different address mode now
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateful
+-ra_test 000005dc 88 $dns_addr $dnssl 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 88 $dns_addr $dnssl $route_info 80 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ # And the other address mode
+ ovn-nbctl --wait=hv set Logical_Router_Port ro-sw ipv6_ra_configs:address_mode=dhcpv6_stateless
+-ra_test 000005dc 48 $dns_addr $dnssl c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
++ra_test 000005dc 48 $dns_addr $dnssl $route_info c0 40 aef00000000000000000000000000000 30 fd0f0000000000000000000000000000
+ 
+ OVN_CLEANUP([hv1],[hv2])
+ AT_CLEANUP
diff --git a/SOURCES/0002-Combine-conjunctions-with-identical-matches-into-one.patch b/SOURCES/0002-Combine-conjunctions-with-identical-matches-into-one.patch
new file mode 100644
index 0000000..37c06df
--- /dev/null
+++ b/SOURCES/0002-Combine-conjunctions-with-identical-matches-into-one.patch
@@ -0,0 +1,213 @@
+From b52c176dd76bc0321d9c8bfe1c9b4832daf64835 Mon Sep 17 00:00:00 2001
+From: Mark Michelson <mmichels@redhat.com>
+Date: Fri, 25 Oct 2019 13:25:12 -0400
+Subject: [PATCH 2/2] Combine conjunctions with identical matches into one
+ flow.
+
+As stated in previous commits, conjunctive matches have an issue where
+it is possible to install multiple flows that have identical matches.
+This results in ambiguity, and can lead to features (such as ACLs) not
+functioning properly.
+
+This change fixes the problem by combining conjunctions with identical
+matches into a single flow. As an example, in the past we may have had
+something like:
+
+nw_dst=10.0.0.1 actions=conjunction(2,1/2)
+nw_dst=10.0.0.1 actions=conjunction(3,1/2)
+
+This commit changes this into
+
+nw_dst=10.0.0.1 actions=conjunction(2,1/2),conjunction(3,1/2)
+
+This way, there is only a single flow with the proscribed match, and
+there is no ambiguity.
+
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/controller/lflow.c  |  5 ++--
+ ovn/controller/ofctrl.c | 74 +++++++++++++++++++++++++++++++++++++++++++++--------
+ ovn/controller/ofctrl.h |  6 +++++
+ tests/ovn.at        | 17 +++++-------
+ 4 files changed, 80 insertions(+), 22 deletions(-)
+
+diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
+index 24e3a521e..f34abcebc 100644
+--- a/ovn/controller/lflow.c
++++ b/ovn/controller/lflow.c
+@@ -734,8 +734,9 @@ consider_logical_flow(
+                 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);
++
++            ofctrl_add_or_append_flow(flow_table, ptable, lflow->priority, 0,
++                                      &m->match, &conj, &lflow->header_.uuid);
+             ofpbuf_uninit(&conj);
+         }
+     }
+diff --git a/ovn/controller/ofctrl.c b/ovn/controller/ofctrl.c
+index 3131baff0..10edd84fb 100644
+--- a/ovn/controller/ofctrl.c
++++ b/ovn/controller/ofctrl.c
+@@ -69,6 +69,11 @@ struct ovn_flow {
+     uint64_t cookie;
+ };
+ 
++static struct ovn_flow *ovn_flow_alloc(uint8_t table_id, uint16_t priority,
++                                       uint64_t cookie,
++                                       const struct match *match,
++                                       const struct ofpbuf *actions,
++                                       const struct uuid *sb_uuid);
+ 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,
+@@ -657,16 +662,8 @@ ofctrl_check_and_add_flow(struct ovn_desired_flow_table *flow_table,
+                           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;
++    struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match,
++                                        actions, sb_uuid);
+ 
+     ovn_flow_log(f, "ofctrl_add_flow");
+ 
+@@ -721,9 +718,66 @@ ofctrl_add_flow(struct ovn_desired_flow_table *desired_flows,
+     ofctrl_check_and_add_flow(desired_flows, table_id, priority, cookie,
+                               match, actions, sb_uuid, true);
+ }
++
++void
++ofctrl_add_or_append_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)
++{
++    struct ovn_flow *f = ovn_flow_alloc(table_id, priority, cookie, match,
++                                        actions, sb_uuid);
++
++    ovn_flow_log(f, "ofctrl_add_or_append_flow");
++
++    struct ovn_flow *existing;
++    existing = ovn_flow_lookup(&desired_flows->match_flow_table, f, false);
++    if (existing) {
++        /* There's already a flow with this particular match. Append the
++         * action to that flow rather than adding a new flow
++         */
++        uint64_t compound_stub[64 / 8];
++        struct ofpbuf compound;
++        ofpbuf_use_stub(&compound, compound_stub, sizeof(compound_stub));
++        ofpbuf_put(&compound, existing->ofpacts, existing->ofpacts_len);
++        ofpbuf_put(&compound, f->ofpacts, f->ofpacts_len);
++
++        free(existing->ofpacts);
++        existing->ofpacts = xmemdup(compound.data, compound.size);
++        existing->ofpacts_len = compound.size;
++
++        ofpbuf_uninit(&compound);
++        ovn_flow_destroy(f);
++    } else {
++        hmap_insert(&desired_flows->match_flow_table, &f->match_hmap_node,
++                    f->match_hmap_node.hash);
++        hindex_insert(&desired_flows->uuid_flow_table, &f->uuid_hindex_node,
++                      f->uuid_hindex_node.hash);
++    }
++}
+ 
+ /* ovn_flow. */
+ 
++static struct ovn_flow *
++ovn_flow_alloc(uint8_t table_id, uint16_t priority, uint64_t cookie,
++               const struct match *match, const struct ofpbuf *actions,
++               const struct uuid *sb_uuid)
++{
++    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;
++
++    return f;
++}
++
+ /* Returns a hash of the match key in 'f'. */
+ static uint32_t
+ ovn_flow_match_hash(const struct ovn_flow *f)
+diff --git a/ovn/controller/ofctrl.h b/ovn/controller/ofctrl.h
+index 1e9ac16b9..21d2ce648 100644
+--- a/ovn/controller/ofctrl.h
++++ b/ovn/controller/ofctrl.h
+@@ -70,6 +70,12 @@ void ofctrl_add_flow(struct ovn_desired_flow_table *, uint8_t table_id,
+                      const struct match *, const struct ofpbuf *ofpacts,
+                      const struct uuid *);
+ 
++void ofctrl_add_or_append_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);
++
+ void ofctrl_remove_flows(struct ovn_desired_flow_table *, const struct uuid *);
+ 
+ void ovn_desired_flow_table_init(struct ovn_desired_flow_table *);
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 641a646fc..50d8efeec 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -12247,7 +12247,7 @@ ovn-nbctl create Address_Set name=set1 \
+ addresses=\"10.0.0.4\",\"10.0.0.5\",\"10.0.0.6\"
+ ovn-nbctl create Address_Set name=set2 \
+ addresses=\"10.0.0.7\",\"10.0.0.8\",\"10.0.0.9\"
+-ovn-nbctl acl-add ls1 to-lport 1002 \
++ovn-nbctl acl-add ls1 to-lport 1001 \
+ 'ip4 && ip4.src == $set1 && ip4.dst == $set1' allow
+ ovn-nbctl acl-add ls1 to-lport 1001 \
+ 'ip4 && ip4.src == $set1 && ip4.dst == $set2' drop
+@@ -12296,7 +12296,7 @@ cat 2.expected > expout
+ $PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap > 2.packets
+ AT_CHECK([cat 2.packets], [0], [expout])
+ 
+-# There should be total of 12 flows present with conjunction action and 2 flows
++# There should be total of 9 flows present with conjunction action and 2 flows
+ # with conj match. Eg.
+ # table=44, priority=2002,conj_id=2,metadata=0x1 actions=resubmit(,45)
+ # table=44, priority=2001,conj_id=3,metadata=0x1 actions=drop
+@@ -12306,14 +12306,11 @@ AT_CHECK([cat 2.packets], [0], [expout])
+ # priority=2001,ip,metadata=0x1,nw_dst=10.0.0.7 actions=conjunction(3,2/2)
+ # priority=2001,ip,metadata=0x1,nw_dst=10.0.0.9 actions=conjunction(3,2/2)
+ # priority=2001,ip,metadata=0x1,nw_dst=10.0.0.8 actions=conjunction(3,2/2)
+-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(2,1/2)
+-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(2,1/2)
+-# priority=2002,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(2,1/2)
+-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(3,1/2)
+-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(3,1/2)
+-# priority=2001,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(3,1/2)
+-
+-OVS_WAIT_UNTIL([test 12 = `as hv1 ovs-ofctl dump-flows br-int | \
++# priority=2002,ip,metadata=0x1,nw_src=10.0.0.6 actions=conjunction(2,1/2),conjunction(3,1/2)
++# priority=2002,ip,metadata=0x1,nw_src=10.0.0.4 actions=conjunction(2,1/2),conjunction(3,1/2)
++# priority=2002,ip,metadata=0x1,nw_src=10.0.0.5 actions=conjunction(2,1/2),conjunction(3,1/2)
++
++OVS_WAIT_UNTIL([test 9 = `as hv1 ovs-ofctl dump-flows br-int | \
+ grep conjunction | wc -l`])
+ OVS_WAIT_UNTIL([test 2 = `as hv1 ovs-ofctl dump-flows br-int | \
+ grep conj_id | wc -l`])
+-- 
+2.14.5
+
diff --git a/SOURCES/0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch b/SOURCES/0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch
new file mode 100644
index 0000000..c292a9a
--- /dev/null
+++ b/SOURCES/0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch
@@ -0,0 +1,85 @@
+From b59b64c0be5f16791c77d1b0104f09fd424ac098 Mon Sep 17 00:00:00 2001
+Message-Id: <b59b64c0be5f16791c77d1b0104f09fd424ac098.1578588395.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com>
+References: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 7 Jan 2020 17:50:05 +0100
+Subject: [PATCH 2/3] DNSSL: copy dnssl string in order to avoid truncated
+ value
+
+ipv6_ra_send can run 2 times in a row before prepare_ipv6_ras updates
+the dnss list. Copy the dnss dynamic string in packet_put_ra_dnssl_opt
+in order to avoid sending truncated dnssl list on the wire.
+Moreover move ip6_hdr definition just before accessing it because the
+packet can be reallocated if the data area is not big enough for packet
+content
+
+Fixes: 5a12a940f ("Add DNSSL support to OVN")
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/pinctrl.c | 15 ++++++++++-----
+ 1 file changed, 10 insertions(+), 5 deletions(-)
+
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 04ccb25a8..43b4e9299 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2417,14 +2417,15 @@ packet_put_ra_rdnss_opt(struct dp_packet *b, uint8_t num,
+ 
+ static void
+ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
+-                        char *dnssl_list)
++                        char *dnssl_data)
+ {
++    char *dnssl_list, *t0, *r0 = NULL, dnssl[255] = {};
+     size_t prev_l4_size = dp_packet_l4_size(b);
+     size_t size = sizeof(struct ovs_nd_dnssl);
+-    struct ip6_hdr *nh = dp_packet_l3(b);
+-    char *t0, *r0 = NULL, dnssl[255] = {};
+     int i = 0;
+ 
++    dnssl_list = xstrdup(dnssl_data);
++
+     /* Multiple DNS Search List must be 'comma' separated
+      * (e.g. "a.b.c, d.e.f"). Domain names must be encoded
+      * as described in Section 3.1 of RFC1035.
+@@ -2437,7 +2438,7 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
+ 
+         size += strlen(t0) + 2;
+         if (size > sizeof(dnssl)) {
+-            return;
++            goto out;
+         }
+ 
+         for (t1 = strtok_r(t0, ".", &r1); t1;
+@@ -2449,6 +2450,8 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
+         dnssl[i++] = 0;
+     }
+     size = ROUND_UP(size, 8);
++
++    struct ip6_hdr *nh = dp_packet_l3(b);
+     nh->ip6_plen = htons(prev_l4_size + size);
+ 
+     struct ovs_nd_dnssl *nd_dnssl = dp_packet_put_uninit(b, sizeof *nd_dnssl);
+@@ -2464,6 +2467,8 @@ packet_put_ra_dnssl_opt(struct dp_packet *b, ovs_be32 lifetime,
+     uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+     ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra,
+                                                       prev_l4_size + size));
++out:
++    free(dnssl_list);
+ }
+ 
+ static void
+@@ -2571,7 +2576,7 @@ ipv6_ra_send(struct rconn *swconn, struct ipv6_ra_state *ra)
+     }
+     if (ra->config->dnssl.length) {
+         packet_put_ra_dnssl_opt(&packet, htonl(0xffffffff),
+-                                ra->config->dnssl.string);
++                                ds_cstr(&ra->config->dnssl));
+     }
+     if (ra->config->route_info.length) {
+         packet_put_ra_route_info_opt(&packet, htonl(0xffffffff),
+-- 
+2.21.1
+
diff --git a/SOURCES/0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch b/SOURCES/0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch
new file mode 100644
index 0000000..0cbb3d3
--- /dev/null
+++ b/SOURCES/0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch
@@ -0,0 +1,188 @@
+From 13895b0543721db71569134d101d472e482c3997 Mon Sep 17 00:00:00 2001
+Message-Id: <13895b0543721db71569134d101d472e482c3997.1583175283.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <b4ddab6aa4d08a04eb7b55337b3fbdcc30b916a6.1583175283.git.lorenzo.bianconi@redhat.com>
+References: <b4ddab6aa4d08a04eb7b55337b3fbdcc30b916a6.1583175283.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Mon, 2 Mar 2020 10:37:49 +0100
+Subject: [PATCH 2/2] Manage ARP process locally in a DVR scenario
+
+OVN currently performs L2 address resolution and IP buffering on the
+gw node. If the system relies on FIPs, OVN will re-inject the buffered
+IP packets on the gw node, while following packets will go though
+the localnet port on the compute node resulting in a ToR switch
+misconfiguration. This patch addresses the issue managing ARP
+and IP buffering locally if FIPs are configured on the node
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml | 37 +++++++++++++++++++++++--
+ ovn/northd/ovn-northd.c     | 55 ++++++++++++++++++++++++++++++++++++-
+ tests/ovn.at                | 14 ++++++++++
+ 3 files changed, 103 insertions(+), 3 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -2290,11 +2290,44 @@ output;
+ 
+       <li>
+         <p>
++          For distributed logical routers where one of the logical router ports
++          specifies a <code>redirect-chassis</code>, a priority-400 logical
++          flow for each <code>dnat_and_snat</code> NAT rules configured.
++          These flows will allow to properly forward traffic to the external
++          connections if available and avoid sending it through the tunnel.
++          Assuming the following NAT rule has been configured:
++        </p>
++
++        <pre>
++external_ip = <var>A</var>;
++external_mac = <var>B</var>;
++logical_ip = <var>C</var>;
++        </pre>
++
++        <p>
++          the following action will be applied:
++        </p>
++
++        <pre>
++ip.ttl--;
++reg0 = <var>ip.dst</var>;
++reg1 = <var>A</var>;
++eth.src = <var>B</var>;
++outport = <var>router-port</var>;
++next;
++        </pre>
++
++      </li>
++
++      <li>
++        <p>
+           IPv4 routing table.  For each route to IPv4 network <var>N</var> with
+           netmask <var>M</var>, on router port <var>P</var> with IP address
+           <var>A</var> and Ethernet
+           address <var>E</var>, a logical flow with match <code>ip4.dst ==
+-          <var>N</var>/<var>M</var></code>, whose priority is the number of
++          <var>N</var>/<var>M</var></code>, whose priority is <code>400</code>
++          + the number of 1-bits in <var>M</var> if the router port is not a
++          distributed gateway port, else the priority is the number of
+           1-bits in <var>M</var>, has the following actions:
+         </p>
+ 
+@@ -2621,7 +2654,7 @@ icmp4 {
+ 
+       <li>
+         For each NAT rule in the OVN Northbound database that can
+-        be handled in a distributed manner, a priority-100 logical
++        be handled in a distributed manner, a priority-200 logical
+         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
+         outport == <var>GW</var></code>, where <var>GW</var> is
+         the logical router distributed gateway port, with actions
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6598,6 +6598,43 @@ build_routing_policy_flow(struct hmap *l
+     ds_destroy(&actions);
+ }
+ 
++/* default logical flow prioriry for distributed routes */
++#define DROUTE_PRIO 400
++
++static void
++add_distributed_routes(struct hmap *lflows, struct ovn_datapath *od)
++{
++    struct ds actions = DS_EMPTY_INITIALIZER;
++    struct ds match = DS_EMPTY_INITIALIZER;
++
++    for (size_t i = 0; i < od->nbr->n_nat; i++) {
++        const struct nbrec_nat *nat = od->nbr->nat[i];
++
++        if (strcmp(nat->type, "dnat_and_snat") ||
++            !nat->external_mac) {
++            continue;
++        }
++
++        bool is_ipv4 = strchr(nat->logical_ip, '.') ? true : false;
++        ds_put_format(&match, "ip%s.src == %s && is_chassis_resident(\"%s\")",
++                      is_ipv4 ? "4" : "6", nat->logical_ip,
++                      nat->logical_port);
++        char *prefix = is_ipv4 ? "" : "xx";
++        ds_put_format(&actions, "outport = %s; eth.src = %s; "
++                      "%sreg0 = ip%s.dst; %sreg1 = %s; next;",
++                      od->l3dgw_port->json_key, nat->external_mac,
++                      prefix, is_ipv4 ? "4" : "6",
++                      prefix, nat->external_ip);
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, DROUTE_PRIO,
++                      ds_cstr(&match), ds_cstr(&actions));
++        ds_clear(&match);
++        ds_clear(&actions);
++    }
++
++    ds_destroy(&actions);
++    ds_destroy(&match);
++}
++
+ static void
+ add_route(struct hmap *lflows, const struct ovn_port *op,
+           const char *lrp_addr_s, const char *network_s, int plen,
+@@ -6616,6 +6653,13 @@ add_route(struct hmap *lflows, const str
+         priority = (plen * 2) + 1;
+     }
+ 
++    /* traffic for internal IPs of logical switch ports must be sent to
++     * the gw controller through the overlay tunnels
++     */
++    if (op->nbrp && !op->nbrp->n_gateway_chassis) {
++        priority += DROUTE_PRIO;
++    }
++
+     /* IPv6 link-local addresses must be scoped to the local router port. */
+     if (!is_ipv4) {
+         struct in6_addr network;
+@@ -8256,7 +8300,7 @@ build_lrouter_flows(struct hmap *datapat
+                               is_v6 ? "6" : "4",
+                               nat->logical_ip,
+                               od->l3dgw_port->json_key);
+-                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
++                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200,
+                               ds_cstr(&match), "next;");
+             }
+ 
+@@ -8532,6 +8576,15 @@ build_lrouter_flows(struct hmap *datapat
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1", "next;");
+     }
+ 
++    /* Logical router ingress table IP_ROUTING - IP routing for distributed
++     * logical router
++     */
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++        if (od->nbr && od->l3dgw_port) {
++            add_distributed_routes(lflows, od);
++        }
++    }
++
+     /* Logical router ingress table 7: IP Routing.
+      *
+      * A packet that arrives at this table is an IP packet that should be
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -9440,6 +9440,20 @@ AT_CHECK([as hv3 ovs-vsctl set Open_vSwi
+ OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-vsctl show | \
+ grep "Port patch-br-int-to-ln_port" | wc -l`])
+ 
++AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \
++grep "ip4.src == 10.0.0.3 && is_chassis_resident(\"foo1\")" -c`])
++AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \
++grep "ip4.src == 10.0.0.4 && is_chassis_resident(\"foo2\")" -c`])
++
++key=`ovn-sbctl --bare --columns tunnel_key list datapath_Binding lr0`
++# Check that the OVS flows appear for the dnat_and_snat entries in
++# lr_in_ip_routing table.
++OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \
++grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.3" -c`])
++
++OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \
++grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.4" -c`])
++
+ # Re-add nat-addresses option
+ ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
+ 
diff --git a/SOURCES/0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch b/SOURCES/0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch
new file mode 100644
index 0000000..5e602cd
--- /dev/null
+++ b/SOURCES/0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch
@@ -0,0 +1,291 @@
+From c0bdec23cc9967abdac8eeaa3b8d7cddafedacdb Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Wed, 28 Aug 2019 01:55:28 +0000
+Subject: [PATCH 02/12] OVN: Vlan backed DVR N-S, redirect-type option
+
+Background:
+With 795d7f24ce0e ("OVN: Enable E-W Traffic, Vlan backed DVR"), we have added
+support for E-W workflow for vlan backed DVRs.
+
+This series enables N-S workflow for vlan backed DVRs.
+
+Key difference between E-W and N-S traffic flow is that
+N-S flow requires a gateway chassis. A gateway chassis
+will be responsible for following:
+a. Doing Network Address Translation (NAT).
+b. Becoming entry and exit point for North->South
+   and South->North traffic respectively.
+
+OVN by default always uses overlay encapsulation to redirect
+the packet to gateway chassis. This series will enable
+the redirection to gateway chassis in the absence of encapsulation.
+
+This patch:
+a. Adds a new key-value in options of a router port.
+b. This new config key will be used by ovn-controller
+   to determine if a redirected packet will go out of
+   tunnel port or localnet port.
+c. key is "redirect-type" and it takes "overlay" and
+   "bridged" as values.
+d. Added ovn-nbctl command to set and get redirect-type
+   option on a router port.
+e. This new configuration is added because bridged or overlay
+   based forwarding is considered to be a logical switch property,
+   hence for a router configuration has to be done at the router port
+   level.
+
+Change-Id: Ic002afa5ddb1da22d5833df9d584de256ba3f65a
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/northd/ovn-northd.c   |  6 ++++
+ ovn/ovn-nb.xml            | 43 ++++++++++++++++++++++++++
+ ovn/utilities/ovn-nbctl.c | 64 +++++++++++++++++++++++++++++++++++++++
+ tests/ovn-nbctl.at        | 25 +++++++++++++++
+ tests/ovn-northd.at       | 30 ++++++++++++++++++
+ 5 files changed, 168 insertions(+)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 952d0ae8a..cd13f308e 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -2648,6 +2648,9 @@ ovn_port_update_sbrec(struct northd_context *ctx,
+         if (op->derived) {
+             const char *redirect_chassis = smap_get(&op->nbrp->options,
+                                                     "redirect-chassis");
++            const char *redirect_type = smap_get(&op->nbrp->options,
++                                                 "redirect-type");
++
+             int n_gw_options_set = 0;
+             if (op->nbrp->ha_chassis_group) {
+                 n_gw_options_set++;
+@@ -2739,6 +2742,9 @@ ovn_port_update_sbrec(struct northd_context *ctx,
+                 sbrec_port_binding_set_gateway_chassis(op->sb, NULL, 0);
+             }
+             smap_add(&new, "distributed-port", op->nbrp->name);
++            if (redirect_type) {
++                smap_add(&new, "redirect-type", redirect_type);
++            }
+         } else {
+             if (op->peer) {
+                 smap_add(&new, "peer", op->peer->key);
+diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
+index 0323a9d33..bce7463d2 100644
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -2032,6 +2032,49 @@
+           to <code>true</code>.
+         </p>
+       </column>
++
++      <column name="options" key="redirect-type">
++        <p>
++          This options dictates if a packet redirected to
++          <code>gateway chassis</code> will be overlay encapsulated
++          or go as a regular packet via the localnet port.
++        </p>
++
++        <p>
++          Option takes following values
++        </p>
++
++        <ul>
++          <li>
++            OVERLAY
++          </li>
++
++          <li>
++            BRIDGED
++          </li>
++        </ul>
++
++        <p>
++          OVERLAY option will ensure that redirected packet goes out as
++          encapsulation via the tunnel port.
++        </p>
++
++        <p>
++          BRIDGED option will ensure that redirected packet goes out
++          via the localnet port tagged with vlan (if configured).
++        </p>
++
++        <p>
++          OVERLAY is the default redirection type.
++        </p>
++
++        <p>
++          Option is applicable only to gateway chassis attached logical
++          router ports.
++        </p>
++
++      </column>
++
+     </group>
+ 
+     <group title="Attachment">
+diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
+index 497d6f231..112cc1d54 100644
+--- a/ovn/utilities/ovn-nbctl.c
++++ b/ovn/utilities/ovn-nbctl.c
+@@ -661,6 +661,14 @@ Logical router port commands:\n\
+                             ('enabled' or 'disabled')\n\
+   lrp-get-enabled PORT      get administrative state PORT\n\
+                             ('enabled' or 'disabled')\n\
++  lrp-set-redirect-type PORT TYPE\n\
++                            set whether redirected packet to gateway chassis\n\
++                            of PORT will be encapsulated or not\n\
++                            ('overlay' or 'vlan')\n\
++  lrp-get-redirect-type PORT\n\
++                            get whether redirected packet to gateway chassis\n\
++                            of PORT will be encapsulated or not\n\
++                            ('overlay' or 'vlan')\n\
+ \n\
+ Route commands:\n\
+   [--policy=POLICY] lr-route-add ROUTER PREFIX NEXTHOP [PORT]\n\
+@@ -4617,6 +4625,58 @@ nbctl_lrp_get_enabled(struct ctl_context *ctx)
+                   *lrp->enabled ? "enabled" : "disabled");
+ }
+ 
++/* Set the logical router port redirect type. */
++static void
++nbctl_lrp_set_redirect_type(struct ctl_context *ctx)
++{
++    const char *id = ctx->argv[1];
++    const char *type = ctx->argv[2];
++    const struct nbrec_logical_router_port *lrp = NULL;
++    struct smap lrp_options;
++
++    char *error = lrp_by_name_or_uuid(ctx, id, true, &lrp);
++    if (error) {
++        ctx->error = error;
++        return;
++    }
++
++    if (strcasecmp(type, "bridged") && strcasecmp(type, "overlay")) {
++        error = xasprintf("Invalid redirect type: %s", type);
++        ctx->error = error;
++        return;
++    }
++
++    smap_init(&lrp_options);
++    smap_clone(&lrp_options, &lrp->options);
++
++    if (smap_get(&lrp_options, "redirect-type")) {
++        smap_replace(&lrp_options, "redirect-type", type);
++    } else {
++        smap_add(&lrp_options, "redirect-type", type);
++    }
++
++    nbrec_logical_router_port_set_options(lrp, &lrp_options);
++
++    smap_destroy(&lrp_options);
++}
++
++static void
++nbctl_lrp_get_redirect_type(struct ctl_context *ctx)
++{
++    const char *id = ctx->argv[1];
++    const struct nbrec_logical_router_port *lrp = NULL;
++
++    char *error = lrp_by_name_or_uuid(ctx, id, true, &lrp);
++    if (error) {
++        ctx->error = error;
++        return;
++    }
++
++    const char *redirect_type = smap_get(&lrp->options, "redirect-type");
++    ds_put_format(&ctx->output, "%s\n",
++                  !redirect_type ? "overlay": redirect_type);
++}
++
+ struct ipv4_route {
+     int priority;
+     ovs_be32 addr;
+@@ -5623,6 +5683,10 @@ static const struct ctl_command_syntax nbctl_commands[] = {
+       NULL, "", RW },
+     { "lrp-get-enabled", 1, 1, "PORT", NULL, nbctl_lrp_get_enabled,
+       NULL, "", RO },
++    { "lrp-set-redirect-type", 2, 2, "PORT TYPE", NULL,
++      nbctl_lrp_set_redirect_type, NULL, "", RW },
++    { "lrp-get-redirect-type", 1, 1, "PORT", NULL, nbctl_lrp_get_redirect_type,
++      NULL, "", RO },
+ 
+     /* logical router route commands. */
+     { "lr-route-add", 3, 4, "ROUTER PREFIX NEXTHOP [PORT]", NULL,
+diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
+index 30849682c..620b778b7 100644
+--- a/tests/ovn-nbctl.at
++++ b/tests/ovn-nbctl.at
+@@ -1243,6 +1243,31 @@ lrp0-chassis1     1
+ 
+ dnl ---------------------------------------------------------------------
+ 
++OVN_NBCTL_TEST([ovn_nbctl_redirect_type], [logical router port redirect type], [
++AT_CHECK([ovn-nbctl lr-add lr0])
++AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24])
++AT_CHECK([ovn-nbctl lrp-get-redirect-type lrp0], [0], [dnl
++overlay
++])
++AT_CHECK([ovn-nbctl lrp-set-redirect-type lp0 bridged], [1], [],
++[ovn-nbctl: lp0: port name not found
++])
++AT_CHECK([ovn-nbctl lrp-set-redirect-type lrp0 bridged], [0], [])
++AT_CHECK([ovn-nbctl lrp-get-redirect-type lrp0], [0], [dnl
++bridged
++])
++AT_CHECK([ovn-nbctl lrp-set-redirect-type lrp0 overlay], [0], [])
++AT_CHECK([ovn-nbctl lrp-get-redirect-type lrp0], [0], [dnl
++overlay
++])
++AT_CHECK([ovn-nbctl lrp-set-redirect-type lrp0 abcd], [1], [],
++[ovn-nbctl: Invalid redirect type: abcd
++])
++
++])
++
++dnl ---------------------------------------------------------------------
++
+ OVN_NBCTL_TEST([ovn_nbctl_lrp_enable], [logical router port enable and disable], [
+ AT_CHECK([ovn-nbctl lr-add lr0])
+ AT_CHECK([ovn-nbctl lrp-add lr0 lrp0 00:00:00:01:02:03 192.168.1.1/24])
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index 0dea04edc..42033d589 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -936,3 +936,33 @@ OVS_WAIT_UNTIL([
+     test 0 = $?])
+ 
+ AT_CLEANUP
++
++AT_SETUP([ovn -- check Redirect Chassis propagation from NB to SB])
++AT_SKIP_IF([test $HAVE_PYTHON = no])
++ovn_start
++
++ovn-sbctl chassis-add gw1 geneve 127.0.0.1
++
++ovn-nbctl lr-add R1
++ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
++
++ovn-nbctl ls-add S1
++ovn-nbctl lsp-add S1 S1-R1
++ovn-nbctl lsp-set-type S1-R1 router
++ovn-nbctl lsp-set-addresses S1-R1 router
++ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
++
++ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
++
++uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1`
++echo "CR-LRP UUID is: " $uuid
++
++ovn-nbctl lrp-set-redirect-type R1-S1 bridged
++OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], [bridged
++])
++
++ovn-nbctl lrp-set-redirect-type R1-S1 overlay
++OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], [overlay
++])
++
++AT_CLEANUP
+-- 
+2.23.0
+
diff --git a/SOURCES/0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch b/SOURCES/0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch
new file mode 100644
index 0000000..25e0968
--- /dev/null
+++ b/SOURCES/0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch
@@ -0,0 +1,85 @@
+From c4621fefd5a5a9169125c104e6a81bf987b4c029 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Sun, 5 Jan 2020 00:46:37 +0530
+Subject: [PATCH 2/2] Restrict ARP/IPv6 ND replies for LB VIP only on chassis
+ redirect port
+
+Presently when ARP/ND request for the load balance VIP is received
+from the provider network, all the ovn-controllers' reply to the ARP/ND
+request which have ovn-bridge-mappings configured.
+
+This patch restricts these ARP/ND replies only on the chassis where the
+chassis redirect port of the distributed router port is resident.
+
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream commit 6a5783021c35429dbb98ecebbbfbd7cdf3983f73)
+
+Change-Id: I70b091730a7380ed8be63040ce30aa9e64a684bd
+---
+ ovn/northd/ovn-northd.8.xml | 14 ++++++++++++++
+ ovn/northd/ovn-northd.c     |  8 ++++++++
+ 2 files changed, 22 insertions(+)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 956a10362..11ef65964 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1670,6 +1670,13 @@ flags.loopback = 1;
+ output;
+         </pre>
+ 
++        <p>
++          If the router port <var>P</var> is a distributed gateway router
++          port, then the <code>is_chassis_resident(<var>P</var>)</code> is
++          also added in the match condition for the load balancer IPv4
++          VIP <var>A</var>.
++        </p>
++
+         <p>
+           IPv6: For a configured DNAT IP address or a load balancer
+           IPv6 VIP <var>A</var>, solicited node address <var>S</var>,
+@@ -1694,6 +1701,13 @@ nd_na {
+ }
+         </pre>
+ 
++        <p>
++          If the router port <var>P</var> is a distributed gateway router
++          port, then the <code>is_chassis_resident(<var>P</var>)</code>
++          is also added in the match condition for the load balancer IPv6
++          VIP <var>A</var>.
++        </p>
++
+         <p>
+           For the gateway port on a distributed logical router with NAT
+           (where one of the logical router ports specifies a
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 55734b090..655d6240a 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -7495,6 +7495,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                           "inport == %s && arp.tpa == %s && arp.op == 1",
+                           op->json_key, ip_address);
+ 
++            if (op == op->od->l3dgw_port) {
++                ds_put_format(&match, " && is_chassis_resident(%s)",
++                              op->od->l3redirect_port->json_key);
++            }
+             ds_clear(&actions);
+             ds_put_format(&actions,
+                           "eth.dst = eth.src; "
+@@ -7522,6 +7526,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                           "inport == %s && nd_ns && nd.target == %s",
+                           op->json_key, ip_address);
+ 
++            if (op == op->od->l3dgw_port) {
++                ds_put_format(&match, " && is_chassis_resident(%s)",
++                              op->od->l3redirect_port->json_key);
++            }
+             ds_clear(&actions);
+             ds_put_format(&actions,
+                           "nd_na { "
+-- 
+2.24.1
+
diff --git a/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch b/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch
new file mode 100644
index 0000000..2c75235
--- /dev/null
+++ b/SOURCES/0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch
@@ -0,0 +1,181 @@
+From d9ed450713eda62af1bec5009694b2d206c9f435 Mon Sep 17 00:00:00 2001
+Message-Id: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Mon, 25 May 2020 23:55:06 +0200
+Subject: [PATCH ovn 1/3] Revert "Manage ARP process locally in a DVR scenario"
+
+This reverts commit c0bf32d72f8b893bbe3cb64912b0fd259d71555f.
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+---
+ northd/ovn-northd.8.xml | 37 ++--------------------------
+ northd/ovn-northd.c     | 53 +----------------------------------------
+ tests/ovn.at            | 14 -----------
+ 3 files changed, 3 insertions(+), 101 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -2311,44 +2311,11 @@ output;
+ 
+       <li>
+         <p>
+-          For distributed logical routers where one of the logical router ports
+-          specifies a <code>redirect-chassis</code>, a priority-400 logical
+-          flow for each <code>dnat_and_snat</code> NAT rules configured.
+-          These flows will allow to properly forward traffic to the external
+-          connections if available and avoid sending it through the tunnel.
+-          Assuming the following NAT rule has been configured:
+-        </p>
+-
+-        <pre>
+-external_ip = <var>A</var>;
+-external_mac = <var>B</var>;
+-logical_ip = <var>C</var>;
+-        </pre>
+-
+-        <p>
+-          the following action will be applied:
+-        </p>
+-
+-        <pre>
+-ip.ttl--;
+-reg0 = <var>ip.dst</var>;
+-reg1 = <var>A</var>;
+-eth.src = <var>B</var>;
+-outport = <var>router-port</var>;
+-next;
+-        </pre>
+-
+-      </li>
+-
+-      <li>
+-        <p>
+           IPv4 routing table.  For each route to IPv4 network <var>N</var> with
+           netmask <var>M</var>, on router port <var>P</var> with IP address
+           <var>A</var> and Ethernet
+           address <var>E</var>, a logical flow with match <code>ip4.dst ==
+-          <var>N</var>/<var>M</var></code>, whose priority is <code>400</code>
+-          + the number of 1-bits in <var>M</var> if the router port is not a
+-          distributed gateway port, else the priority is the number of
++          <var>N</var>/<var>M</var></code>, whose priority is the number of
+           1-bits in <var>M</var>, has the following actions:
+         </p>
+ 
+@@ -2663,7 +2630,7 @@ icmp4 {
+     <ul>
+       <li>
+         For each NAT rule in the OVN Northbound database that can
+-        be handled in a distributed manner, a priority-200 logical
++        be handled in a distributed manner, a priority-100 logical
+         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
+         outport == <var>GW</var></code>, where <var>GW</var> is
+         the logical router distributed gateway port, with actions
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6655,43 +6655,6 @@ build_routing_policy_flow(struct hmap *l
+     ds_destroy(&actions);
+ }
+ 
+-/* default logical flow prioriry for distributed routes */
+-#define DROUTE_PRIO 400
+-
+-static void
+-add_distributed_routes(struct hmap *lflows, struct ovn_datapath *od)
+-{
+-    struct ds actions = DS_EMPTY_INITIALIZER;
+-    struct ds match = DS_EMPTY_INITIALIZER;
+-
+-    for (size_t i = 0; i < od->nbr->n_nat; i++) {
+-        const struct nbrec_nat *nat = od->nbr->nat[i];
+-
+-        if (strcmp(nat->type, "dnat_and_snat") ||
+-            !nat->external_mac) {
+-            continue;
+-        }
+-
+-        bool is_ipv4 = strchr(nat->logical_ip, '.') ? true : false;
+-        ds_put_format(&match, "ip%s.src == %s && is_chassis_resident(\"%s\")",
+-                      is_ipv4 ? "4" : "6", nat->logical_ip,
+-                      nat->logical_port);
+-        char *prefix = is_ipv4 ? "" : "xx";
+-        ds_put_format(&actions, "outport = %s; eth.src = %s; "
+-                      "%sreg0 = ip%s.dst; %sreg1 = %s; next;",
+-                      od->l3dgw_port->json_key, nat->external_mac,
+-                      prefix, is_ipv4 ? "4" : "6",
+-                      prefix, nat->external_ip);
+-        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, DROUTE_PRIO,
+-                      ds_cstr(&match), ds_cstr(&actions));
+-        ds_clear(&match);
+-        ds_clear(&actions);
+-    }
+-
+-    ds_destroy(&actions);
+-    ds_destroy(&match);
+-}
+-
+ static void
+ add_route(struct hmap *lflows, const struct ovn_port *op,
+           const char *lrp_addr_s, const char *network_s, int plen,
+@@ -6710,13 +6673,6 @@ add_route(struct hmap *lflows, const str
+         priority = (plen * 2) + 1;
+     }
+ 
+-    /* traffic for internal IPs of logical switch ports must be sent to
+-     * the gw controller through the overlay tunnels
+-     */
+-    if (op->nbrp && !op->nbrp->n_gateway_chassis) {
+-        priority += DROUTE_PRIO;
+-    }
+-
+     /* IPv6 link-local addresses must be scoped to the local router port. */
+     if (!is_ipv4) {
+         struct in6_addr network;
+@@ -8357,7 +8313,7 @@ build_lrouter_flows(struct hmap *datapat
+                               is_v6 ? "6" : "4",
+                               nat->logical_ip,
+                               od->l3dgw_port->json_key);
+-                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 200,
++                ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
+                               ds_cstr(&match), "next;");
+             }
+ 
+@@ -8633,15 +8589,6 @@ build_lrouter_flows(struct hmap *datapat
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_ND_RA_RESPONSE, 0, "1", "next;");
+     }
+ 
+-    /* Logical router ingress table IP_ROUTING - IP routing for distributed
+-     * logical router
+-     */
+-    HMAP_FOR_EACH (od, key_node, datapaths) {
+-        if (od->nbr && od->l3dgw_port) {
+-            add_distributed_routes(lflows, od);
+-        }
+-    }
+-
+     /* Logical router ingress table 7: IP Routing.
+      *
+      * A packet that arrives at this table is an IP packet that should be
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -9482,20 +9482,6 @@ AT_CHECK([as hv3 ovs-vsctl set Open_vSwi
+ OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-vsctl show | \
+ grep "Port patch-br-int-to-ln_port" | wc -l`])
+ 
+-AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \
+-grep "ip4.src == 10.0.0.3 && is_chassis_resident(\"foo1\")" -c`])
+-AT_CHECK([test 1 = `ovn-sbctl dump-flows lr0 | grep lr_in_ip_routing | \
+-grep "ip4.src == 10.0.0.4 && is_chassis_resident(\"foo2\")" -c`])
+-
+-key=`ovn-sbctl --bare --columns tunnel_key list datapath_Binding lr0`
+-# Check that the OVS flows appear for the dnat_and_snat entries in
+-# lr_in_ip_routing table.
+-OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \
+-grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.3" -c`])
+-
+-OVS_WAIT_UNTIL([test 1 = `as hv3 ovs-ofctl dump-flows br-int table=17 | \
+-grep "priority=400,ip,metadata=0x$key,nw_src=10.0.0.4" -c`])
+-
+ # Re-add nat-addresses option
+ ovn-nbctl lsp-set-options lrp0-rp router-port=lrp0 nat-addresses="router"
+ 
diff --git a/SOURCES/0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch b/SOURCES/0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch
new file mode 100644
index 0000000..7682c37
--- /dev/null
+++ b/SOURCES/0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch
@@ -0,0 +1,67 @@
+From be5b458778abfc6dd3d30c0f520c77186935f9b2 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Tue, 19 Nov 2019 17:52:01 +0530
+Subject: [PATCH 2/2] Skip IPv6 NS packets in router egress SNAT pipeline
+
+When ovn-controller injects IPv6 NS packet to learn the mac, in some
+setups it is observed that the packet is dropped by ovs-vswitchd. We see
+below logs
+
+2019-11-18T10:42:17.973Z|00001|ofproto_dpif_upcall(handler6)|INFO|received packet on unassociated datapath port 4294967295
+2019-11-18T10:42:18.221Z|00001|ofproto_dpif_upcall(revalidator8)|WARN|Failed to acquire udpif_key corresponding to unexpected flow (Invalid argument): ufid:9ba1081f-a692-4c1c-a79b-d1cf04175f7d
+
+Upon further debugging I noticed that, xlate_lookup() fails when there is upcall
+from kernel datapath because of ct related actions. When ovn-controller injects
+the packet it sets inport=CONTROLLER.
+
+This patch addresses this issue by avoiding the IPv6 NS packets to be
+sent to conntrack in the router egress pipeline.
+
+This should be ideally fixed in ovs-vswitchd.
+
+Reported-by: Russell Bryant <russell@ovn.org>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml | 9 +++++++++
+ ovn/northd/ovn-northd.c     | 5 +++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 0fac02340..8f8fd5c01 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -2704,6 +2704,15 @@ nd_ns {
+       changed based on the configuration in the OVN Northbound database.
+     </p>
+ 
++    <ul>
++      <li>
++        A priority-120 flow to advance the IPv6 Neighbor solicitation packet
++        to next table to skip SNAT. In the case where ovn-controller injects
++        an IPv6 Neighbor Solicitation packet (for <code>nd_ns</code> action)
++        we don't want the packet to go throught conntrack.
++      </li>
++    </ul>
++
+     <p>Egress Table 1: SNAT on Gateway Routers</p>
+ 
+     <ul>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index bcadcca3d..d94276b1e 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -7394,6 +7394,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 0, "1", "next;");
+         ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 0, "1", "next;");
+ 
++        /* Send the IPv6 NS packets to next table. When ovn-controller
++         * generates IPv6 NS (for the action - nd_ns{}), the injected
++         * packet would go through conntrack - which is not required. */
++        ovn_lflow_add(lflows, od, S_ROUTER_OUT_SNAT, 120, "nd_ns", "next;");
++
+         /* NAT rules are only valid on Gateway routers and routers with
+          * l3dgw_port (router has a port with "redirect-chassis"
+          * specified). */
+-- 
+2.23.0
+
diff --git a/SOURCES/0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch b/SOURCES/0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch
new file mode 100644
index 0000000..4a011f9
--- /dev/null
+++ b/SOURCES/0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch
@@ -0,0 +1,185 @@
+From 67b9788bb5c4de9a22f52ebfaee2701c804cd9e8 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 25 Oct 2019 17:20:32 -0400
+Subject: [PATCH 2/5] actions: Add IPv6 support to lflow NAT actions
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ include/ovn/actions.h     |  6 +++++-
+ ovn/lib/actions.c         | 35 +++++++++++++++++++++++++++--------
+ ovn/utilities/ovn-trace.c | 15 ++++++++++-----
+ tests/ovn.at              | 18 ++++++++++++------
+ 4 files changed, 54 insertions(+), 20 deletions(-)
+
+diff --git a/include/ovn/actions.h b/include/ovn/actions.h
+index 4e2f4d28d..f4997e9c9 100644
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -225,7 +225,11 @@ struct ovnact_ct_commit {
+ /* OVNACT_CT_DNAT, OVNACT_CT_SNAT. */
+ struct ovnact_ct_nat {
+     struct ovnact ovnact;
+-    ovs_be32 ip;
++    int family;
++    union {
++        struct in6_addr ipv6;
++        ovs_be32 ipv4;
++    };
+     uint8_t ltable;             /* Logical table ID of next table. */
+ };
+ 
+diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
+index 45f8715cf..7857f658a 100644
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -755,11 +755,18 @@ parse_ct_nat(struct action_context *ctx, const char *name,
+ 
+     if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
+         if (ctx->lexer->token.type != LEX_T_INTEGER
+-            || ctx->lexer->token.format != LEX_F_IPV4) {
+-            lexer_syntax_error(ctx->lexer, "expecting IPv4 address");
++            || (ctx->lexer->token.format != LEX_F_IPV4
++                && ctx->lexer->token.format != LEX_F_IPV6)) {
++            lexer_syntax_error(ctx->lexer, "expecting IPv4 or IPv6 address");
+             return;
+         }
+-        cn->ip = ctx->lexer->token.value.ipv4;
++        if (ctx->lexer->token.format == LEX_F_IPV4) {
++            cn->family = AF_INET;
++            cn->ipv4 = ctx->lexer->token.value.ipv4;
++        } else if (ctx->lexer->token.format == LEX_F_IPV6) {
++            cn->family = AF_INET6;
++            cn->ipv6 = ctx->lexer->token.value.ipv6;
++        }
+         lexer_get(ctx->lexer);
+ 
+         if (!lexer_force_match(ctx->lexer, LEX_T_RPAREN)) {
+@@ -784,8 +791,12 @@ static void
+ format_ct_nat(const struct ovnact_ct_nat *cn, const char *name, struct ds *s)
+ {
+     ds_put_cstr(s, name);
+-    if (cn->ip) {
+-        ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ip));
++    if (cn->family == AF_INET) {
++        ds_put_format(s, "("IP_FMT")", IP_ARGS(cn->ipv4));
++    } else if (cn->family == AF_INET6) {
++        ds_put_char(s, '(');
++        ipv6_format_addr(&cn->ipv6, s);
++        ds_put_char(s, ')');
+     }
+     ds_put_char(s, ';');
+ }
+@@ -831,9 +842,17 @@ encode_ct_nat(const struct ovnact_ct_nat *cn,
+     nat->flags = 0;
+     nat->range_af = AF_UNSPEC;
+ 
+-    if (cn->ip) {
++    if (cn->family == AF_INET) {
+         nat->range_af = AF_INET;
+-        nat->range.addr.ipv4.min = cn->ip;
++        nat->range.addr.ipv4.min = cn->ipv4;
++        if (snat) {
++            nat->flags |= NX_NAT_F_SRC;
++        } else {
++            nat->flags |= NX_NAT_F_DST;
++        }
++    } else if (cn->family == AF_INET6) {
++        nat->range_af = AF_INET6;
++        nat->range.addr.ipv6.min = cn->ipv6;
+         if (snat) {
+             nat->flags |= NX_NAT_F_SRC;
+         } else {
+@@ -843,7 +862,7 @@ encode_ct_nat(const struct ovnact_ct_nat *cn,
+ 
+     ofpacts->header = ofpbuf_push_uninit(ofpacts, nat_offset);
+     ct = ofpacts->header;
+-    if (cn->ip) {
++    if (cn->family == AF_INET || cn->family == AF_INET6) {
+         ct->flags |= NX_CT_F_COMMIT;
+     }
+     ofpact_finish(ofpacts, &ct->ofpact);
+diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
+index 103b25891..7ed4a3842 100644
+--- a/ovn/utilities/ovn-trace.c
++++ b/ovn/utilities/ovn-trace.c
+@@ -1886,7 +1886,7 @@ execute_ct_nat(const struct ovnact_ct_nat *ct_nat,
+                enum ovnact_pipeline pipeline, struct ovs_list *super)
+ {
+     bool is_dst = ct_nat->ovnact.type == OVNACT_CT_DNAT;
+-    if (!is_dst && dp->has_local_l3gateway && !ct_nat->ip) {
++    if (!is_dst && dp->has_local_l3gateway && ct_nat->family == AF_UNSPEC) {
+         /* "ct_snat;" has no visible effect in a gateway router. */
+         return;
+     }
+@@ -1897,10 +1897,15 @@ execute_ct_nat(const struct ovnact_ct_nat *ct_nat,
+     struct flow ct_flow = *uflow;
+     struct ds s = DS_EMPTY_INITIALIZER;
+     ds_put_format(&s, "ct_%cnat", direction[0]);
+-    if (ct_nat->ip) {
+-        ds_put_format(&s, "(ip4.%s="IP_FMT")", direction, IP_ARGS(ct_nat->ip));
+-        ovs_be32 *ip = is_dst ? &ct_flow.nw_dst : &ct_flow.nw_src;
+-        *ip = ct_nat->ip;
++    if (ct_nat->family != AF_UNSPEC) {
++        if (ct_nat->family == AF_INET) {
++            ds_put_format(&s, "(ip4.%s="IP_FMT")", direction,
++                          IP_ARGS(ct_nat->ipv4));
++        } else {
++            ds_put_format(&s, "(ip6.%s=", direction);
++            ipv6_format_addr(&ct_nat->ipv6, &s);
++            ds_put_char(&s, ')');
++        }
+ 
+         uint8_t state = is_dst ? CS_DST_NAT : CS_SRC_NAT;
+         ct_flow.ct_state |= state;
+diff --git a/tests/ovn.at b/tests/ovn.at
+index ccf6a5332..e38d14cdf 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -1027,15 +1027,18 @@ ct_dnat;
+ ct_dnat(192.168.1.2);
+     encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=192.168.1.2))
+     has prereqs ip
++ct_dnat(fd11::2);
++    encodes as ct(commit,table=19,zone=NXM_NX_REG11[0..15],nat(dst=fd11::2))
++    has prereqs ip
+ 
+ ct_dnat(192.168.1.2, 192.168.1.3);
+     Syntax error at `,' expecting `)'.
+ ct_dnat(foo);
+-    Syntax error at `foo' expecting IPv4 address.
++    Syntax error at `foo' expecting IPv4 or IPv6 address.
+ ct_dnat(foo, bar);
+-    Syntax error at `foo' expecting IPv4 address.
++    Syntax error at `foo' expecting IPv4 or IPv6 address.
+ ct_dnat();
+-    Syntax error at `)' expecting IPv4 address.
++    Syntax error at `)' expecting IPv4 or IPv6 address.
+ 
+ # ct_snat
+ ct_snat;
+@@ -1044,15 +1047,18 @@ ct_snat;
+ ct_snat(192.168.1.2);
+     encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=192.168.1.2))
+     has prereqs ip
++ct_snat(fd11::2);
++    encodes as ct(commit,table=19,zone=NXM_NX_REG12[0..15],nat(src=fd11::2))
++    has prereqs ip
+ 
+ ct_snat(192.168.1.2, 192.168.1.3);
+     Syntax error at `,' expecting `)'.
+ ct_snat(foo);
+-    Syntax error at `foo' expecting IPv4 address.
++    Syntax error at `foo' expecting IPv4 or IPv6 address.
+ ct_snat(foo, bar);
+-    Syntax error at `foo' expecting IPv4 address.
++    Syntax error at `foo' expecting IPv4 or IPv6 address.
+ ct_snat();
+-    Syntax error at `)' expecting IPv4 address.
++    Syntax error at `)' expecting IPv4 or IPv6 address.
+ 
+ # ct_clear
+ ct_clear;
+-- 
+2.23.0
+
diff --git a/SOURCES/0002-add-meter-support-to-trigger_event-action.patch b/SOURCES/0002-add-meter-support-to-trigger_event-action.patch
new file mode 100644
index 0000000..bd48e6b
--- /dev/null
+++ b/SOURCES/0002-add-meter-support-to-trigger_event-action.patch
@@ -0,0 +1,141 @@
+From da1253be2ed4bc2ff43d8eb2e2f57c4abdbd5e5f Mon Sep 17 00:00:00 2001
+Message-Id: <da1253be2ed4bc2ff43d8eb2e2f57c4abdbd5e5f.1568637354.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com>
+References: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 3 Sep 2019 16:22:52 +0200
+Subject: [PATCH ovn 2/3] add meter support to trigger_event action
+
+Introduce meter support to trigger_event action in order to not
+overload pinctrl thread under heavy load
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+---
+ include/ovn/actions.h |  1 +
+ ovn/lib/actions.c         | 43 +++++++++++++++++++++++++++++++++++++------
+ ovn/ovn-sb.xml            |  5 ++++-
+ tests/ovn.at          |  4 ++++
+ 4 files changed, 46 insertions(+), 7 deletions(-)
+
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -327,6 +327,7 @@ struct ovnact_controller_event {
+     int event_type;   /* controller event type */
+     struct ovnact_gen_option *options;
+     size_t n_options;
++    char *meter;
+ };
+ 
+ /* OVNACT_BIND_VPORT. */
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -1273,6 +1273,9 @@ format_TRIGGER_EVENT(const struct ovnact
+ {
+     ds_put_format(s, "trigger_event(event = \"%s\"",
+                   event_to_string(event->event_type));
++    if (event->meter) {
++        ds_put_format(s, ", meter = \"%s\"", event->meter);
++    }
+     for (const struct ovnact_gen_option *o = event->options;
+          o < &event->options[event->n_options]; o++) {
+         ds_put_cstr(s, ", ");
+@@ -1420,10 +1423,21 @@ encode_TRIGGER_EVENT(const struct ovnact
+                      const struct ovnact_encode_params *ep OVS_UNUSED,
+                      struct ofpbuf *ofpacts)
+ {
++    uint32_t meter_id = NX_CTLR_NO_METER;
+     size_t oc_offset;
+ 
++    if (event->meter) {
++        meter_id = ovn_extend_table_assign_id(ep->meter_table, event->meter,
++                                              ep->lflow_uuid);
++        if (meter_id == EXT_TABLE_ID_INVALID) {
++            VLOG_WARN("Unable to assign id for trigger meter: %s",
++                      event->meter);
++            return;
++        }
++    }
++
+     oc_offset = encode_start_controller_op(ACTION_OPCODE_EVENT, false,
+-                                           NX_CTLR_NO_METER, ofpacts);
++                                           meter_id, ofpacts);
+     ovs_be32 ofs = htonl(event->event_type);
+     ofpbuf_put(ofpacts, &ofs, sizeof ofs);
+ 
+@@ -1738,11 +1752,27 @@ parse_trigger_event(struct action_contex
+                                      sizeof *event->options);
+         }
+ 
+-        struct ovnact_gen_option *o = &event->options[event->n_options++];
+-        memset(o, 0, sizeof *o);
+-        parse_gen_opt(ctx, o,
+-                      &ctx->pp->controller_event_opts->event_opts[event_type],
+-                      event_to_string(event_type));
++        if (lexer_match_id(ctx->lexer, "meter")) {
++            if (!lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
++                return;
++            }
++            /* If multiple meters are given, use the most recent. */
++            if (ctx->lexer->token.type == LEX_T_STRING &&
++                strlen(ctx->lexer->token.s)) {
++                free(event->meter);
++                event->meter = xstrdup(ctx->lexer->token.s);
++            } else if (ctx->lexer->token.type != LEX_T_STRING) {
++                lexer_syntax_error(ctx->lexer, "expecting string");
++                return;
++            }
++            lexer_get(ctx->lexer);
++        } else {
++            struct ovnact_gen_option *o = &event->options[event->n_options++];
++            memset(o, 0, sizeof *o);
++            parse_gen_opt(ctx, o,
++                    &ctx->pp->controller_event_opts->event_opts[event_type],
++                    event_to_string(event_type));
++            }
+         if (ctx->lexer->error) {
+             return;
+         }
+@@ -1763,6 +1793,7 @@ static void
+ ovnact_controller_event_free(struct ovnact_controller_event *event)
+ {
+     free_gen_options(event->options, event->n_options);
++    free(event->meter);
+ }
+ 
+ static void
+--- a/ovn/ovn-sb.xml
++++ b/ovn/ovn-sb.xml
+@@ -1994,7 +1994,9 @@ tcp.flags = RST;
+           <p>
+             This action is used to allow ovs-vswitchd to report CMS related
+             events writing them in <ref table="Controller_Event"/> table.
+-            Supported event:
++            It is possible to associate a meter to a each event in order to
++            not overload pinctrl thread under heavy load; each meter is
++            identified though a defined naming convention. Supported events:
+           </p>
+ 
+           <ul>
+@@ -2005,6 +2007,7 @@ tcp.flags = RST;
+                 no configured backend destinations. For this event, the event
+                 info includes the load balancer VIP, the load balancer UUID,
+                 and the transport protocol.
++                Associated meter: <code>event-elb</code>
+               </p>
+             </li>
+           </ul>
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -1363,6 +1363,10 @@ tcp_reset { };
+ trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
+     encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63)
+ 
++trigger_event(event = "empty_lb_backends", meter="event-elb" vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
++    formats as trigger_event(event = "empty_lb_backends", meter = "event-elb", vip = "10.0.0.1:80", protocol = "tcp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
++    encodes as controller(userdata=00.00.00.0f.00.00.00.00.00.00.00.00.00.01.00.0b.31.30.2e.30.2e.30.2e.31.3a.38.30.00.02.00.03.74.63.70.00.03.00.24.31.32.33.34.35.36.37.38.2d.61.62.63.64.2d.39.38.37.36.2d.66.65.64.63.2d.31.31.31.31.39.66.38.65.37.64.36.63,meter_id=5)
++
+ # Testing invalid vip results in extra error messages from socket-util.c
+ trigger_event(event = "empty_lb_backends", vip = "10.0.0.1:80", protocol = "sctp", load_balancer = "12345678-abcd-9876-fedc-11119f8e7d6c");
+     Load balancer protocol 'sctp' is not 'tcp' or 'udp'
diff --git a/SOURCES/0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch b/SOURCES/0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch
new file mode 100644
index 0000000..c77dbde
--- /dev/null
+++ b/SOURCES/0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch
@@ -0,0 +1,65 @@
+From d6d98d46254435b0ce0507d694f37b401bd91b30 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Thu, 11 Jun 2020 18:44:41 +0530
+Subject: [PATCH 2/2] northd: By pass IPv6 Router Adv and Router Solicitation
+ packets from ACL stages.
+
+We already add below logical flows to by pass IPv6 Neighbor discovery packets
+from in/out ACL stage.
+
+table=6 (ls_in_acl          ), priority=65535, match=(nd), action=(next;)
+table=4 (ls_out_acl         ), priority=65535, match=(nd), action=(next;)
+
+This patch also adds nd_rs and nd_ra to these logical flows. Without these
+the IPv6 Router Adv packets generated by ovn-controller are dropped if
+CMS has configured ACLs.
+
+Reported-by: Jakub Libosvar <jlibosva@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+
+(cherry-picked from upstream master commit 90e5971018277ab0f383a56f59ffcfe17466a2c6)
+
+Change-Id: I33fcb3032fe946f2b2333a8cf2791af75dceaf44
+---
+ ovn/northd/ovn-northd.8.xml | 6 ++++++
+ ovn/northd/ovn-northd.c     | 6 ++++--
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 95b826944..3fb811bf6 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -421,6 +421,12 @@
+         ACL re-allow this connection.
+       </li>
+ 
++      <li>
++        A priority-65535 flow that allows IPv6 Neighbor solicitation,
++        Neighbor discover, Router solicitation and Router advertisement
++        packets.
++      </li>
++
+       <li>
+         A priority 34000 logical flow is added for each logical switch datapath
+         with the match <code>eth.dst = <var>E</var></code> to allow the service
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index b3561f986..ab5c291c7 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5217,8 +5217,10 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
+         /* Ingress and Egress ACL Table (Priority 65535).
+          *
+          * Not to do conntrack on ND packets. */
+-        ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX, "nd", "next;");
+-        ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX, "nd", "next;");
++        ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, UINT16_MAX,
++                      "nd || nd_ra || nd_rs", "next;");
++        ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, UINT16_MAX,
++                      "nd || nd_ra || nd_rs", "next;");
+     }
+ 
+     /* Ingress or Egress ACL Table (Various priorities). */
+-- 
+2.26.2
+
diff --git a/SOURCES/0002-northd-Log-all-dynamic-address-assignments.patch b/SOURCES/0002-northd-Log-all-dynamic-address-assignments.patch
new file mode 100644
index 0000000..c26b700
--- /dev/null
+++ b/SOURCES/0002-northd-Log-all-dynamic-address-assignments.patch
@@ -0,0 +1,55 @@
+From e7461df9caf912b2040bb8e330ba68f538001113 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Sat, 7 Dec 2019 23:09:38 -0500
+Subject: [PATCH ovn 2/4] northd: Log all dynamic address assignments
+
+This patch adds INFO level log messages for all dynamic address
+assignments (MAC, IPv4, IPv6).  While debugging some issues in
+ovn-kubernetes, I found it would be helpful to see ovn-northd's view
+of what addresses were assigned where and when from its perspective.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+
+(cherry picked from upstream commit 50fdc3e4cfc487eeb9b81c07d645cf46f4557309)
+---
+ ovn/northd/ovn-northd.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 55734b0..40835a0 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -1708,6 +1708,8 @@ update_dynamic_addresses(struct dynamic_address_update *update)
+         break;
+     case DYNAMIC:
+         ip4 = htonl(ipam_get_unused_ip(update->od));
++        VLOG_INFO("Assigned dynamic IPv4 address '"IP_FMT"' to port '%s'",
++                  IP_ARGS(ip4), update->op->nbsp->name);
+     }
+ 
+     struct eth_addr mac;
+@@ -1722,6 +1724,8 @@ update_dynamic_addresses(struct dynamic_address_update *update)
+         break;
+     case DYNAMIC:
+         eth_addr_from_uint64(ipam_get_unused_mac(ip4), &mac);
++        VLOG_INFO("Assigned dynamic MAC address '"ETH_ADDR_FMT"' to port '%s'",
++                  ETH_ADDR_ARGS(mac), update->op->nbsp->name);
+         break;
+     }
+ 
+@@ -1739,6 +1743,11 @@ update_dynamic_addresses(struct dynamic_address_update *update)
+         break;
+     case DYNAMIC:
+         in6_generate_eui64(mac, &update->od->ipam_info.ipv6_prefix, &ip6);
++        struct ds ip6_ds = DS_EMPTY_INITIALIZER;
++        ipv6_format_addr(&ip6, &ip6_ds);
++        VLOG_INFO("Assigned dynamic IPv6 address '%s' to port '%s'",
++                  ip6_ds.string, update->op->nbsp->name);
++        ds_destroy(&ip6_ds);
+         break;
+     }
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0002-northd-add-the-possibility-to-define-localnet-as-qos.patch b/SOURCES/0002-northd-add-the-possibility-to-define-localnet-as-qos.patch
new file mode 100644
index 0000000..5dbd365
--- /dev/null
+++ b/SOURCES/0002-northd-add-the-possibility-to-define-localnet-as-qos.patch
@@ -0,0 +1,117 @@
+From 82e377ad94ff84f3b1fd82e9aa8ea8edbd5b9337 Mon Sep 17 00:00:00 2001
+Message-Id: <82e377ad94ff84f3b1fd82e9aa8ea8edbd5b9337.1569932887.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com>
+References: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 24 Sep 2019 18:39:55 +0200
+Subject: [PATCH ovn 2/3] northd: add the possibility to define localnet as qos
+ capable port
+
+Refactor allocate_chassis_queueid and free_chassis_queueid in order
+to get an unused queue_id even for localnet ports and add the
+the possibility to define localnet as qos capable port
+
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/northd/ovn-northd.c | 45 ++++++++++++++++++++++++++++++---------------
+ 1 file changed, 30 insertions(+), 15 deletions(-)
+
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -357,7 +357,7 @@ destroy_chassis_queues(struct hmap *set)
+ }
+ 
+ static void
+-add_chassis_queue(struct hmap *set, struct uuid *chassis_uuid,
++add_chassis_queue(struct hmap *set, const struct uuid *chassis_uuid,
+                   uint32_t queue_id)
+ {
+     struct ovn_chassis_qdisc_queues *node = xmalloc(sizeof *node);
+@@ -368,7 +368,7 @@ add_chassis_queue(struct hmap *set, stru
+ }
+ 
+ static bool
+-chassis_queueid_in_use(const struct hmap *set, struct uuid *chassis_uuid,
++chassis_queueid_in_use(const struct hmap *set, const struct uuid *chassis_uuid,
+                        uint32_t queue_id)
+ {
+     const struct ovn_chassis_qdisc_queues *node;
+@@ -383,31 +383,38 @@ chassis_queueid_in_use(const struct hmap
+ }
+ 
+ static uint32_t
+-allocate_chassis_queueid(struct hmap *set, struct sbrec_chassis *chassis)
++allocate_chassis_queueid(struct hmap *set, const struct uuid *uuid, char *name)
+ {
++    if (!uuid) {
++        return 0;
++    }
++
+     for (uint32_t queue_id = QDISC_MIN_QUEUE_ID + 1;
+          queue_id <= QDISC_MAX_QUEUE_ID;
+          queue_id++) {
+-        if (!chassis_queueid_in_use(set, &chassis->header_.uuid, queue_id)) {
+-            add_chassis_queue(set, &chassis->header_.uuid, queue_id);
++        if (!chassis_queueid_in_use(set, uuid, queue_id)) {
++            add_chassis_queue(set, uuid, queue_id);
+             return queue_id;
+         }
+     }
+ 
+     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+-    VLOG_WARN_RL(&rl, "all %s queue ids exhausted", chassis->name);
++    VLOG_WARN_RL(&rl, "all %s queue ids exhausted", name);
+     return 0;
+ }
+ 
+ static void
+-free_chassis_queueid(struct hmap *set, struct sbrec_chassis *chassis,
++free_chassis_queueid(struct hmap *set, const struct uuid *uuid,
+                      uint32_t queue_id)
+ {
+-    const struct uuid *chassis_uuid = &chassis->header_.uuid;
++    if (!uuid) {
++        return;
++    }
++
+     struct ovn_chassis_qdisc_queues *node;
+     HMAP_FOR_EACH_WITH_HASH (node, key_node,
+-                             hash_chassis_queue(chassis_uuid, queue_id), set) {
+-        if (uuid_equals(chassis_uuid, &node->chassis_uuid)
++                             hash_chassis_queue(uuid, queue_id), set) {
++        if (uuid_equals(uuid, &node->chassis_uuid)
+             && node->queue_id == queue_id) {
+             hmap_remove(set, &node->key_node);
+             free(node);
+@@ -2568,15 +2575,23 @@ ovn_port_update_sbrec(struct northd_cont
+             uint32_t queue_id = smap_get_int(
+                     &op->sb->options, "qdisc_queue_id", 0);
+             bool has_qos = port_has_qos_params(&op->nbsp->options);
++            const struct uuid *uuid = NULL;
+             struct smap options;
++            char *name = "";
++
++            if (!strcmp(op->nbsp->type, "localnet")) {
++                uuid = &op->sb->header_.uuid;
++                name = "localnet";
++            } else if (op->sb->chassis) {
++                uuid = &op->sb->chassis->header_.uuid;
++                name = op->sb->chassis->name;
++            }
+ 
+-            if (op->sb->chassis && has_qos && !queue_id) {
++            if (has_qos && !queue_id) {
+                 queue_id = allocate_chassis_queueid(chassis_qdisc_queues,
+-                                                    op->sb->chassis);
++                                                    uuid, name);
+             } else if (!has_qos && queue_id) {
+-                free_chassis_queueid(chassis_qdisc_queues,
+-                                     op->sb->chassis,
+-                                     queue_id);
++                free_chassis_queueid(chassis_qdisc_queues, uuid, queue_id);
+                 queue_id = 0;
+             }
+ 
diff --git a/SOURCES/0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch b/SOURCES/0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch
new file mode 100644
index 0000000..fe15b5b
--- /dev/null
+++ b/SOURCES/0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch
@@ -0,0 +1,799 @@
+From 9bb08e772a3ac946466906d89cf460d852e6144d Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Wed, 27 Nov 2019 14:15:22 +0100
+Subject: [PATCH ovn 2/4] ovn-controller: Add per node states to I-P engine.
+
+This commit transforms the 'changed' field in struct engine_node in a
+'state' field. Possible node states are:
+- "Stale": data in the node is not up to date with the DB.
+- "Updated": data in the node is valid but was updated during
+  the last run of the engine.
+- "Valid": data in the node is valid and didn't change during
+  the last run of the engine.
+- "Aborted": during the last run, processing was aborted for
+  this node.
+
+This commit also further refactors the I-P engine:
+- instead of recursively performing all the engine processing a
+  preprocessing stage is added (engine_get_nodes()) before the main processing
+  loop is executed in order to topologically sort nodes in the engine such
+  that all inputs of a given node appear in the sorted array before the node
+  itself. This simplifies a bit the code in engine_run().
+- remove the need for using an engine_run_id by using the newly added states.
+- turn the global 'engine_abort_recompute' into an argument to be passed to
+  engine_run(). It's relevant only in the current run context anyway as
+  we reset it before every call to engine_run().
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit 5ed53faecef12c09330ced445418c961cb1f8caf)
+
+Change-Id: I9c3322fe8eb51ffcad62797f5f2a7306bb0c53f4
+---
+ ovn/controller/ovn-controller.c | 101 +++++++++---------
+ ovn/lib/inc-proc-eng.c          | 231 ++++++++++++++++++++++++++++------------
+ ovn/lib/inc-proc-eng.h          |  74 +++++++++----
+ 3 files changed, 268 insertions(+), 138 deletions(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index ad5b067..8c474e9 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -722,10 +722,10 @@ en_ofctrl_is_connected_run(struct engine_node *node)
+         (struct ed_type_ofctrl_is_connected *)node->data;
+     if (data->connected != ofctrl_is_connected()) {
+         data->connected = !data->connected;
+-        node->changed = true;
++        engine_set_node_state(node, EN_UPDATED);
+         return;
+     }
+-    node->changed = false;
++    engine_set_node_state(node, EN_VALID);
+ }
+ 
+ struct ed_type_addr_sets {
+@@ -775,7 +775,7 @@ en_addr_sets_run(struct engine_node *node)
+     addr_sets_init(as_table, &as->addr_sets);
+ 
+     as->change_tracked = false;
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+ }
+ 
+ static bool
+@@ -794,11 +794,14 @@ addr_sets_sb_address_set_handler(struct engine_node *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);
++    if (!sset_is_empty(&as->new) || !sset_is_empty(&as->deleted) ||
++            !sset_is_empty(&as->updated)) {
++        engine_set_node_state(node, EN_UPDATED);
++    } else {
++        engine_set_node_state(node, EN_VALID);
++    }
+ 
+     as->change_tracked = true;
+-    node->changed = true;
+     return true;
+ }
+ 
+@@ -849,7 +852,7 @@ en_port_groups_run(struct engine_node *node)
+     port_groups_init(pg_table, &pg->port_groups);
+ 
+     pg->change_tracked = false;
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+ }
+ 
+ static bool
+@@ -868,11 +871,14 @@ port_groups_sb_port_group_handler(struct engine_node *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);
++    if (!sset_is_empty(&pg->new) || !sset_is_empty(&pg->deleted) ||
++            !sset_is_empty(&pg->updated)) {
++        engine_set_node_state(node, EN_UPDATED);
++    } else {
++        engine_set_node_state(node, EN_VALID);
++    }
+ 
+     pg->change_tracked = true;
+-    node->changed = true;
+     return true;
+ }
+ 
+@@ -1053,7 +1059,7 @@ en_runtime_data_run(struct engine_node *node)
+     update_ct_zones(local_lports, local_datapaths, ct_zones,
+                     ct_zone_bitmap, pending_ct_zones);
+ 
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+ }
+ 
+ static bool
+@@ -1119,10 +1125,10 @@ en_mff_ovn_geneve_run(struct engine_node *node)
+     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;
++        engine_set_node_state(node, EN_UPDATED);
+         return;
+     }
+-    node->changed = false;
++    engine_set_node_state(node, EN_VALID);
+ }
+ 
+ struct ed_type_flow_output {
+@@ -1284,7 +1290,7 @@ en_flow_output_run(struct engine_node *node)
+                  active_tunnels,
+                  flow_table);
+ 
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+ }
+ 
+ static bool
+@@ -1366,7 +1372,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node)
+               flow_table, group_table, meter_table, lfrr,
+               conj_id_ofs);
+ 
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+     return handled;
+ }
+ 
+@@ -1389,7 +1395,7 @@ flow_output_sb_mac_binding_handler(struct engine_node *node)
+     lflow_handle_changed_neighbors(sbrec_port_binding_by_name,
+             mac_binding_table, flow_table);
+ 
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+     return true;
+ }
+ 
+@@ -1493,7 +1499,7 @@ flow_output_sb_port_binding_handler(struct engine_node *node)
+             chassis, ct_zones, local_datapaths,
+             active_tunnels, flow_table);
+ 
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+     return true;
+ }
+ 
+@@ -1542,7 +1548,7 @@ flow_output_sb_multicast_group_handler(struct engine_node *node)
+             mff_ovn_geneve, chassis, ct_zones, local_datapaths,
+             flow_table);
+ 
+-    node->changed = true;
++    engine_set_node_state(node, EN_UPDATED);
+     return true;
+ 
+ }
+@@ -1656,7 +1662,9 @@ _flow_output_resource_ref_handler(struct engine_node *node,
+                     conj_id_ofs, &changed)) {
+             return false;
+         }
+-        node->changed = changed || node->changed;
++        if (changed) {
++            engine_set_node_state(node, EN_UPDATED);
++        }
+     }
+     SSET_FOR_EACH (ref_name, updated) {
+         if (!lflow_handle_changed_ref(ref_type, ref_name,
+@@ -1669,7 +1677,9 @@ _flow_output_resource_ref_handler(struct engine_node *node,
+                     conj_id_ofs, &changed)) {
+             return false;
+         }
+-        node->changed = changed || node->changed;
++        if (changed) {
++            engine_set_node_state(node, EN_UPDATED);
++        }
+     }
+     SSET_FOR_EACH (ref_name, new) {
+         if (!lflow_handle_changed_ref(ref_type, ref_name,
+@@ -1682,7 +1692,9 @@ _flow_output_resource_ref_handler(struct engine_node *node,
+                     conj_id_ofs, &changed)) {
+             return false;
+         }
+-        node->changed = changed || node->changed;
++        if (changed) {
++            engine_set_node_state(node, EN_UPDATED);
++        }
+     }
+ 
+     return true;
+@@ -1904,9 +1916,6 @@ main(int argc, char *argv[])
+     unixctl_command_register("recompute", "", 0, 0, engine_recompute_cmd,
+                              NULL);
+ 
+-    uint64_t engine_run_id = 0;
+-    bool engine_run_done = true;
+-
+     unsigned int ovs_cond_seqno = UINT_MAX;
+     unsigned int ovnsb_cond_seqno = UINT_MAX;
+ 
+@@ -1914,7 +1923,7 @@ main(int argc, char *argv[])
+     exiting = false;
+     restart = false;
+     while (!exiting) {
+-        engine_run_id++;
++        engine_init_run();
+ 
+         update_sb_db(ovs_idl_loop.idl, ovnsb_idl_loop.idl);
+         update_ssl_config(ovsrec_ssl_table_get(ovs_idl_loop.idl));
+@@ -2007,15 +2016,9 @@ main(int argc, char *argv[])
+                              * this round of engine_run and continue processing
+                              * acculated changes incrementally later when
+                              * ofctrl_can_put() returns true. */
+-                            if (engine_run_done) {
+-                                engine_set_abort_recompute(true);
+-                                engine_run_done = engine_run(&en_flow_output,
+-                                                             engine_run_id);
+-                            }
++                            engine_run(false);
+                         } else {
+-                            engine_set_abort_recompute(false);
+-                            engine_run_done = true;
+-                            engine_run(&en_flow_output, engine_run_id);
++                            engine_run(true);
+                         }
+                     }
+                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
+@@ -2034,7 +2037,7 @@ main(int argc, char *argv[])
+                                sbrec_meter_table_get(ovnsb_idl_loop.idl),
+                                get_nb_cfg(sbrec_sb_global_table_get(
+                                               ovnsb_idl_loop.idl)),
+-                               en_flow_output.changed);
++                               engine_node_changed(&en_flow_output));
+                     pinctrl_run(ovnsb_idl_txn,
+                                 sbrec_datapath_binding_by_key,
+                                 sbrec_port_binding_by_datapath,
+@@ -2052,7 +2055,7 @@ main(int argc, char *argv[])
+                                 &ed_runtime_data.local_datapaths,
+                                 &ed_runtime_data.active_tunnels);
+ 
+-                    if (en_runtime_data.changed) {
++                    if (engine_node_changed(&en_runtime_data)) {
+                         update_sb_monitors(ovnsb_idl_loop.idl, chassis,
+                                            &ed_runtime_data.local_lports,
+                                            &ed_runtime_data.local_datapaths);
+@@ -2060,20 +2063,23 @@ main(int argc, char *argv[])
+                 }
+ 
+             }
+-            if (engine_need_run(&en_flow_output, engine_run_id)) {
+-                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 if (!engine_run_done) {
++
++            if (!engine_has_run()) {
++                if (engine_need_run()) {
++                    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 if (engine_aborted()) {
+                 VLOG_DBG("engine was aborted, force recompute next time: "
+                          "br_int %p, chassis %p", br_int, chassis);
+                 engine_set_force_recompute(true);
+                 poll_immediate_wake();
+-            } else if (!engine_has_run(&en_flow_output, engine_run_id)) {
+-                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);
+             }
+@@ -2098,8 +2104,7 @@ main(int argc, char *argv[])
+                     }
+                 } else {
+                     VLOG_DBG("Pending_pkt conn but br_int %p or chassis "
+-                             "%p not ready. run-id: %"PRIu64, br_int,
+-                             chassis, engine_run_id);
++                             "%p not ready.", br_int, chassis);
+                     unixctl_command_reply_error(pending_pkt.conn,
+                         "ovn-controller not ready.");
+                 }
+@@ -2148,7 +2153,7 @@ main(int argc, char *argv[])
+     }
+ 
+     engine_set_context(NULL);
+-    engine_cleanup(&en_flow_output);
++    engine_cleanup();
+ 
+     /* It's time to exit.  Clean up the databases if we are not restarting */
+     if (!restart) {
+diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c
+index ff07ad9..59b5cac 100644
+--- a/ovn/lib/inc-proc-eng.c
++++ b/ovn/lib/inc-proc-eng.c
+@@ -31,21 +31,25 @@
+ VLOG_DEFINE_THIS_MODULE(inc_proc_eng);
+ 
+ static bool engine_force_recompute = false;
+-static bool engine_abort_recompute = false;
++static bool engine_run_aborted = false;
+ static const struct engine_context *engine_context;
+ 
++static struct engine_node **engine_nodes;
++static size_t engine_n_nodes;
++
++static const char *engine_node_state_name[EN_STATE_MAX] = {
++    [EN_STALE]   = "Stale",
++    [EN_UPDATED] = "Updated",
++    [EN_VALID]   = "Valid",
++    [EN_ABORTED] = "Aborted",
++};
++
+ void
+ engine_set_force_recompute(bool val)
+ {
+     engine_force_recompute = val;
+ }
+ 
+-void
+-engine_set_abort_recompute(bool val)
+-{
+-    engine_abort_recompute = val;
+-}
+-
+ const struct engine_context *
+ engine_get_context(void)
+ {
+@@ -58,26 +62,69 @@ engine_set_context(const struct engine_context *ctx)
+     engine_context = ctx;
+ }
+ 
+-void
+-engine_init(struct engine_node *node)
++/* Builds the topologically sorted 'sorted_nodes' array starting from
++ * 'node'.
++ */
++static struct engine_node **
++engine_topo_sort(struct engine_node *node, struct engine_node **sorted_nodes,
++                 size_t *n_count, size_t *n_size)
+ {
++    /* It's not so efficient to walk the array of already sorted nodes but
++     * we know that sorting is done only once at startup so it's ok for now.
++     */
++    for (size_t i = 0; i < *n_count; i++) {
++        if (sorted_nodes[i] == node) {
++            return sorted_nodes;
++        }
++    }
++
+     for (size_t i = 0; i < node->n_inputs; i++) {
+-        engine_init(node->inputs[i].node);
++        sorted_nodes = engine_topo_sort(node->inputs[i].node, sorted_nodes,
++                                        n_count, n_size);
+     }
+-    if (node->init) {
+-        node->init(node);
++    if (*n_count == *n_size) {
++        sorted_nodes = x2nrealloc(sorted_nodes, n_size, sizeof *sorted_nodes);
+     }
++    sorted_nodes[(*n_count)] = node;
++    (*n_count)++;
++    return sorted_nodes;
++}
++
++/* Return the array of topologically sorted nodes when starting from
++ * 'node'. Stores the number of nodes in 'n_count'.
++ */
++static struct engine_node **
++engine_get_nodes(struct engine_node *node, size_t *n_count)
++{
++    size_t n_size = 0;
++
++    *n_count = 0;
++    return engine_topo_sort(node, NULL, n_count, &n_size);
+ }
+ 
+ void
+-engine_cleanup(struct engine_node *node)
++engine_init(struct engine_node *node)
+ {
+-    for (size_t i = 0; i < node->n_inputs; i++) {
+-        engine_cleanup(node->inputs[i].node);
++    engine_nodes = engine_get_nodes(node, &engine_n_nodes);
++
++    for (size_t i = 0; i < engine_n_nodes; i++) {
++        if (engine_nodes[i]->init) {
++            engine_nodes[i]->init(engine_nodes[i]);
++        }
+     }
+-    if (node->cleanup) {
+-        node->cleanup(node);
++}
++
++void
++engine_cleanup(void)
++{
++    for (size_t i = 0; i < engine_n_nodes; i++) {
++        if (engine_nodes[i]->cleanup) {
++            engine_nodes[i]->cleanup(engine_nodes[i]);
++        }
+     }
++    free(engine_nodes);
++    engine_nodes = NULL;
++    engine_n_nodes = 0;
+ }
+ 
+ struct engine_node *
+@@ -128,16 +175,59 @@ engine_ovsdb_node_add_index(struct engine_node *node, const char *name,
+     ed->n_indexes ++;
+ }
+ 
++void
++engine_set_node_state_at(struct engine_node *node,
++                         enum engine_node_state state,
++                         const char *where)
++{
++    if (node->state == state) {
++        return;
++    }
++
++    VLOG_DBG("%s: node: %s, old_state %s, new_state %s",
++             where, node->name,
++             engine_node_state_name[node->state],
++             engine_node_state_name[state]);
++
++    node->state = state;
++}
++
++bool
++engine_node_changed(struct engine_node *node)
++{
++    return node->state == EN_UPDATED;
++}
++
+ bool
+-engine_has_run(struct engine_node *node, uint64_t run_id)
++engine_has_run(void)
+ {
+-    return node->run_id == run_id;
++    for (size_t i = 0; i < engine_n_nodes; i++) {
++        if (engine_nodes[i]->state != EN_STALE) {
++            return true;
++        }
++    }
++    return false;
++}
++
++bool
++engine_aborted(void)
++{
++    return engine_run_aborted;
++}
++
++void
++engine_init_run(void)
++{
++    VLOG_DBG("Initializing new run");
++    for (size_t i = 0; i < engine_n_nodes; i++) {
++        engine_set_node_state(engine_nodes[i], EN_STALE);
++    }
+ }
+ 
+ /* Do a full recompute (or at least try). If we're not allowed then
+  * mark the node as "aborted".
+  */
+-static bool
++static void
+ engine_recompute(struct engine_node *node, bool forced, bool allowed)
+ {
+     VLOG_DBG("node: %s, recompute (%s)", node->name,
+@@ -145,12 +235,12 @@ engine_recompute(struct engine_node *node, bool forced, bool allowed)
+ 
+     if (!allowed) {
+         VLOG_DBG("node: %s, recompute aborted", node->name);
+-        return false;
++        engine_set_node_state(node, EN_ABORTED);
++        return;
+     }
+ 
++    /* Run the node handler which might change state. */
+     node->run(node);
+-    VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
+-    return true;
+ }
+ 
+ /* Return true if the node could be computed, false otherwise. */
+@@ -159,7 +249,7 @@ engine_compute(struct engine_node *node, bool recompute_allowed)
+ {
+     for (size_t i = 0; i < node->n_inputs; i++) {
+         /* If the input node data changed call its change handler. */
+-        if (node->inputs[i].node->changed) {
++        if (node->inputs[i].node->state == EN_UPDATED) {
+             VLOG_DBG("node: %s, handle change for input %s",
+                      node->name, node->inputs[i].node->name);
+ 
+@@ -170,55 +260,40 @@ engine_compute(struct engine_node *node, bool recompute_allowed)
+                 VLOG_DBG("node: %s, can't handle change for input %s, "
+                          "fall back to recompute",
+                          node->name, node->inputs[i].node->name);
+-                return engine_recompute(node, false, recompute_allowed);
++                engine_recompute(node, false, recompute_allowed);
++                return (node->state != EN_ABORTED);
+             }
+         }
+     }
+-
+     return true;
+ }
+ 
+-bool engine_run(struct engine_node *node, uint64_t run_id)
++static void
++engine_run_node(struct engine_node *node, bool recompute_allowed)
+ {
+-    if (node->run_id == run_id) {
+-        /* The node was already updated in this run (could be input for
+-         * multiple other nodes). Stop processing.
+-         */
+-        return true;
+-    }
+-
+-    /* Initialize the node for this run. */
+-    node->run_id = run_id;
+-    node->changed = false;
+-
+     if (!node->n_inputs) {
++        /* Run the node handler which might change state. */
+         node->run(node);
+-        VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
+-        return true;
+-    }
+-
+-    for (size_t i = 0; i < node->n_inputs; i++) {
+-        if (!engine_run(node->inputs[i].node, run_id)) {
+-            return false;
+-        }
++        return;
+     }
+ 
+-    bool need_compute = false;
+-
+     if (engine_force_recompute) {
+-        return engine_recompute(node, true, !engine_abort_recompute);
++        engine_recompute(node, true, recompute_allowed);
++        return;
+     }
+ 
+     /* If any of the inputs updated data but there is no change_handler, then
+      * recompute the current node too.
+      */
++    bool need_compute = false;
+     for (size_t i = 0; i < node->n_inputs; i++) {
+-        if (node->inputs[i].node->changed) {
++        if (node->inputs[i].node->state == EN_UPDATED) {
+             need_compute = true;
+ 
+             /* Trigger a recompute if we don't have a change handler. */
+             if (!node->inputs[i].change_handler) {
+-                return engine_recompute(node, false, !engine_abort_recompute);
++                engine_recompute(node, false, recompute_allowed);
++                return;
+             }
+         }
+     }
+@@ -227,33 +302,55 @@ bool engine_run(struct engine_node *node, uint64_t run_id)
+         /* If we couldn't compute the node we either aborted or triggered
+          * a full recompute. In any case, stop processing.
+          */
+-        return engine_compute(node, !engine_abort_recompute);
++        if (!engine_compute(node, recompute_allowed)) {
++            return;
++        }
+     }
+ 
+-    VLOG_DBG("node: %s, changed: %d", node->name, node->changed);
+-    return true;
++    /* If we reached this point, either the node was updated or its state is
++     * still valid.
++     */
++    if (!engine_node_changed(node)) {
++        engine_set_node_state(node, EN_VALID);
++    }
+ }
+ 
+-bool
+-engine_need_run(struct engine_node *node, uint64_t run_id)
++void
++engine_run(bool recompute_allowed)
+ {
+-    size_t i;
+-
+-    if (node->run_id == run_id) {
+-        return false;
++    /* If the last run was aborted skip the incremental run because a
++     * recompute is needed first.
++     */
++    if (!recompute_allowed && engine_run_aborted) {
++        return;
+     }
+ 
+-    if (!node->n_inputs) {
+-        node->run(node);
+-        VLOG_DBG("input node: %s, changed: %d", node->name, node->changed);
+-        return node->changed;
++    engine_run_aborted = false;
++    for (size_t i = 0; i < engine_n_nodes; i++) {
++        engine_run_node(engine_nodes[i], recompute_allowed);
++
++        if (engine_nodes[i]->state == EN_ABORTED) {
++            engine_run_aborted = true;
++            return;
++        }
+     }
++}
+ 
+-    for (i = 0; i < node->n_inputs; i++) {
+-        if (engine_need_run(node->inputs[i].node, run_id)) {
++bool
++engine_need_run(void)
++{
++    for (size_t i = 0; i < engine_n_nodes; i++) {
++        /* Check only leaf nodes for updates. */
++        if (engine_nodes[i]->n_inputs) {
++            continue;
++        }
++
++        engine_nodes[i]->run(engine_nodes[i]);
++        VLOG_DBG("input node: %s, state: %s", engine_nodes[i]->name,
++                 engine_node_state_name[engine_nodes[i]->state]);
++        if (engine_nodes[i]->state == EN_UPDATED) {
+             return true;
+         }
+     }
+-
+     return false;
+ }
+diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h
+index a4151ca..2f3ff62 100644
+--- a/ovn/lib/inc-proc-eng.h
++++ b/ovn/lib/inc-proc-eng.h
+@@ -82,10 +82,21 @@ struct engine_node_input {
+     bool (*change_handler)(struct engine_node *node);
+ };
+ 
+-struct engine_node {
+-    /* A unique id to distinguish each iteration of the engine_run(). */
+-    uint64_t run_id;
++enum engine_node_state {
++    EN_STALE,     /* Data in the node is not up to date with the DB. */
++    EN_UPDATED,   /* Data in the node is valid but was updated during the
++                   * last run.
++                   */
++    EN_VALID,     /* Data in the node is valid and didn't change during the
++                   * last run.
++                   */
++    EN_ABORTED,   /* During the last run, processing was aborted for
++                   * this node.
++                   */
++    EN_STATE_MAX,
++};
+ 
++struct engine_node {
+     /* A unique name for each node. */
+     char *name;
+ 
+@@ -102,8 +113,8 @@ struct engine_node {
+      * node. */
+     void *data;
+ 
+-    /* Whether the data changed in the last engine run. */
+-    bool changed;
++    /* State of the node after the last engine run. */
++    enum engine_node_state state;
+ 
+     /* Method to initialize data. It may be NULL. */
+     void (*init)(struct engine_node *);
+@@ -116,23 +127,29 @@ struct engine_node {
+     void (*run)(struct engine_node *);
+ };
+ 
+-/* Initialize the data for the engine nodes recursively. It calls each node's
++/* Initialize the data for the engine nodes. It calls each node's
+  * init() method if not NULL. It should be called before the main loop. */
+-void engine_init(struct engine_node *);
++void engine_init(struct engine_node *node);
+ 
+-/* Execute the processing recursively, which should be called in the main
+- * loop. Returns true if the execution is compelte, false if it is aborted,
+- * which could happen when engine_abort_recompute is set. */
+-bool engine_run(struct engine_node *, uint64_t run_id);
++/* Initialize the engine nodes for a new run. It should be called in the
++ * main processing loop before every potential engine_run().
++ */
++void engine_init_run(void);
++
++/* Execute the processing, which should be called in the main loop.
++ * Updates the engine node's states accordingly. If 'recompute_allowed' is
++ * false and a recompute is required by the current engine run then the engine
++ * aborts.
++ */
++void engine_run(bool recompute_allowed);
+ 
+-/* Clean up the data for the engine nodes recursively. It calls each node's
++/* Clean up the data for the engine nodes. It calls each node's
+  * cleanup() method if not NULL. It should be called before the program
+  * terminates. */
+-void engine_cleanup(struct engine_node *);
++void engine_cleanup(void);
+ 
+ /* Check if engine needs to run but didn't. */
+-bool
+-engine_need_run(struct engine_node *, uint64_t run_id);
++bool engine_need_run(void);
+ 
+ /* Get the input node with <name> for <node> */
+ struct engine_node * engine_get_input(const char *input_name,
+@@ -151,16 +168,26 @@ void engine_add_input(struct engine_node *node, struct engine_node *input,
+  * iteration, and the change can't be tracked across iterations */
+ void engine_set_force_recompute(bool val);
+ 
+-/* Set the flag to cause engine execution to be aborted when there
+- * is any recompute to be triggered in any node. */
+-void engine_set_abort_recompute(bool val);
+-
+ const struct engine_context * engine_get_context(void);
+ 
+ void engine_set_context(const struct engine_context *);
+ 
+-/* Return true if the engine has run for 'node' in the 'run_id' iteration. */
+-bool engine_has_run(struct engine_node *node, uint64_t run_id);
++void engine_set_node_state_at(struct engine_node *node,
++                              enum engine_node_state state,
++                              const char *where);
++
++/* Return true if during the last iteration the node's data was updated. */
++bool engine_node_changed(struct engine_node *node);
++
++/* Return true if the engine has run in the last iteration. */
++bool engine_has_run(void);
++
++/* Returns true if during the last engine run we had to abort processing. */
++bool engine_aborted(void);
++
++/* Set the state of the node and log changes. */
++#define engine_set_node_state(node, state) \
++    engine_set_node_state_at(node, state, OVS_SOURCE_LOCATOR)
+ 
+ struct ed_ovsdb_index {
+     const char *name;
+@@ -187,6 +214,7 @@ void engine_ovsdb_node_add_index(struct engine_node *, const char *name,
+     struct engine_node en_##NAME = { \
+         .name = NAME_STR, \
+         .data = &ed_##NAME, \
++        .state = EN_STALE, \
+         .init = en_##NAME##_init, \
+         .run = en_##NAME##_run, \
+         .cleanup = en_##NAME##_cleanup, \
+@@ -201,10 +229,10 @@ en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \
+     const struct DB_NAME##rec_##TBL_NAME##_table *table = \
+         EN_OVSDB_GET(node); \
+     if (DB_NAME##rec_##TBL_NAME##_table_track_get_first(table)) { \
+-        node->changed = true; \
++        engine_set_node_state(node, EN_UPDATED); \
+         return; \
+     } \
+-    node->changed = false; \
++    engine_set_node_state(node, EN_VALID); \
+ } \
+ static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \
+             = NULL; \
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch b/SOURCES/0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch
new file mode 100644
index 0000000..b59c935
--- /dev/null
+++ b/SOURCES/0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch
@@ -0,0 +1,255 @@
+From f781f1bdb099c0bfc45e6bf732940f076f28c026 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 12 Sep 2019 16:14:20 +0200
+Subject: [PATCH 2/4] ovn-controller: Minimize SB DB port_binding lookups.
+
+Instead of storing only peer_ports in struct local_datapath, store both
+local-remote mappings for patch ports. Also, it's useful to directly
+store sbrec_port_binding pointers for all datapath ports as we avoid
+doing costly sbrec_port_binding index lookups by port name.
+
+Change-Id: Id8df886cab6bb4a81cd105d2ac52dd7e9aa03326
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Han Zhou <hzhou8@ebay.com>
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+
+(cherry-picked from upstream commit 89f5048f960c67e9dac3d45c6a9b25fa260e1a46)
+---
+ ovn/controller/binding.c        | 19 +++++++++---
+ ovn/controller/ovn-controller.h | 11 ++++++-
+ ovn/controller/physical.c       | 17 ++++++-----
+ ovn/controller/pinctrl.c        | 53 ++++++++-------------------------
+ 4 files changed, 48 insertions(+), 52 deletions(-)
+
+diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
+index c71731a1e..1380a3e6e 100644
+--- a/ovn/controller/binding.c
++++ b/ovn/controller/binding.c
+@@ -161,13 +161,24 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                                          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;
++                    if (ld->n_peer_ports > ld->n_allocated_peer_ports) {
++                        ld->peer_ports =
++                            x2nrealloc(ld->peer_ports,
++                                       &ld->n_allocated_peer_ports,
++                                       sizeof *ld->peer_ports);
++                    }
++                    ld->peer_ports[ld->n_peer_ports - 1].local = pb;
++                    ld->peer_ports[ld->n_peer_ports - 1].remote = peer;
+                 }
+             }
+         }
++
++        ld->n_ports++;
++        if (ld->n_ports > ld->n_allocated_ports) {
++            ld->ports = x2nrealloc(ld->ports, &ld->n_allocated_ports,
++                                   sizeof *ld->ports);
++        }
++        ld->ports[ld->n_ports - 1] = pb;
+     }
+     sbrec_port_binding_index_destroy_row(target);
+ }
+diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h
+index 078c9eabe..09ef4b632 100644
+--- a/ovn/controller/ovn-controller.h
++++ b/ovn/controller/ovn-controller.h
+@@ -60,8 +60,17 @@ struct local_datapath {
+      * hypervisor. */
+     bool has_local_l3gateway;
+ 
+-    const struct sbrec_port_binding **peer_ports;
++    const struct sbrec_port_binding **ports;
++    size_t n_ports;
++    size_t n_allocated_ports;
++
++    struct {
++        const struct sbrec_port_binding *local;
++        const struct sbrec_port_binding *remote;
++    } *peer_ports;
++
+     size_t n_peer_ports;
++    size_t n_allocated_peer_ports;
+ };
+ 
+ struct local_datapath *get_local_datapath(const struct hmap *,
+diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
+index 95cbaba49..b439a4852 100644
+--- a/ovn/controller/physical.c
++++ b/ovn/controller/physical.c
+@@ -259,11 +259,12 @@ put_remote_port_redirect_bridged(const struct
+ 
+         uint32_t ls_dp_key = 0;
+         for (int i = 0; i < ld->n_peer_ports; i++) {
+-            const struct sbrec_port_binding *sport_binding = ld->peer_ports[i];
+-            const char *sport_peer_name = smap_get(&sport_binding->options,
+-                                                   "peer");
+-            const char *distributed_port = smap_get(&binding->options,
+-                                                    "distributed-port");
++            const struct sbrec_port_binding *sport_binding =
++                ld->peer_ports[i].remote;
++            const char *sport_peer_name =
++                smap_get(&sport_binding->options, "peer");
++            const char *distributed_port =
++                smap_get(&binding->options, "distributed-port");
+ 
+             if (!strcmp(sport_peer_name, distributed_port)) {
+                 ls_dp_key = sport_binding->datapath->tunnel_key;
+@@ -525,7 +526,8 @@ put_replace_chassis_mac_flows(const struct simap *ct_zones,
+     struct zone_ids zone_ids = get_zone_ids(localnet_port, ct_zones);
+ 
+     for (int i = 0; i < ld->n_peer_ports; i++) {
+-        const struct sbrec_port_binding *rport_binding = ld->peer_ports[i];
++        const struct sbrec_port_binding *rport_binding =
++            ld->peer_ports[i].remote;
+         struct eth_addr router_port_mac;
+         char *err_str = NULL;
+         struct match match;
+@@ -632,7 +634,8 @@ put_replace_router_port_mac_flows(struct ovsdb_idl_index
+     }
+ 
+     for (int i = 0; i < ld->n_peer_ports; i++) {
+-        const struct sbrec_port_binding *rport_binding = ld->peer_ports[i];
++        const struct sbrec_port_binding *rport_binding =
++            ld->peer_ports[i].remote;
+         struct eth_addr router_port_mac;
+         struct match match;
+         struct ofpact_mac *replace_mac;
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 8bc99df4e..5636fc1e0 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -175,8 +175,7 @@ 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,
++run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
+                      const struct hmap *local_datapaths)
+     OVS_REQUIRES(pinctrl_mutex);
+ 
+@@ -240,10 +239,7 @@ static void wait_controller_event(struct ovsdb_idl_txn *ovnsb_idl_txn);
+ static void init_ipv6_ras(void);
+ static void destroy_ipv6_ras(void);
+ static void ipv6_ra_wait(long long int send_ipv6_ra_time);
+-static void prepare_ipv6_ras(
+-    struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+-    struct ovsdb_idl_index *sbrec_port_binding_by_name,
+-    const struct hmap *local_datapaths)
++static void prepare_ipv6_ras(const struct hmap *local_datapaths)
+     OVS_REQUIRES(pinctrl_mutex);
+ static void send_ipv6_ras(struct rconn *swconn,
+                           long long int *send_ipv6_ra_time)
+@@ -2216,8 +2212,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+     send_garp_prepare(sbrec_port_binding_by_datapath,
+                       sbrec_port_binding_by_name, br_int, chassis,
+                       local_datapaths, active_tunnels);
+-    prepare_ipv6_ras(sbrec_port_binding_by_datapath,
+-                     sbrec_port_binding_by_name, local_datapaths);
++    prepare_ipv6_ras(local_datapaths);
+     sync_dns_cache(dns_table);
+     controller_event_run(ovnsb_idl_txn, ce_table, chassis);
+     ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths,
+@@ -2225,8 +2220,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                   sbrec_port_binding_by_key,
+                   sbrec_igmp_groups,
+                   sbrec_ip_multicast_opts);
+-    run_buffered_binding(sbrec_port_binding_by_datapath,
+-                         sbrec_mac_binding_by_lport_ip,
++    run_buffered_binding(sbrec_mac_binding_by_lport_ip,
+                          local_datapaths);
+     sync_svc_monitors(ovnsb_idl_txn, svc_mon_table, sbrec_port_binding_by_name,
+                       chassis);
+@@ -2669,9 +2663,7 @@ send_ipv6_ras(struct rconn *swconn, long long int *send_ipv6_ra_time)
+ /* Called by pinctrl_run(). Runs with in the main ovn-controller
+  * thread context. */
+ static void
+-prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+-                 struct ovsdb_idl_index *sbrec_port_binding_by_name,
+-                 const struct hmap *local_datapaths)
++prepare_ipv6_ras(const struct hmap *local_datapaths)
+     OVS_REQUIRES(pinctrl_mutex)
+ {
+     struct shash_node *iter, *iter_next;
+@@ -2684,25 +2676,12 @@ prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+     bool changed = false;
+     const struct local_datapath *ld;
+     HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
+-        struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+-            sbrec_port_binding_by_datapath);
+-        sbrec_port_binding_index_set_datapath(target, ld->datapath);
+ 
+-        struct sbrec_port_binding *pb;
+-        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+-                                           sbrec_port_binding_by_datapath) {
+-            if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) {
+-                continue;
+-            }
++        for (size_t i = 0; i < ld->n_peer_ports; i++) {
++            const struct sbrec_port_binding *peer = ld->peer_ports[i].remote;
++            const struct sbrec_port_binding *pb = ld->peer_ports[i].local;
+ 
+-            const char *peer_s = smap_get(&pb->options, "peer");
+-            if (!peer_s) {
+-                continue;
+-            }
+-
+-            const struct sbrec_port_binding *peer
+-                = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
+-            if (!peer) {
++            if (!smap_get_bool(&pb->options, "ipv6_ra_send_periodic", false)) {
+                 continue;
+             }
+ 
+@@ -2741,7 +2720,6 @@ prepare_ipv6_ras(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+ 
+             /* pinctrl_handler thread will send the IPv6 RAs. */
+         }
+-        sbrec_port_binding_index_destroy_row(target);
+     }
+ 
+     /* Remove those that are no longer in the SB database */
+@@ -2987,8 +2965,7 @@ run_put_mac_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn,
+ }
+ 
+ static void
+-run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+-                     struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
++run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
+                      const struct hmap *local_datapaths)
+     OVS_REQUIRES(pinctrl_mutex)
+ {
+@@ -2996,13 +2973,10 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+     bool notify = false;
+ 
+     HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
+-        struct sbrec_port_binding *target = sbrec_port_binding_index_init_row(
+-                sbrec_port_binding_by_datapath);
+-        sbrec_port_binding_index_set_datapath(target, ld->datapath);
+ 
+-        const struct sbrec_port_binding *pb;
+-        SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+-                                           sbrec_port_binding_by_datapath) {
++        for (size_t i = 0; i < ld->n_ports; i++) {
++
++            const struct sbrec_port_binding *pb = ld->ports[i];
+             struct buffered_packets *cur_qp, *next_qp;
+             HMAP_FOR_EACH_SAFE (cur_qp, next_qp, hmap_node,
+                                 &buffered_packets_map) {
+@@ -3020,7 +2994,6 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_port_binding_by_datapath,
+                 ds_destroy(&ip_s);
+             }
+         }
+-        sbrec_port_binding_index_destroy_row(target);
+     }
+     buffered_packets_map_gc();
+ 
+-- 
+2.26.2
+
diff --git a/SOURCES/0002-ovn-northd-Add-IGMP-Relay-support.patch b/SOURCES/0002-ovn-northd-Add-IGMP-Relay-support.patch
new file mode 100644
index 0000000..11eb346
--- /dev/null
+++ b/SOURCES/0002-ovn-northd-Add-IGMP-Relay-support.patch
@@ -0,0 +1,1313 @@
+From 5e165afe5eca8e093879df006a6d61be2cbdda53 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Fri, 16 Aug 2019 14:20:45 +0200
+Subject: [PATCH 2/4] ovn-northd: Add IGMP Relay support
+
+Add a new configuration option 'mcast_relay' to the Logical_Router:options
+in the OVN Northbound database.
+
+If a router is configured with 'mcast_relay' enabled then ovn-northd
+will install Logical_Flows to allow IP multicast traffic to be routed
+between Logical_Switches. The logical router will aggregate all IGMP
+groups from attached logical switches and modify the routing pipeline in
+the following way:
+- Table S_ROUTER_IN_IP_INPUT: add flow allowing IP multicast traffic
+  if mcast_relay is enabled on the datapath.
+- Table S_ROUTER_IN_IP_ROUTING: add flow matching the group address,
+  update TTL and set outport="<Multicast_Group> associated with the
+  IGMP group". Continue to next table.
+- Table S_ROUTER_IN_ARP_RESOLVE: bypass ARP resolve for IP multicast
+  traffic and continue to next table.
+- Table S_ROUTER_OUT_DELIVERY: add flow matching IP multicast traffic
+  and set ETH.SRC to the MAC address of the logical port on which
+  traffic is forwarded.
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+
+(cherry-picked from ovn commit 5d1527b11e9420a36adc2410f78be6b6674c098e)
+---
+ NEWS                        |   1 +
+ ovn/lib/logical-fields.c    |   2 +
+ ovn/lib/mcast-group-index.h |  13 +-
+ ovn/northd/ovn-northd.8.xml |  79 +++++-
+ ovn/northd/ovn-northd.c     | 499 +++++++++++++++++++++++++++---------
+ ovn/ovn-nb.xml              |   6 +
+ ovn/ovn-sb.xml              |   2 +
+ tests/ovn.at                | 199 ++++++++++++--
+ 8 files changed, 650 insertions(+), 151 deletions(-)
+
+diff --git a/NEWS b/NEWS
+index c81890b7e..754ac2e71 100644
+--- a/NEWS
++++ b/NEWS
+@@ -64,6 +64,7 @@ v2.12.0 - 03 Sep 2019
+        members of the same transport zone(s).
+      * Support for IGMP Snooping and IGMP Querier.
+      * Support for new logical switch port type - 'virtual'.
++     * Support for IGMP Snooping/Querier and Relay.
+    - New QoS type "linux-netem" on Linux.
+    - Added support for TLS Server Name Indication (SNI).
+    - Linux datapath:
+diff --git a/ovn/lib/logical-fields.c b/ovn/lib/logical-fields.c
+index 4ad5bf481..8fb591c0a 100644
+--- a/ovn/lib/logical-fields.c
++++ b/ovn/lib/logical-fields.c
+@@ -156,6 +156,8 @@ ovn_init_symtab(struct shash *symtab)
+ 
+     expr_symtab_add_field(symtab, "ip4.src", MFF_IPV4_SRC, "ip4", false);
+     expr_symtab_add_field(symtab, "ip4.dst", MFF_IPV4_DST, "ip4", false);
++    expr_symtab_add_predicate(symtab, "ip4.src_mcast",
++                              "ip4.src[28..31] == 0xe");
+     expr_symtab_add_predicate(symtab, "ip4.mcast", "ip4.dst[28..31] == 0xe");
+ 
+     expr_symtab_add_predicate(symtab, "icmp4", "ip4 && ip.proto == 1");
+diff --git a/ovn/lib/mcast-group-index.h b/ovn/lib/mcast-group-index.h
+index 859e6a72f..6249cac99 100644
+--- a/ovn/lib/mcast-group-index.h
++++ b/ovn/lib/mcast-group-index.h
+@@ -20,8 +20,17 @@ struct ovsdb_idl;
+ 
+ struct sbrec_datapath_binding;
+ 
+-#define OVN_MCAST_FLOOD_TUNNEL_KEY   65535
+-#define OVN_MCAST_UNKNOWN_TUNNEL_KEY (OVN_MCAST_FLOOD_TUNNEL_KEY - 1)
++#define OVN_MIN_MULTICAST 32768
++#define OVN_MAX_MULTICAST 65535
++
++enum ovn_mcast_tunnel_keys {
++
++    OVN_MCAST_FLOOD_TUNNEL_KEY = OVN_MIN_MULTICAST,
++    OVN_MCAST_UNKNOWN_TUNNEL_KEY,
++    OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY,
++    OVN_MIN_IP_MULTICAST,
++    OVN_MAX_IP_MULTICAST = OVN_MAX_MULTICAST,
++};
+ 
+ struct ovsdb_idl_index *mcast_group_index_create(struct ovsdb_idl *);
+ const struct sbrec_multicast_group *
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 0435dae26..ec2b6454c 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -952,10 +952,40 @@ output;
+ 
+     <ul>
+       <li>
+-        A priority-100 flow that outputs all packets with an Ethernet broadcast
++        A priority-100 flow that punts all IGMP packets to
++        <code>ovn-controller</code> if IGMP snooping is enabled on the
++        logical switch.
++      </li>
++
++      <li>
++        Priority-90 flows that forward registered IP multicast traffic to
++        their corresponding multicast group, which <code>ovn-northd</code>
++        creates based on learnt <ref table="IGMP_Group" db="OVN_Southbound"/>
++        entries.  The flows also forward packets to the
++        <code>MC_MROUTER_FLOOD</code> multicast group, which
++        <code>ovn-nortdh</code> populates with all the logical ports that
++        are connected to logical routers with
++        <ref column="options" table="Logical_Router"/>:mcast_relay='true'.
++      </li>
++
++      <li>
++        A priority-85 flow that forwards all IP multicast traffic destined to
++        224.0.0.X to the <code>MC_FLOOD</code> multicast group, which
++        <code>ovn-northd</code> populates with all enabled logical ports.
++      </li>
++
++      <li>
++        A priority-80 flow that forwards all unregistered IP multicast traffic
++        to the <code>MC_MROUTER_FLOOD</code> multicast group, if any.
++        Otherwise the flow drops all unregistered IP multicast packets.  This
++        flow is added only if <ref column="other_config"
++        table="Logical_Switch"/>:mcast_flood_unregistered='false'.
++      </li>
++
++      <li>
++        A priority-70 flow that outputs all packets with an Ethernet broadcast
+         or multicast <code>eth.dst</code> to the <code>MC_FLOOD</code>
+-        multicast group, which <code>ovn-northd</code> populates with all
+-        enabled logical ports.
++        multicast group.
+       </li>
+ 
+       <li>
+@@ -1236,6 +1266,14 @@ output;
+         </ul>
+       </li>
+ 
++      <li>
++        <p>
++          A priority-95 flow allows IP multicast traffic if
++          <ref column="options" table="Logical_Router"/>:mcast_relay='true',
++          otherwise drops it.
++        </p>
++      </li>
++
+       <li>
+         <p>
+           ICMP echo reply.  These flows reply to ICMP echo requests received
+@@ -1982,6 +2020,16 @@ output;
+     </p>
+ 
+     <ul>
++      <li>
++        <p>
++          Priority-500 flows that match IP multicast traffic destined to
++          groups registered on any of the attached switches and sets
++          <code>outport</code> to the associated multicast group that will
++          eventually flood the traffic to all interested attached logical
++          switches. The flows also decrement TTL.
++        </p>
++      </li>
++
+       <li>
+         <p>
+           For distributed logical routers where one of the logical router
+@@ -2115,6 +2163,15 @@ next;
+     </p>
+ 
+     <ul>
++      <li>
++        <p>
++          A priority-500 flow that matches IP multicast traffic that was
++          allowed in the routing pipeline. For this kind of traffic the
++          <code>outport</code> was already set so the flow just advances to
++          the next table.
++        </p>
++      </li>
++
+       <li>
+         <p>
+           For distributed logical routers where one of the logical router
+@@ -2684,9 +2741,19 @@ clone {
+     <h3>Egress Table 3: Delivery</h3>
+ 
+     <p>
+-      Packets that reach this table are ready for delivery.  It contains
+-      priority-100 logical flows that match packets on each enabled logical
+-      router port, with action <code>output;</code>.
++      Packets that reach this table are ready for delivery.  It contains:
++      <ul>
++        <li>
++          Priority-110 logical flows that match IP multicast packets on each
++          enabled logical router port and modify the Ethernet source address
++          of the packets to the Ethernet address of the port and then execute
++          action <code>output;</code>.
++        </li>
++        <li>
++          Priority-100 logical flows that match packets on each enabled
++          logical router port, with action <code>output;</code>.
++        </li>
++      </ul>
+     </p>
+ 
+ </manpage>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index d81cfd893..83a7ec14f 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -440,32 +440,52 @@ struct ipam_info {
+     bool mac_only;
+ };
+ 
+-#define OVN_MIN_MULTICAST 32768
+-#define OVN_MAX_MULTICAST OVN_MCAST_FLOOD_TUNNEL_KEY
+-BUILD_ASSERT_DECL(OVN_MIN_MULTICAST < OVN_MAX_MULTICAST);
+-
+-#define OVN_MIN_IP_MULTICAST OVN_MIN_MULTICAST
+-#define OVN_MAX_IP_MULTICAST (OVN_MCAST_UNKNOWN_TUNNEL_KEY - 1)
+-BUILD_ASSERT_DECL(OVN_MAX_IP_MULTICAST >= OVN_MIN_MULTICAST);
+-
+ /*
+  * Multicast snooping and querier per datapath configuration.
+  */
++struct mcast_switch_info {
++
++    bool enabled;               /* True if snooping enabled. */
++    bool querier;               /* True if querier enabled. */
++    bool flood_unregistered;    /* True if unregistered multicast should be
++                                 * flooded.
++                                 */
++    bool flood_relay;           /* True if the switch is connected to a
++                                 * multicast router and unregistered multicast
++                                 * should be flooded to the mrouter. Only
++                                 * applicable if flood_unregistered == false.
++                                 */
++
++    int64_t table_size;         /* Max number of IP multicast groups. */
++    int64_t idle_timeout;       /* Timeout after which an idle group is
++                                 * flushed.
++                                 */
++    int64_t query_interval;     /* Interval between multicast queries. */
++    char *eth_src;              /* ETH src address of the multicast queries. */
++    char *ipv4_src;             /* IP src address of the multicast queries. */
++    int64_t query_max_response; /* Expected time after which reports should
++                                 * be received for queries that were sent out.
++                                 */
++
++    uint32_t active_flows;      /* Current number of active IP multicast
++                                 * flows.
++                                 */
++};
++
++struct mcast_router_info {
++    bool relay; /* True if the router should relay IP multicast. */
++};
++
+ struct mcast_info {
+-    bool enabled;
+-    bool querier;
+-    bool flood_unregistered;
+-
+-    int64_t table_size;
+-    int64_t idle_timeout;
+-    int64_t query_interval;
+-    char *eth_src;
+-    char *ipv4_src;
+-    int64_t  query_max_response;
+-
+-    struct hmap group_tnlids;
+-    uint32_t group_tnlid_hint;
+-    uint32_t active_flows;
++
++    struct hmap group_tnlids;  /* Group tunnel IDs in use on this DP. */
++    uint32_t group_tnlid_hint; /* Hint for allocating next group tunnel ID. */
++    struct ovs_list groups;    /* List of groups learnt on this DP. */
++
++    union {
++        struct mcast_switch_info sw;  /* Switch specific multicast info. */
++        struct mcast_router_info rtr; /* Router specific multicast info. */
++    };
+ };
+ 
+ static uint32_t
+@@ -566,6 +586,7 @@ ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
+ }
+ 
+ static void ovn_ls_port_group_destroy(struct hmap *nb_pgs);
++static void destroy_mcast_info_for_datapath(struct ovn_datapath *od);
+ 
+ static void
+ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
+@@ -579,12 +600,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
+         bitmap_free(od->ipam_info.allocated_ipv4s);
+         free(od->router_ports);
+         ovn_ls_port_group_destroy(&od->nb_pgs);
+-
+-        if (od->nbs) {
+-            free(od->mcast_info.eth_src);
+-            free(od->mcast_info.ipv4_src);
+-            destroy_tnlids(&od->mcast_info.group_tnlids);
+-        }
++        destroy_mcast_info_for_datapath(od);
+ 
+         free(od);
+     }
+@@ -721,23 +737,28 @@ init_ipam_info_for_datapath(struct ovn_datapath *od)
+ }
+ 
+ static void
+-init_mcast_info_for_datapath(struct ovn_datapath *od)
++init_mcast_info_for_router_datapath(struct ovn_datapath *od)
+ {
+-    if (!od->nbs) {
+-        return;
+-    }
++    struct mcast_router_info *mcast_rtr_info = &od->mcast_info.rtr;
+ 
+-    struct mcast_info *mcast_info = &od->mcast_info;
++    mcast_rtr_info->relay = smap_get_bool(&od->nbr->options, "mcast_relay",
++                                          false);
++}
+ 
+-    mcast_info->enabled =
++static void
++init_mcast_info_for_switch_datapath(struct ovn_datapath *od)
++{
++    struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
++
++    mcast_sw_info->enabled =
+         smap_get_bool(&od->nbs->other_config, "mcast_snoop", false);
+-    mcast_info->querier =
++    mcast_sw_info->querier =
+         smap_get_bool(&od->nbs->other_config, "mcast_querier", true);
+-    mcast_info->flood_unregistered =
++    mcast_sw_info->flood_unregistered =
+         smap_get_bool(&od->nbs->other_config, "mcast_flood_unregistered",
+                       false);
+ 
+-    mcast_info->table_size =
++    mcast_sw_info->table_size =
+         smap_get_ullong(&od->nbs->other_config, "mcast_table_size",
+                         OVN_MCAST_DEFAULT_MAX_ENTRIES);
+ 
+@@ -749,54 +770,94 @@ init_mcast_info_for_datapath(struct ovn_datapath *od)
+     } else if (idle_timeout > OVN_MCAST_MAX_IDLE_TIMEOUT_S) {
+         idle_timeout = OVN_MCAST_MAX_IDLE_TIMEOUT_S;
+     }
+-    mcast_info->idle_timeout = idle_timeout;
++    mcast_sw_info->idle_timeout = idle_timeout;
+ 
+     uint32_t query_interval =
+         smap_get_ullong(&od->nbs->other_config, "mcast_query_interval",
+-                        mcast_info->idle_timeout / 2);
++                        mcast_sw_info->idle_timeout / 2);
+     if (query_interval < OVN_MCAST_MIN_QUERY_INTERVAL_S) {
+         query_interval = OVN_MCAST_MIN_QUERY_INTERVAL_S;
+     } else if (query_interval > OVN_MCAST_MAX_QUERY_INTERVAL_S) {
+         query_interval = OVN_MCAST_MAX_QUERY_INTERVAL_S;
+     }
+-    mcast_info->query_interval = query_interval;
++    mcast_sw_info->query_interval = query_interval;
+ 
+-    mcast_info->eth_src =
++    mcast_sw_info->eth_src =
+         nullable_xstrdup(smap_get(&od->nbs->other_config, "mcast_eth_src"));
+-    mcast_info->ipv4_src =
++    mcast_sw_info->ipv4_src =
+         nullable_xstrdup(smap_get(&od->nbs->other_config, "mcast_ip4_src"));
+ 
+-    mcast_info->query_max_response =
++    mcast_sw_info->query_max_response =
+         smap_get_ullong(&od->nbs->other_config, "mcast_query_max_response",
+                         OVN_MCAST_DEFAULT_QUERY_MAX_RESPONSE_S);
+ 
+-    hmap_init(&mcast_info->group_tnlids);
+-    mcast_info->group_tnlid_hint = OVN_MIN_IP_MULTICAST;
+-    mcast_info->active_flows = 0;
++    mcast_sw_info->active_flows = 0;
++}
++
++static void
++init_mcast_info_for_datapath(struct ovn_datapath *od)
++{
++    if (!od->nbr && !od->nbs) {
++        return;
++    }
++
++    hmap_init(&od->mcast_info.group_tnlids);
++    od->mcast_info.group_tnlid_hint = OVN_MIN_IP_MULTICAST;
++    ovs_list_init(&od->mcast_info.groups);
++
++    if (od->nbs) {
++        init_mcast_info_for_switch_datapath(od);
++    } else {
++        init_mcast_info_for_router_datapath(od);
++    }
++}
++
++static void
++destroy_mcast_info_for_switch_datapath(struct ovn_datapath *od)
++{
++    struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
++
++    free(mcast_sw_info->eth_src);
++    free(mcast_sw_info->ipv4_src);
++}
++
++static void
++destroy_mcast_info_for_datapath(struct ovn_datapath *od)
++{
++    if (!od->nbr && !od->nbs) {
++        return;
++    }
++
++    if (od->nbs) {
++        destroy_mcast_info_for_switch_datapath(od);
++    }
++
++    destroy_tnlids(&od->mcast_info.group_tnlids);
+ }
+ 
+ static void
+-store_mcast_info_for_datapath(const struct sbrec_ip_multicast *sb,
+-                              struct ovn_datapath *od)
++store_mcast_info_for_switch_datapath(const struct sbrec_ip_multicast *sb,
++                                     struct ovn_datapath *od)
+ {
+-    struct mcast_info *mcast_info = &od->mcast_info;
++    struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
+ 
+     sbrec_ip_multicast_set_datapath(sb, od->sb);
+-    sbrec_ip_multicast_set_enabled(sb, &mcast_info->enabled, 1);
+-    sbrec_ip_multicast_set_querier(sb, &mcast_info->querier, 1);
+-    sbrec_ip_multicast_set_table_size(sb, &mcast_info->table_size, 1);
+-    sbrec_ip_multicast_set_idle_timeout(sb, &mcast_info->idle_timeout, 1);
++    sbrec_ip_multicast_set_enabled(sb, &mcast_sw_info->enabled, 1);
++    sbrec_ip_multicast_set_querier(sb, &mcast_sw_info->querier, 1);
++    sbrec_ip_multicast_set_table_size(sb, &mcast_sw_info->table_size, 1);
++    sbrec_ip_multicast_set_idle_timeout(sb, &mcast_sw_info->idle_timeout, 1);
+     sbrec_ip_multicast_set_query_interval(sb,
+-                                          &mcast_info->query_interval, 1);
++                                          &mcast_sw_info->query_interval, 1);
+     sbrec_ip_multicast_set_query_max_resp(sb,
+-                                          &mcast_info->query_max_response, 1);
++                                          &mcast_sw_info->query_max_response,
++                                          1);
+ 
+-    if (mcast_info->eth_src) {
+-        sbrec_ip_multicast_set_eth_src(sb, mcast_info->eth_src);
++    if (mcast_sw_info->eth_src) {
++        sbrec_ip_multicast_set_eth_src(sb, mcast_sw_info->eth_src);
+     }
+ 
+-    if (mcast_info->ipv4_src) {
+-        sbrec_ip_multicast_set_ip4_src(sb, mcast_info->ipv4_src);
++    if (mcast_sw_info->ipv4_src) {
++        sbrec_ip_multicast_set_ip4_src(sb, mcast_sw_info->ipv4_src);
+     }
+ }
+ 
+@@ -913,6 +974,7 @@ join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
+                                      NULL, nbr, NULL);
+             ovs_list_push_back(nb_only, &od->list);
+         }
++        init_mcast_info_for_datapath(od);
+         ovs_list_push_back(lr_list, &od->lr_list);
+     }
+ }
+@@ -2006,6 +2068,13 @@ join_logical_ports(struct northd_context *ctx,
+                     break;
+                 }
+             }
++
++            /* If the router is multicast enabled then set relay on the switch
++             * datapath.
++             */
++            if (peer->od && peer->od->mcast_info.rtr.relay) {
++                op->od->mcast_info.sw.flood_relay = true;
++            }
+         } else if (op->nbrp && op->nbrp->peer && !op->derived) {
+             struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
+             if (peer) {
+@@ -2861,6 +2930,10 @@ struct multicast_group {
+ static const struct multicast_group mc_flood =
+     { MC_FLOOD, OVN_MCAST_FLOOD_TUNNEL_KEY };
+ 
++#define MC_MROUTER_FLOOD "_MC_mrouter_flood"
++static const struct multicast_group mc_mrouter_flood =
++    { MC_MROUTER_FLOOD, OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY };
++
+ #define MC_UNKNOWN "_MC_unknown"
+ static const struct multicast_group mc_unknown =
+     { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY };
+@@ -2970,7 +3043,8 @@ ovn_multicast_update_sbrec(const struct ovn_multicast *mc,
+  */
+ struct ovn_igmp_group_entry {
+     struct ovs_list list_node; /* Linkage in the list of entries. */
+-    const struct sbrec_igmp_group *sb;
++    size_t n_ports;
++    struct ovn_port **ports;
+ };
+ 
+ /*
+@@ -2979,12 +3053,13 @@ struct ovn_igmp_group_entry {
+  */
+ struct ovn_igmp_group {
+     struct hmap_node hmap_node; /* Index on 'datapath' and 'address'. */
++    struct ovs_list list_node;  /* Linkage in the per-dp igmp group list. */
+ 
+     struct ovn_datapath *datapath;
+     struct in6_addr address; /* Multicast IPv6-mapped-IPv4 or IPv4 address. */
+     struct multicast_group mcgroup;
+ 
+-    struct ovs_list sb_entries; /* List of SB entries for this group. */
++    struct ovs_list entries; /* List of SB entries for this group. */
+ };
+ 
+ static uint32_t
+@@ -3012,77 +3087,120 @@ ovn_igmp_group_find(struct hmap *igmp_groups,
+     return NULL;
+ }
+ 
+-static void
++static struct ovn_igmp_group *
+ ovn_igmp_group_add(struct northd_context *ctx, struct hmap *igmp_groups,
+                    struct ovn_datapath *datapath,
+-                   const struct sbrec_igmp_group *sb_igmp_group)
++                   const struct in6_addr *address,
++                   const char *address_s)
+ {
+-    struct in6_addr group_address;
+-    ovs_be32 ipv4;
+-
+-    if (ip_parse(sb_igmp_group->address, &ipv4)) {
+-        group_address = in6_addr_mapped_ipv4(ipv4);
+-    } else if (!ipv6_parse(sb_igmp_group->address, &group_address)) {
+-        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+-        VLOG_WARN_RL(&rl, "invalid IGMP group address: %s",
+-                     sb_igmp_group->address);
+-        return;
+-    }
+-
+     struct ovn_igmp_group *igmp_group =
+-        ovn_igmp_group_find(igmp_groups, datapath, &group_address);
++        ovn_igmp_group_find(igmp_groups, datapath, address);
+ 
+     if (!igmp_group) {
+         igmp_group = xmalloc(sizeof *igmp_group);
+ 
+         const struct sbrec_multicast_group *mcgroup =
+-            mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp,
+-                               sb_igmp_group->address, datapath->sb);
++            mcast_group_lookup(ctx->sbrec_mcast_group_by_name_dp, address_s,
++                               datapath->sb);
+ 
+         igmp_group->datapath = datapath;
+-        igmp_group->address = group_address;
++        igmp_group->address = *address;
+         if (mcgroup) {
+             igmp_group->mcgroup.key = mcgroup->tunnel_key;
+             add_tnlid(&datapath->mcast_info.group_tnlids, mcgroup->tunnel_key);
+         } else {
+             igmp_group->mcgroup.key = 0;
+         }
+-        igmp_group->mcgroup.name = sb_igmp_group->address;
+-        ovs_list_init(&igmp_group->sb_entries);
++        igmp_group->mcgroup.name = address_s;
++        ovs_list_init(&igmp_group->entries);
+ 
+         hmap_insert(igmp_groups, &igmp_group->hmap_node,
+-                    ovn_igmp_group_hash(datapath, &group_address));
++                    ovn_igmp_group_hash(datapath, address));
++        ovs_list_push_back(&datapath->mcast_info.groups,
++                           &igmp_group->list_node);
+     }
+ 
++    return igmp_group;
++}
++
++static bool
++ovn_igmp_group_get_address(const struct sbrec_igmp_group *sb_igmp_group,
++                           struct in6_addr *address)
++{
++    ovs_be32 ipv4;
++
++    if (ip_parse(sb_igmp_group->address, &ipv4)) {
++        *address = in6_addr_mapped_ipv4(ipv4);
++        return true;
++    }
++    if (!ipv6_parse(sb_igmp_group->address, address)) {
++        return false;
++    }
++    return true;
++}
++
++static struct ovn_port **
++ovn_igmp_group_get_ports(const struct sbrec_igmp_group *sb_igmp_group,
++                         size_t *n_ports, struct hmap *ovn_ports)
++{
++    struct ovn_port **ports = xmalloc(sb_igmp_group->n_ports * sizeof *ports);
++
++     *n_ports = 0;
++     for (size_t i = 0; i < sb_igmp_group->n_ports; i++) {
++        ports[(*n_ports)] =
++            ovn_port_find(ovn_ports, sb_igmp_group->ports[i]->logical_port);
++        if (ports[(*n_ports)]) {
++            (*n_ports)++;
++        }
++    }
++
++    return ports;
++}
++
++static void
++ovn_igmp_group_add_entry(struct ovn_igmp_group *igmp_group,
++                         struct ovn_port **ports, size_t n_ports)
++{
+     struct ovn_igmp_group_entry *entry = xmalloc(sizeof *entry);
+ 
+-    entry->sb = sb_igmp_group;
+-    ovs_list_push_back(&igmp_group->sb_entries , &entry->list_node);
++    entry->ports = ports;
++    entry->n_ports = n_ports;
++    ovs_list_push_back(&igmp_group->entries, &entry->list_node);
++}
++
++static void
++ovn_igmp_group_destroy_entry(struct ovn_igmp_group_entry *entry)
++{
++    free(entry->ports);
++}
++
++static bool
++ovn_igmp_group_allocate_id(struct ovn_igmp_group *igmp_group)
++{
++    if (igmp_group->mcgroup.key == 0) {
++        struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info;
++        igmp_group->mcgroup.key = ovn_mcast_group_allocate_key(mcast_info);
++    }
++
++    if (igmp_group->mcgroup.key == 0) {
++        return false;
++    }
++
++    return true;
+ }
+ 
+ static void
+ ovn_igmp_group_aggregate_ports(struct ovn_igmp_group *igmp_group,
+-                               struct hmap *ovn_ports,
+                                struct hmap *mcast_groups)
+ {
+     struct ovn_igmp_group_entry *entry;
+ 
+-    LIST_FOR_EACH_POP (entry, list_node, &igmp_group->sb_entries) {
+-        size_t n_oports = 0;
+-        struct ovn_port **oports =
+-            xmalloc(entry->sb->n_ports * sizeof *oports);
+-
+-        for (size_t i = 0; i < entry->sb->n_ports; i++) {
+-            oports[n_oports] =
+-                ovn_port_find(ovn_ports, entry->sb->ports[i]->logical_port);
+-            if (oports[n_oports]) {
+-                n_oports++;
+-            }
+-        }
+-
++    LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) {
+         ovn_multicast_add_ports(mcast_groups, igmp_group->datapath,
+-                                &igmp_group->mcgroup, oports, n_oports);
+-        free(oports);
++                                &igmp_group->mcgroup, entry->ports,
++                                entry->n_ports);
++
++        ovn_igmp_group_destroy_entry(entry);
+         free(entry);
+     }
+ }
+@@ -3094,10 +3212,12 @@ ovn_igmp_group_destroy(struct hmap *igmp_groups,
+     if (igmp_group) {
+         struct ovn_igmp_group_entry *entry;
+ 
+-        LIST_FOR_EACH_POP (entry, list_node, &igmp_group->sb_entries) {
++        LIST_FOR_EACH_POP (entry, list_node, &igmp_group->entries) {
++            ovn_igmp_group_destroy_entry(entry);
+             free(entry);
+         }
+         hmap_remove(igmp_groups, &igmp_group->hmap_node);
++        ovs_list_remove(&igmp_group->list_node);
+         free(igmp_group);
+     }
+ }
+@@ -5404,7 +5524,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
+-        if (od->mcast_info.enabled) {
++        struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
++
++        if (mcast_sw_info->enabled) {
+             /* Punt IGMP traffic to controller. */
+             ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
+                           "ip4 && ip.proto == 2", "igmp;");
+@@ -5417,9 +5539,16 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+                           "outport = \""MC_FLOOD"\"; output;");
+ 
+             /* Drop unregistered IP multicast if not allowed. */
+-            if (!od->mcast_info.flood_unregistered) {
+-                ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
+-                              "ip4 && ip4.mcast", "drop;");
++            if (!mcast_sw_info->flood_unregistered) {
++                /* Forward unregistered IP multicast to mrouter (if any). */
++                if (mcast_sw_info->flood_relay) {
++                    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
++                                  "ip4 && ip4.mcast",
++                                  "outport = \""MC_MROUTER_FLOOD"\"; output;");
++                } else {
++                    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
++                                  "ip4 && ip4.mcast", "drop;");
++                }
+             }
+         }
+ 
+@@ -5436,18 +5565,26 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
+-        struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info;
++        struct mcast_switch_info *mcast_sw_info =
++            &igmp_group->datapath->mcast_info.sw;
+ 
+-        if (mcast_info->active_flows >= mcast_info->table_size) {
++        if (mcast_sw_info->active_flows >= mcast_sw_info->table_size) {
+             continue;
+         }
+-        mcast_info->active_flows++;
++        mcast_sw_info->active_flows++;
+ 
+         ds_clear(&match);
+         ds_clear(&actions);
+ 
+         ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ",
+                       igmp_group->mcgroup.name);
++        /* Also flood traffic to all multicast routers with relay enabled. */
++        if (mcast_sw_info->flood_relay) {
++            ds_put_cstr(&actions,
++                        "clone { "
++                            "outport = \""MC_MROUTER_FLOOD "\"; output; "
++                        "};");
++        }
+         ds_put_format(&actions, "outport = \"%s\"; output; ",
+                       igmp_group->mcgroup.name);
+ 
+@@ -6293,7 +6430,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+          * source or destination, and zero network source or destination
+          * (priority 100). */
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 100,
+-                      "ip4.mcast || "
++                      "ip4.src_mcast ||"
+                       "ip4.src == 255.255.255.255 || "
+                       "ip4.src == 127.0.0.0/8 || "
+                       "ip4.dst == 127.0.0.0/8 || "
+@@ -6301,6 +6438,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                       "ip4.dst == 0.0.0.0/8",
+                       "drop;");
+ 
++        /* Allow multicast if relay enabled (priority 95). */
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 95, "ip4.mcast",
++                      od->mcast_info.rtr.relay ? "next;" : "drop;");
++
+         /* ARP reply handling.  Use ARP replies to populate the logical
+          * router's ARP table. */
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2",
+@@ -7608,6 +7749,27 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+     }
+ 
++    /* IP Multicast lookup. Here we set the output port, adjust TTL and
++     * advance to next table (priority 500).
++     */
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++        if (!od->nbr || !od->mcast_info.rtr.relay) {
++            continue;
++        }
++        struct ovn_igmp_group *igmp_group;
++
++        LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) {
++            ds_clear(&match);
++            ds_clear(&actions);
++            ds_put_format(&match, "ip4 && ip4.dst == %s ",
++                          igmp_group->mcgroup.name);
++            ds_put_format(&actions, "outport = \"%s\"; ip.ttl--; next;",
++                          igmp_group->mcgroup.name);
++            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 500,
++                          ds_cstr(&match), ds_cstr(&actions));
++        }
++    }
++
+     /* Logical router ingress table 8: Policy.
+      *
+      * A packet that arrives at this table is an IP packet that should be
+@@ -7638,10 +7800,24 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+     /* Local router ingress table 9: ARP Resolution.
+      *
+-     * Any packet that reaches this table is an IP packet whose next-hop IP
+-     * address is in reg0. (ip4.dst is the final destination.) This table
+-     * resolves the IP address in reg0 into an output port in outport and an
+-     * Ethernet address in eth.dst. */
++     * Multicast packets already have the outport set so just advance to next
++     * table (priority 500). */
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++        if (!od->nbr) {
++            continue;
++        }
++
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_RESOLVE, 500,
++                      "ip4.mcast", "next;");
++    }
++
++    /* Local router ingress table 9: ARP Resolution.
++     *
++     * Any unicast packet that reaches this table is an IP packet whose
++     * next-hop IP address is in reg0. (ip4.dst is the final destination.)
++     * This table resolves the IP address in reg0 into an output port in
++     * outport and an Ethernet address in eth.dst.
++     */
+     HMAP_FOR_EACH (op, key_node, ports) {
+         if (op->nbsp && !lsp_is_enabled(op->nbsp)) {
+             continue;
+@@ -8123,9 +8299,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_ARP_REQUEST, 0, "1", "output;");
+     }
+ 
+-    /* Logical router egress table 1: Delivery (priority 100).
++    /* Logical router egress table 1: Delivery (priority 100-110).
+      *
+-     * Priority 100 rules deliver packets to enabled logical ports. */
++     * Priority 100 rules deliver packets to enabled logical ports.
++     * Priority 110 rules match multicast packets and update the source
++     * mac before delivering to enabled logical ports. IP multicast traffic
++     * bypasses S_ROUTER_IN_IP_ROUTING route lookups.
++     */
+     HMAP_FOR_EACH (op, key_node, ports) {
+         if (!op->nbrp) {
+             continue;
+@@ -8145,6 +8325,20 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
++        /* If multicast relay is enabled then also adjust source mac for IP
++         * multicast traffic.
++         */
++        if (op->od->mcast_info.rtr.relay) {
++            ds_clear(&match);
++            ds_clear(&actions);
++            ds_put_format(&match, "ip4.mcast && outport == %s",
++                          op->json_key);
++            ds_put_format(&actions, "eth.src = %s; output;",
++                          op->lrp_networks.ea_s);
++            ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 110,
++                        ds_cstr(&match), ds_cstr(&actions));
++        }
++
+         ds_clear(&match);
+         ds_put_format(&match, "outport == %s", op->json_key);
+         ovn_lflow_add(lflows, op->od, S_ROUTER_OUT_DELIVERY, 100,
+@@ -8696,7 +8890,7 @@ build_ip_mcast(struct northd_context *ctx, struct hmap *datapaths)
+         if (!ip_mcast) {
+             ip_mcast = sbrec_ip_multicast_insert(ctx->ovnsb_txn);
+         }
+-        store_mcast_info_for_datapath(ip_mcast, od);
++        store_mcast_info_for_switch_datapath(ip_mcast, od);
+     }
+ 
+     /* Delete southbound records without northbound matches. */
+@@ -8728,6 +8922,14 @@ build_mcast_groups(struct northd_context *ctx,
+ 
+         if (lsp_is_enabled(op->nbsp)) {
+             ovn_multicast_add(mcast_groups, &mc_flood, op);
++
++            /* If this port is connected to a multicast router then add it
++             * to the MC_MROUTER_FLOOD group.
++             */
++            if (op->od->mcast_info.sw.flood_relay && op->peer &&
++                    op->peer->od && op->peer->od->mcast_info.rtr.relay) {
++                ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op);
++            }
+         }
+     }
+ 
+@@ -8750,10 +8952,61 @@ build_mcast_groups(struct northd_context *ctx,
+             continue;
+         }
+ 
++        struct in6_addr group_address;
++        if (!ovn_igmp_group_get_address(sb_igmp, &group_address)) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
++            VLOG_WARN_RL(&rl, "invalid IGMP group address: %s",
++                         sb_igmp->address);
++            continue;
++        }
++
+         /* Add the IGMP group entry. Will also try to allocate an ID for it
+          * if the multicast group already exists.
+          */
+-        ovn_igmp_group_add(ctx, igmp_groups, od, sb_igmp);
++        struct ovn_igmp_group *igmp_group =
++            ovn_igmp_group_add(ctx, igmp_groups, od, &group_address,
++                               sb_igmp->address);
++
++        /* Extract the IGMP group ports from the SB entry and store them
++         * in the IGMP group.
++         */
++        size_t n_igmp_ports;
++        struct ovn_port **igmp_ports =
++            ovn_igmp_group_get_ports(sb_igmp, &n_igmp_ports, ports);
++        ovn_igmp_group_add_entry(igmp_group, igmp_ports, n_igmp_ports);
++    }
++
++    /* Build IGMP groups for multicast routers with relay enabled. The router
++     * IGMP groups are based on the groups learnt by their multicast enabled
++     * peers.
++     */
++    struct ovn_datapath *od;
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++
++        if (ovs_list_is_empty(&od->mcast_info.groups)) {
++            continue;
++        }
++
++        for (size_t i = 0; i < od->n_router_ports; i++) {
++            struct ovn_port *router_port = od->router_ports[i]->peer;
++
++            if (!router_port || !router_port->od ||
++                    !router_port->od->mcast_info.rtr.relay) {
++                continue;
++            }
++
++            struct ovn_igmp_group *igmp_group;
++            LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) {
++                struct ovn_igmp_group *igmp_group_rtr =
++                    ovn_igmp_group_add(ctx, igmp_groups, router_port->od,
++                                       &igmp_group->address,
++                                       igmp_group->mcgroup.name);
++                struct ovn_port **router_igmp_ports =
++                    xmalloc(sizeof *router_igmp_ports);
++                router_igmp_ports[0] = router_port;
++                ovn_igmp_group_add_entry(igmp_group_rtr, router_igmp_ports, 1);
++            }
++        }
+     }
+ 
+     /* Walk the aggregated IGMP groups and allocate IDs for new entries.
+@@ -8761,21 +9014,17 @@ build_mcast_groups(struct northd_context *ctx,
+      */
+     struct ovn_igmp_group *igmp_group, *igmp_group_next;
+     HMAP_FOR_EACH_SAFE (igmp_group, igmp_group_next, hmap_node, igmp_groups) {
+-        if (igmp_group->mcgroup.key == 0) {
+-            struct mcast_info *mcast_info = &igmp_group->datapath->mcast_info;
+-            igmp_group->mcgroup.key = ovn_mcast_group_allocate_key(mcast_info);
+-        }
+ 
+-        /* If we ran out of keys just destroy the entry. */
+-        if (igmp_group->mcgroup.key == 0) {
++        if (!ovn_igmp_group_allocate_id(igmp_group)) {
++            /* If we ran out of keys just destroy the entry. */
+             ovn_igmp_group_destroy(igmp_groups, igmp_group);
+             continue;
+         }
+ 
+-        /* Aggregate the ports from all SB entries corresponding to this
++        /* Aggregate the ports from all entries corresponding to this
+          * group.
+          */
+-        ovn_igmp_group_aggregate_ports(igmp_group, ports, mcast_groups);
++        ovn_igmp_group_aggregate_ports(igmp_group, mcast_groups);
+     }
+ }
+ 
+diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
+index b457b6efc..c2472a04a 100644
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -1535,6 +1535,12 @@
+           address.
+         </p>
+       </column>
++      <column name="options" key="mcast_relay" type='{"type": "boolean"}'>
++        <p>
++          Enables/disables IP multicast relay between logical switches
++          connected to the logical router. Default: False.
++        </p>
++      </column>
+     </group>
+ 
+     <group title="Common Columns">
+diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
+index 71bd08665..477e7bc7a 100644
+--- a/ovn/ovn-sb.xml
++++ b/ovn/ovn-sb.xml
+@@ -1017,6 +1017,8 @@
+         <li><code>eth.mcast</code> expands to <code>eth.dst[40]</code></li>
+         <li><code>vlan.present</code> expands to <code>vlan.tci[12]</code></li>
+         <li><code>ip4</code> expands to <code>eth.type == 0x800</code></li>
++        <li><code>ip4.src_mcast</code> expands to
++        <code>ip4.src[28..31] == 0xe</code></li>
+         <li><code>ip4.mcast</code> expands to <code>ip4.dst[28..31] == 0xe</code></li>
+         <li><code>ip6</code> expands to <code>eth.type == 0x86dd</code></li>
+         <li><code>ip</code> expands to <code>ip4 || ip6</code></li>
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 3f7e06cf5..df41a7549 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -15900,12 +15900,12 @@ AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+ 
+-AT_SETUP([ovn -- IGMP snoop/querier])
++AT_SETUP([ovn -- IGMP snoop/querier/relay])
+ AT_SKIP_IF([test $HAVE_PYTHON = no])
+ ovn_start
+ 
+ # Logical network:
+-# Two independent logical switches (sw1 and sw2).
++# Three logical switches (sw1-sw3) connected to a logical router (rtr).
+ # sw1:
+ #   - subnet 10.0.0.0/8
+ #   - 2 ports bound on hv1 (sw1-p11, sw1-p12)
+@@ -15915,6 +15915,10 @@ ovn_start
+ #   - 1 port bound on hv1 (sw2-p1)
+ #   - 1 port bound on hv2 (sw2-p2)
+ #   - IGMP Querier from 20.0.0.254
++# sw3:
++#   - subnet 30.0.0.0/8
++#   - 1 port bound on hv1 (sw3-p1)
++#   - 1 port bound on hv2 (sw3-p2)
+ 
+ reset_pcap_file() {
+     local iface=$1
+@@ -15991,29 +15995,47 @@ store_igmp_v3_query() {
+ }
+ 
+ #
+-# send_ip_multicast_pkt INPORT HV ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN
+-#    IP_PROTO DATA OUTFILE
++# send_ip_multicast_pkt INPORT HV ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN TTL
++#    IP_CHKSUM IP_PROTO DATA
+ #
+ # This shell function causes an IP multicast packet to be received on INPORT
+ # of HV.
+ # The hexdump of the packet is stored in OUTFILE.
+ #
+ send_ip_multicast_pkt() {
+-    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4 ip_src=$5 ip_dst=$6
+-    local ip_len=$7 ip_chksum=$8 proto=$9 data=${10} outfile=${11}
+-
+-    local ip_ttl=20
++    local inport=$1 hv=$2 eth_src=$3 eth_dst=$4
++    local ip_src=$5 ip_dst=$6 ip_len=$7 ip_ttl=$8 ip_chksum=$9 proto=${10}
++    local data=${11}
+ 
+     local eth=${eth_dst}${eth_src}0800
+     local ip=450000${ip_len}95f14000${ip_ttl}${proto}${ip_chksum}${ip_src}${ip_dst}
+     local packet=${eth}${ip}${data}
+ 
+     as $hv ovs-appctl netdev-dummy/receive ${inport} ${packet}
++}
++
++#
++# store_ip_multicast_pkt ETH_SRC ETH_DST IP_SRC IP_DST IP_LEN TTL
++#    IP_CHKSUM IP_PROTO DATA OUTFILE
++#
++# This shell function builds an IP multicast packet and stores the hexdump of
++# the packet in OUTFILE.
++#
++store_ip_multicast_pkt() {
++    local eth_src=$1 eth_dst=$2
++    local ip_src=$3 ip_dst=$4 ip_len=$5 ip_ttl=$6 ip_chksum=$7 proto=$8
++    local data=$9 outfile=${10}
++
++    local eth=${eth_dst}${eth_src}0800
++    local ip=450000${ip_len}95f14000${ip_ttl}${proto}${ip_chksum}${ip_src}${ip_dst}
++    local packet=${eth}${ip}${data}
++
+     echo ${packet} >> ${outfile}
+ }
+ 
+ ovn-nbctl ls-add sw1
+ ovn-nbctl ls-add sw2
++ovn-nbctl ls-add sw3
+ 
+ ovn-nbctl lsp-add sw1 sw1-p11
+ ovn-nbctl lsp-add sw1 sw1-p12
+@@ -16021,6 +16043,26 @@ ovn-nbctl lsp-add sw1 sw1-p21
+ ovn-nbctl lsp-add sw1 sw1-p22
+ ovn-nbctl lsp-add sw2 sw2-p1
+ ovn-nbctl lsp-add sw2 sw2-p2
++ovn-nbctl lsp-add sw3 sw3-p1
++ovn-nbctl lsp-add sw3 sw3-p2
++
++ovn-nbctl lr-add rtr
++ovn-nbctl lrp-add rtr rtr-sw1 00:00:00:00:01:00 10.0.0.254/24
++ovn-nbctl lrp-add rtr rtr-sw2 00:00:00:00:02:00 20.0.0.254/24
++ovn-nbctl lrp-add rtr rtr-sw3 00:00:00:00:03:00 30.0.0.254/24
++
++ovn-nbctl lsp-add sw1 sw1-rtr                      \
++    -- lsp-set-type sw1-rtr router                 \
++    -- lsp-set-addresses sw1-rtr 00:00:00:00:01:00 \
++    -- lsp-set-options sw1-rtr router-port=rtr-sw1
++ovn-nbctl lsp-add sw2 sw2-rtr                      \
++    -- lsp-set-type sw2-rtr router                 \
++    -- lsp-set-addresses sw2-rtr 00:00:00:00:02:00 \
++    -- lsp-set-options sw2-rtr router-port=rtr-sw2
++ovn-nbctl lsp-add sw3 sw3-rtr                      \
++    -- lsp-set-type sw3-rtr router                 \
++    -- lsp-set-addresses sw3-rtr 00:00:00:00:03:00 \
++    -- lsp-set-options sw3-rtr router-port=rtr-sw3
+ 
+ net_add n1
+ sim_add hv1
+@@ -16042,6 +16084,11 @@ ovs-vsctl -- add-port br-int hv1-vif3 -- \
+     options:tx_pcap=hv1/vif3-tx.pcap \
+     options:rxq_pcap=hv1/vif3-rx.pcap \
+     ofport-request=1
++ovs-vsctl -- add-port br-int hv1-vif4 -- \
++    set interface hv1-vif4 external-ids:iface-id=sw3-p1 \
++    options:tx_pcap=hv1/vif4-tx.pcap \
++    options:rxq_pcap=hv1/vif4-rx.pcap \
++    ofport-request=1
+ 
+ sim_add hv2
+ as hv2
+@@ -16062,12 +16109,18 @@ ovs-vsctl -- add-port br-int hv2-vif3 -- \
+     options:tx_pcap=hv2/vif3-tx.pcap \
+     options:rxq_pcap=hv2/vif3-rx.pcap \
+     ofport-request=1
++ovs-vsctl -- add-port br-int hv2-vif4 -- \
++    set interface hv2-vif4 external-ids:iface-id=sw3-p2 \
++    options:tx_pcap=hv2/vif4-tx.pcap \
++    options:rxq_pcap=hv2/vif4-rx.pcap \
++    ofport-request=1
+ 
+ OVN_POPULATE_ARP
+ 
+ # Enable IGMP snooping on sw1.
+-ovn-nbctl set Logical_Switch sw1 other_config:mcast_querier="false"
+-ovn-nbctl set Logical_Switch sw1 other_config:mcast_snoop="true"
++ovn-nbctl set Logical_Switch sw1       \
++    other_config:mcast_querier="false" \
++    other_config:mcast_snoop="true"
+ 
+ # No IGMP query should be generated by sw1 (mcast_querier="false").
+ truncate -s 0 expected
+@@ -16100,9 +16153,12 @@ truncate -s 0 expected
+ truncate -s 0 expected_empty
+ send_ip_multicast_pkt hv1-vif2 hv1 \
+     000000000001 01005e000144 \
+-    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \
+-    e518e518000a3b3a0000 \
+-    expected
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000
++store_ip_multicast_pkt \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000 expected
+ 
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
+ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+@@ -16123,17 +16179,19 @@ OVS_WAIT_UNTIL([
+     test "${total_entries}" = "1"
+ ])
+ 
+-# Send traffic traffic and make sure it gets forwarded only on the port that
+-# joined.
++# Send traffic and make sure it gets forwarded only on the port that joined.
+ as hv1 reset_pcap_file hv1-vif1 hv1/vif1
+ as hv2 reset_pcap_file hv2-vif1 hv2/vif1
+ truncate -s 0 expected
+ truncate -s 0 expected_empty
+ send_ip_multicast_pkt hv1-vif2 hv1 \
+     000000000001 01005e000144 \
+-    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e ca70 11 \
+-    e518e518000a3b3a0000 \
+-    expected
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000
++store_ip_multicast_pkt \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000 expected
+ 
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+@@ -16167,5 +16225,110 @@ sleep 1
+ OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected])
+ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected])
+ 
++# Dissable IGMP querier on sw2.
++ovn-nbctl set Logical_Switch sw2 \
++    other_config:mcast_querier="false"
++
++# Enable IGMP snooping on sw3.
++ovn-nbctl set Logical_Switch sw3       \
++    other_config:mcast_querier="false" \
++    other_config:mcast_snoop="true"
++
++# Send traffic from sw3 and make sure rtr doesn't relay it.
++truncate -s 0 expected_empty
++
++as hv1 reset_pcap_file hv1-vif1 hv1/vif1
++as hv1 reset_pcap_file hv1-vif2 hv1/vif2
++as hv1 reset_pcap_file hv1-vif3 hv1/vif3
++as hv1 reset_pcap_file hv1-vif4 hv1/vif4
++as hv2 reset_pcap_file hv2-vif1 hv2/vif1
++as hv2 reset_pcap_file hv2-vif2 hv2/vif2
++as hv2 reset_pcap_file hv2-vif3 hv2/vif3
++as hv2 reset_pcap_file hv2-vif4 hv2/vif4
++
++send_ip_multicast_pkt hv2-vif4 hv2 \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000
++
++# Sleep a bit to make sure no traffic is received and then check.
++sleep 1
++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
++
++# Enable IGMP relay on rtr
++ovn-nbctl set logical_router rtr \
++    options:mcast_relay="true"
++
++# Inject IGMP Join for 239.0.1.68 on sw1-p11.
++send_igmp_v3_report hv1-vif1 hv1 \
++    000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
++    $(ip_to_hex 239 0 1 68) 04 e9b9 \
++    /dev/null
++# Inject IGMP Join for 239.0.1.68 on sw2-p2.
++send_igmp_v3_report hv2-vif3 hv2 \
++    000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
++    $(ip_to_hex 239 0 1 68) 04 e9b9 \
++    /dev/null
++# Inject IGMP Join for 239.0.1.68 on sw3-p1.
++send_igmp_v3_report hv1-vif4 hv1 \
++    000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
++    $(ip_to_hex 239 0 1 68) 04 e9b9 \
++    /dev/null
++
++# Check that the IGMP Group is learned by all switches.
++OVS_WAIT_UNTIL([
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    test "${total_entries}" = "3"
++])
++
++# Send traffic from sw3 and make sure it is relayed by rtr.
++# and ports that joined.
++truncate -s 0 expected_routed_sw1
++truncate -s 0 expected_routed_sw2
++truncate -s 0 expected_switched
++truncate -s 0 expected_empty
++
++as hv1 reset_pcap_file hv1-vif1 hv1/vif1
++as hv1 reset_pcap_file hv1-vif2 hv1/vif2
++as hv1 reset_pcap_file hv1-vif3 hv1/vif3
++as hv1 reset_pcap_file hv1-vif4 hv1/vif4
++as hv2 reset_pcap_file hv2-vif1 hv2/vif1
++as hv2 reset_pcap_file hv2-vif2 hv2/vif2
++as hv2 reset_pcap_file hv2-vif3 hv2/vif3
++as hv2 reset_pcap_file hv2-vif4 hv2/vif4
++
++send_ip_multicast_pkt hv2-vif4 hv2 \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000
++store_ip_multicast_pkt \
++    000000000100 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed_sw1
++store_ip_multicast_pkt \
++    000000000200 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed_sw2
++store_ip_multicast_pkt \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000 expected_switched
++
++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_routed_sw1])
++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_routed_sw2])
++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_switched])
++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
++
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+-- 
+2.21.0
+
diff --git a/SOURCES/0002-ovn-northd-Consider-load-balancer-active-backends-in.patch b/SOURCES/0002-ovn-northd-Consider-load-balancer-active-backends-in.patch
new file mode 100644
index 0000000..42baae3
--- /dev/null
+++ b/SOURCES/0002-ovn-northd-Consider-load-balancer-active-backends-in.patch
@@ -0,0 +1,586 @@
+From 14de43e68f38fab76e50c21d3773f44c6e119d0e Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Mon, 27 Jan 2020 18:36:24 +0530
+Subject: [PATCH 2/2] ovn-northd: Consider load balancer active backends in
+ router pipeline
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The commit [1] which added lood balancer health check support
+missed out on updating the logical flows to consider only
+active backends in the logical router pipeline if a load balancer
+is associated. This patch fixes it. It also refactors the code
+a bit.
+
+Without this, an offline backend may be chosen for load balancing,
+resulting in the packet loss.
+
+This patch also adds logical flows in ingress ACL and egress ACL logical
+switch datapath pipeline to skip the ACL policies for service monitor health
+check traffic.
+
+[1] - ba0d6eae960d("ovn-northd: Add support for Load Balancer health check")
+
+Reported-by: Maciej Józefczyk <mjozefcz@redhat.com>
+Fixes - ba0d6eae960d("ovn-northd: Add support for Load Balancer health check")
+Signed-off-by: Numan Siddique <numans@ovn.org>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+
+(cherry-picked from upstream commit bb9f2b9ce56c9319066524c0ad89034adae6b168)
+Conflicts:
+	tests/ovn.at
+
+Change-Id: Ibacbaace0d710aa4d3e0b1d077e032443dd2a9e6
+---
+ ovn/northd/ovn-northd.8.xml |  22 +++
+ ovn/northd/ovn-northd.c     | 268 +++++++++++++++++-------------------
+ tests/ovn.at                |  27 ++++
+ 3 files changed, 172 insertions(+), 145 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 11ef65964..50f7d941b 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -410,6 +410,17 @@
+         in the request direction are skipped here to let a newly created
+         ACL re-allow this connection.
+       </li>
++
++      <li>
++        A priority 34000 logical flow is added for each logical switch datapath
++        with the match <code>eth.dst = <var>E</var></code> to allow the service
++        monitor reply packet destined to <code>ovn-controller</code>
++        with the action <code>next</code>, where <var>E</var> is the
++        service monitor mac defined in the
++        <ref column="options:svc_monitor_mac" table="NB_Global"
++        db="OVN_Northbound"/> colum of <ref table="NB_Global"
++        db="OVN_Northbound"/> table.
++      </li>
+     </ul>
+ 
+     <h3>Ingress Table 7: <code>from-lport</code> QoS Marking</h3>
+@@ -1220,6 +1231,17 @@ output;
+         to allow the DNS reply packet from the
+         <code>Ingress Table 15:DNS responses</code>.
+       </li>
++
++      <li>
++        A priority 34000 logical flow is added for each logical switch datapath
++        with the match <code>eth.src = <var>E</var></code> to allow the service
++        monitor request packet generated by <code>ovn-controller</code>
++        with the action <code>next</code>, where <var>E</var> is the
++        service monitor mac defined in the
++        <ref column="options:svc_monitor_mac" table="NB_Global"
++        db="OVN_Northbound"/> colum of <ref table="NB_Global"
++        db="OVN_Northbound"/> table.
++      </li>
+     </ul>
+ 
+     <h3>Egress Table 5: <code>to-lport</code> QoS Marking</h3>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index b71ae7a94..7139143b3 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -3008,6 +3008,7 @@ struct lb_vip {
+ struct lb_vip_backend {
+     char *ip;
+     uint16_t port;
++    int addr_family;
+ 
+     struct ovn_port *op; /* Logical port to which the ip belong to. */
+     bool health_check;
+@@ -3163,6 +3164,7 @@ ovn_lb_create(struct northd_context *ctx, struct hmap *lbs,
+ 
+             lb->vips[n_vips].backends[i].ip = backend_ip;
+             lb->vips[n_vips].backends[i].port = backend_port;
++            lb->vips[n_vips].backends[i].addr_family = addr_family;
+             lb->vips[n_vips].backends[i].op = op;
+             lb->vips[n_vips].backends[i].svc_mon_src_ip = svc_mon_src_ip;
+ 
+@@ -3226,6 +3228,38 @@ ovn_lb_destroy(struct ovn_lb *lb)
+     free(lb->vips);
+ }
+ 
++static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip,
++                                       struct ds *action)
++{
++    if (lb_vip->health_check) {
++        ds_put_cstr(action, "ct_lb(");
++
++        size_t n_active_backends = 0;
++        for (size_t k = 0; k < lb_vip->n_backends; k++) {
++            struct lb_vip_backend *backend = &lb_vip->backends[k];
++            if (backend->health_check && backend->sbrec_monitor &&
++                backend->sbrec_monitor->status &&
++                strcmp(backend->sbrec_monitor->status, "online")) {
++                continue;
++            }
++
++            n_active_backends++;
++            ds_put_format(action, "%s:%"PRIu16",",
++            backend->ip, backend->port);
++        }
++
++        if (!n_active_backends) {
++            ds_clear(action);
++            ds_put_cstr(action, "drop;");
++        } else {
++            ds_chomp(action, ',');
++            ds_put_cstr(action, ");");
++        }
++    } else {
++        ds_put_format(action, "ct_lb(%s);", lb_vip->backend_ips);
++    }
++}
++
+ static void
+ build_ovn_lbs(struct northd_context *ctx, struct hmap *ports,
+               struct hmap *lbs)
+@@ -4579,11 +4613,11 @@ ls_has_dns_records(const struct nbrec_logical_switch *nbs)
+ 
+ static void
+ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
+-                          struct smap_node *node, char *ip_address,
+-                          struct nbrec_load_balancer *lb, uint16_t port,
+-                          int addr_family, int pl, struct shash *meter_groups)
++                          struct lb_vip *lb_vip,
++                          struct nbrec_load_balancer *lb,
++                          int pl, struct shash *meter_groups)
+ {
+-    if (!controller_event_en || node->value[0]) {
++    if (!controller_event_en || lb_vip->n_backends) {
+         return;
+     }
+ 
+@@ -4594,32 +4628,35 @@ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
+         meter = "event-elb";
+     }
+ 
+-    if (addr_family == AF_INET) {
+-        ds_put_format(&match, "ip4.dst == %s && %s",
+-                      ip_address, lb->protocol);
+-    } else {
+-        ds_put_format(&match, "ip6.dst == %s && %s",
+-                      ip_address, lb->protocol);
+-    }
+-    if (port) {
++    ds_put_format(&match, "ip%s.dst == %s && %s",
++                  lb_vip->addr_family == AF_INET ? "4": "6",
++                  lb_vip->vip, lb->protocol);
++
++    char *vip = lb_vip->vip;
++    if (lb_vip->vip_port) {
+         ds_put_format(&match, " && %s.dst == %u", lb->protocol,
+-                      port);
++                      lb_vip->vip_port);
++        vip = xasprintf("%s:%u", lb_vip->vip, lb_vip->vip_port);
+     }
++
+     action = xasprintf("trigger_event(event = \"%s\", "
+                        "meter = \"%s\", vip = \"%s\", "
+                        "protocol = \"%s\", "
+                        "load_balancer = \"" UUID_FMT "\");",
+                        event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
+-                       meter, node->key, lb->protocol,
++                       meter, vip, lb->protocol,
+                        UUID_ARGS(&lb->header_.uuid));
+     ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
+     ds_destroy(&match);
++    if (lb_vip->vip_port) {
++        free(vip);
++    }
+     free(action);
+ }
+ 
+ static void
+ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+-             struct shash *meter_groups)
++             struct shash *meter_groups, struct hmap *lbs)
+ {
+     /* Do not send ND packets to conntrack */
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
+@@ -4643,40 +4680,29 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+     struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
+     bool vip_configured = false;
+     for (int i = 0; i < od->nbs->n_load_balancer; i++) {
+-        struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
+-        struct smap *vips = &lb->vips;
+-        struct smap_node *node;
+-        int addr_family = AF_INET;
+-
+-        SMAP_FOR_EACH (node, vips) {
+-            vip_configured = true;
+-
+-            /* node->key contains IP:port or just IP. */
+-            char *ip_address = NULL;
+-            uint16_t port;
+-            ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+-                                            &addr_family);
+-            if (!ip_address) {
+-                continue;
+-            }
++        struct nbrec_load_balancer *nb_lb = od->nbs->load_balancer[i];
++        struct ovn_lb *lb =
++            ovn_lb_find(lbs, &nb_lb->header_.uuid);
++        ovs_assert(lb);
+ 
+-            if (addr_family == AF_INET) {
+-                sset_add(&all_ips_v4, ip_address);
++        for (size_t j = 0; j < lb->n_vips; j++) {
++            struct lb_vip *lb_vip = &lb->vips[j];
++            if (lb_vip->addr_family == AF_INET) {
++                sset_add(&all_ips_v4, lb_vip->vip);
+             } else {
+-                sset_add(&all_ips_v6, ip_address);
++                sset_add(&all_ips_v6, lb_vip->vip);
+             }
+ 
+-            build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
+-                                      port, addr_family, S_SWITCH_IN_PRE_LB,
+-                                      meter_groups);
+-
+-            free(ip_address);
++            build_empty_lb_event_flow(od, lflows, lb_vip, nb_lb,
++                                      S_SWITCH_IN_PRE_LB, meter_groups);
+ 
+             /* Ignore L4 port information in the key because fragmented packets
+              * may not have L4 information.  The pre-stateful table will send
+              * the packet through ct() action to de-fragment. In stateful
+              * table, we will eventually look at L4 information. */
+         }
++
++        vip_configured = !!lb->n_vips;
+     }
+ 
+     /* 'REGBIT_CONNTRACK_DEFRAG' is set to let the pre-stateful table send
+@@ -5224,6 +5250,20 @@ build_acls(struct ovn_datapath *od, struct hmap *lflows,
+             lflows, od, S_SWITCH_OUT_ACL, 34000, "udp.src == 53",
+             actions);
+     }
++
++    /* Add a 34000 priority flow to advance the service monitor reply
++     * packets to skip applying ingress ACLs. */
++    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
++    ovn_lflow_add(lflows, od, S_SWITCH_IN_ACL, 34000, svc_check_match,
++                  "next;");
++    free(svc_check_match);
++
++    /* Add a 34000 priority flow to advance the service monitor packets
++     * generated by ovn-controller to skip applying egress ACLs. */
++    svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
++    ovn_lflow_add(lflows, od, S_SWITCH_OUT_ACL, 34000, svc_check_match,
++                  "next;");
++    free(svc_check_match);
+ }
+ 
+ static void
+@@ -5349,36 +5389,7 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
+             struct lb_vip *lb_vip = &lb->vips[j];
+             /* New connections in Ingress table. */
+             struct ds action = DS_EMPTY_INITIALIZER;
+-            if (lb_vip->health_check) {
+-                ds_put_cstr(&action, "ct_lb(");
+-
+-                size_t n_active_backends = 0;
+-                for (size_t k = 0; k < lb_vip->n_backends; k++) {
+-                    struct lb_vip_backend *backend = &lb_vip->backends[k];
+-                    bool is_up = true;
+-                    if (backend->health_check && backend->sbrec_monitor &&
+-                        backend->sbrec_monitor->status &&
+-                        strcmp(backend->sbrec_monitor->status, "online")) {
+-                        is_up = false;
+-                    }
+-
+-                    if (is_up) {
+-                        n_active_backends++;
+-                        ds_put_format(&action, "%s:%"PRIu16",",
+-                        backend->ip, backend->port);
+-                    }
+-                }
+-
+-                if (!n_active_backends) {
+-                    ds_clear(&action);
+-                    ds_put_cstr(&action, "drop;");
+-                } else {
+-                    ds_chomp(&action, ',');
+-                    ds_put_cstr(&action, ");");
+-                }
+-            } else {
+-                ds_put_format(&action, "ct_lb(%s);", lb_vip->backend_ips);
+-            }
++            build_lb_vip_ct_lb_actions(lb_vip, &action);
+ 
+             struct ds match = DS_EMPTY_INITIALIZER;
+             if (lb_vip->addr_family == AF_INET) {
+@@ -5699,7 +5710,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+ 
+         build_pre_acls(od, lflows);
+-        build_pre_lb(od, lflows, meter_groups);
++        build_pre_lb(od, lflows, meter_groups, lbs);
+         build_pre_stateful(od, lflows);
+         build_acls(od, lflows, port_groups);
+         build_qos(od, lflows);
+@@ -6963,15 +6974,11 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type,
+ static void
+ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
+                    struct ds *match, struct ds *actions, int priority,
+-                   const char *lb_force_snat_ip, struct smap_node *lb_info,
+-                   bool is_udp, int addr_family, char *ip_addr,
+-                   uint16_t l4_port, struct nbrec_load_balancer *lb,
++                   const char *lb_force_snat_ip, struct lb_vip *lb_vip,
++                   bool is_udp, struct nbrec_load_balancer *lb,
+                    struct shash *meter_groups)
+ {
+-    char *backend_ips = lb_info->value;
+-
+-    build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb,
+-                              l4_port, addr_family, S_ROUTER_IN_DNAT,
++    build_empty_lb_event_flow(od, lflows, lb_vip, lb, S_ROUTER_IN_DNAT,
+                               meter_groups);
+ 
+     /* A match and actions for new connections. */
+@@ -7000,7 +7007,7 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
+     free(new_match);
+     free(est_match);
+ 
+-    if (!od->l3dgw_port || !od->l3redirect_port || !backend_ips) {
++    if (!od->l3dgw_port || !od->l3redirect_port || !lb_vip->n_backends) {
+         return;
+     }
+ 
+@@ -7009,46 +7016,28 @@ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
+      * router has a gateway router port associated.
+      */
+     struct ds undnat_match = DS_EMPTY_INITIALIZER;
+-    if (addr_family == AF_INET) {
++    if (lb_vip->addr_family == AF_INET) {
+         ds_put_cstr(&undnat_match, "ip4 && (");
+     } else {
+         ds_put_cstr(&undnat_match, "ip6 && (");
+     }
+-    char *start, *next, *ip_str;
+-    start = next = xstrdup(backend_ips);
+-    ip_str = strsep(&next, ",");
+-    bool backend_ips_found = false;
+-    while (ip_str && ip_str[0]) {
+-        char *ip_address = NULL;
+-        uint16_t port = 0;
+-        int addr_family_;
+-        ip_address_and_port_from_lb_key(ip_str, &ip_address, &port,
+-                                        &addr_family_);
+-        if (!ip_address) {
+-            break;
+-        }
+ 
+-        if (addr_family_ == AF_INET) {
+-            ds_put_format(&undnat_match, "(ip4.src == %s", ip_address);
++    for (size_t i = 0; i < lb_vip->n_backends; i++) {
++        struct lb_vip_backend *backend = &lb_vip->backends[i];
++        if (backend->addr_family == AF_INET) {
++            ds_put_format(&undnat_match, "(ip4.src == %s", backend->ip);
+         } else {
+-            ds_put_format(&undnat_match, "(ip6.src == %s", ip_address);
++            ds_put_format(&undnat_match, "(ip6.src == %s", backend->ip);
+         }
+-        free(ip_address);
+-        if (port) {
++
++        if (backend->port) {
+             ds_put_format(&undnat_match, " && %s.src == %d) || ",
+-                          is_udp ? "udp" : "tcp", port);
++                          is_udp ? "udp" : "tcp", backend->port);
+         } else {
+             ds_put_cstr(&undnat_match, ") || ");
+         }
+-        ip_str = strsep(&next, ",");
+-        backend_ips_found = true;
+     }
+ 
+-    free(start);
+-    if (!backend_ips_found) {
+-        ds_destroy(&undnat_match);
+-        return;
+-    }
+     ds_chomp(&undnat_match, ' ');
+     ds_chomp(&undnat_match, '|');
+     ds_chomp(&undnat_match, '|');
+@@ -7166,7 +7155,8 @@ lrouter_nat_is_stateless(const struct nbrec_nat *nat)
+ 
+ static void
+ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+-                    struct hmap *lflows, struct shash *meter_groups)
++                    struct hmap *lflows, struct shash *meter_groups,
++                    struct hmap *lbs)
+ {
+     /* This flow table structure is documented in ovn-northd(8), so please
+      * update ovn-northd.8.xml if you change anything. */
+@@ -8546,24 +8536,18 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         struct sset all_ips = SSET_INITIALIZER(&all_ips);
+ 
+         for (int i = 0; i < od->nbr->n_load_balancer; i++) {
+-            struct nbrec_load_balancer *lb = od->nbr->load_balancer[i];
+-            struct smap *vips = &lb->vips;
+-            struct smap_node *node;
+-
+-            SMAP_FOR_EACH (node, vips) {
+-                uint16_t port = 0;
+-                int addr_family;
+-
+-                /* node->key contains IP:port or just IP. */
+-                char *ip_address = NULL;
+-                ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+-                        &addr_family);
+-                if (!ip_address) {
+-                    continue;
+-                }
++            struct nbrec_load_balancer *nb_lb = od->nbr->load_balancer[i];
++            struct ovn_lb *lb =
++                ovn_lb_find(lbs, &nb_lb->header_.uuid);
++            ovs_assert(lb);
++
++            for (size_t j = 0; j < lb->n_vips; j++) {
++                struct lb_vip *lb_vip = &lb->vips[j];
++                ds_clear(&actions);
++                build_lb_vip_ct_lb_actions(lb_vip, &actions);
+ 
+-                if (!sset_contains(&all_ips, ip_address)) {
+-                    sset_add(&all_ips, ip_address);
++                if (!sset_contains(&all_ips, lb_vip->vip)) {
++                    sset_add(&all_ips, lb_vip->vip);
+                     /* If there are any load balancing rules, we should send
+                      * the packet to conntrack for defragmentation and
+                      * tracking.  This helps with two things.
+@@ -8573,12 +8557,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                      * 2. If there are L4 ports in load balancing rules, we
+                      *    need the defragmentation to match on L4 ports. */
+                     ds_clear(&match);
+-                    if (addr_family == AF_INET) {
++                    if (lb_vip->addr_family == AF_INET) {
+                         ds_put_format(&match, "ip && ip4.dst == %s",
+-                                      ip_address);
+-                    } else if (addr_family == AF_INET6) {
++                                      lb_vip->vip);
++                    } else if (lb_vip->addr_family == AF_INET6) {
+                         ds_put_format(&match, "ip && ip6.dst == %s",
+-                                      ip_address);
++                                      lb_vip->vip);
+                     }
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_DEFRAG,
+                                   100, ds_cstr(&match), "ct_next;");
+@@ -8589,28 +8573,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                  * via add_router_lb_flow().  One flow is for specific matching
+                  * on ct.new with an action of "ct_lb($targets);".  The other
+                  * flow is for ct.est with an action of "ct_dnat;". */
+-                ds_clear(&actions);
+-                ds_put_format(&actions, "ct_lb(%s);", node->value);
+-
+                 ds_clear(&match);
+-                if (addr_family == AF_INET) {
++                if (lb_vip->addr_family == AF_INET) {
+                     ds_put_format(&match, "ip && ip4.dst == %s",
+-                                ip_address);
+-                } else if (addr_family == AF_INET6) {
++                                  lb_vip->vip);
++                } else if (lb_vip->addr_family == AF_INET6) {
+                     ds_put_format(&match, "ip && ip6.dst == %s",
+-                                ip_address);
++                                  lb_vip->vip);
+                 }
+ 
+                 int prio = 110;
+-                bool is_udp = lb->protocol && !strcmp(lb->protocol, "udp") ?
+-                    true : false;
+-                if (port) {
++                bool is_udp = nullable_string_is_equal(nb_lb->protocol, "udp");
++
++                if (lb_vip->vip_port) {
+                     if (is_udp) {
+                         ds_put_format(&match, " && udp && udp.dst == %d",
+-                                      port);
++                                      lb_vip->vip_port);
+                     } else {
+                         ds_put_format(&match, " && tcp && tcp.dst == %d",
+-                                      port);
++                                      lb_vip->vip_port);
+                     }
+                     prio = 120;
+                 }
+@@ -8620,11 +8601,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                                   od->l3redirect_port->json_key);
+                 }
+                 add_router_lb_flow(lflows, od, &match, &actions, prio,
+-                                   lb_force_snat_ip, node, is_udp,
+-                                   addr_family, ip_address, port, lb,
+-                                   meter_groups);
+-
+-                free(ip_address);
++                                   lb_force_snat_ip, lb_vip, is_udp,
++                                   nb_lb, meter_groups);
+             }
+         }
+         sset_destroy(&all_ips);
+@@ -9431,7 +9409,7 @@ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
+ 
+     build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups,
+                         igmp_groups, meter_groups, lbs);
+-    build_lrouter_flows(datapaths, ports, &lflows, meter_groups);
++    build_lrouter_flows(datapaths, ports, &lflows, meter_groups, lbs);
+ 
+     /* Push changes to the Logical_Flow table to database. */
+     const struct sbrec_logical_flow *sbflow, *next_sbflow;
+diff --git a/tests/ovn.at b/tests/ovn.at
+index e2565f274..3a5ecf211 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -17007,6 +17007,22 @@ ovn-nbctl --wait=sb ls-lb-add sw0 lb1
+ ovn-nbctl --wait=sb ls-lb-add sw1 lb1
+ ovn-nbctl --wait=sb lr-lb-add lr0 lb1
+ 
++ovn-nbctl ls-add public
++ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
++ovn-nbctl lsp-add public public-lr0
++ovn-nbctl lsp-set-type public-lr0 router
++ovn-nbctl lsp-set-addresses public-lr0 router
++ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
++
++# localnet port
++ovn-nbctl lsp-add public ln-public
++ovn-nbctl lsp-set-type ln-public localnet
++ovn-nbctl lsp-set-addresses ln-public unknown
++ovn-nbctl lsp-set-options ln-public network_name=public
++
++# schedule the gw router port to a chassis. Change the name of the chassis
++ovn-nbctl --wait=hv lrp-set-gateway-chassis lr0-public hv1 20
++
+ OVN_POPULATE_ARP
+ ovn-nbctl --wait=hv sync
+ 
+@@ -17018,6 +17034,11 @@ AT_CHECK([cat lflows.txt], [0], [dnl
+   table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
+ ])
+ 
++ovn-sbctl dump-flows lr0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++])
++
+ # get the svc monitor mac.
+ svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \
+ sed s/":"//g | sed s/\"//g`
+@@ -17051,5 +17072,11 @@ AT_CHECK([cat lflows.txt], [0], [dnl
+   table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;)
+ ])
+ 
++ovn-sbctl dump-flows lr0 | grep lr_in_dnat | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=6 (lr_in_dnat         ), priority=120  , match=(ct.est && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_dnat;)
++  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(drop;)
++])
++
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+-- 
+2.24.1
+
diff --git a/SOURCES/0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch b/SOURCES/0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch
new file mode 100644
index 0000000..cedb1c2
--- /dev/null
+++ b/SOURCES/0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch
@@ -0,0 +1,739 @@
+From fb226499b9222e0fe9b9ac3b6be9aaa4023b8abe Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Wed, 13 Nov 2019 10:39:58 -0800
+Subject: [PATCH ovn 2/3] ovn-northd: Limit ARP/ND broadcast domain whenever
+ possible.
+
+ARP request and ND NS packets for router owned IPs were being
+flooded in the complete L2 domain (using the MC_FLOOD multicast group).
+However this creates a scaling issue in scenarios where aggregation
+logical switches are connected to more logical routers (~350). The
+logical pipelines of all routers would have to be executed before the
+packet is finally replied to by a single router, the owner of the IP
+address.
+
+This commit limits the broadcast domain by bypassing the L2 Lookup stage
+for ARP requests that will be replied by a single router. The packets
+are forwarded only to the router port that owns the target IP address.
+
+IPs that are owned by the routers and for which this fix applies are:
+- IP addresses configured on the router ports.
+- VIPs.
+- NAT IPs.
+
+Reported-at: https://bugzilla.redhat.com/1756945
+Reported-by: Anil Venkata <vkommadi@redhat.com>
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry-picked from upstream commit 32f5ebb06226e3433e53e05bdc75d16752859a0e)
+
+Change-Id: I3b63e970593a26cbbfc6f4a495db0487c4002419
+---
+ ovn/northd/ovn-northd.8.xml |  14 ++
+ ovn/northd/ovn-northd.c     | 230 +++++++++++++++++++++++++++++----
+ ovn/ovn-architecture.7.xml  |  19 +++
+ tests/ovn.at                | 307 ++++++++++++++++++++++++++++++++++++++++++--
+ 4 files changed, 530 insertions(+), 40 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 8f8fd5c..dc5fdf4 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1005,6 +1005,20 @@ output;
+       </li>
+ 
+       <li>
++        Priority-80 flows for each port connected to a logical router
++        matching self originated GARP/ARP request/ND packets. These packets
++        are flooded to the <code>MC_FLOOD</code> which contains all logical
++        ports.
++      </li>
++
++      <li>
++        Priority-75 flows for each IP address/VIP/NAT address owned by a
++        router port connected to the switch. These flows match ARP requests
++        and ND packets for the specific IP addresses.  Matched packets are
++        forwarded only to the router that owns the IP address.
++      </li>
++
++      <li>
+         A priority-70 flow that outputs all packets with an Ethernet broadcast
+         or multicast <code>eth.dst</code> to the <code>MC_FLOOD</code>
+         multicast group.
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 81c313f..03eb7a4 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -210,6 +210,8 @@ enum ovn_stage {
+ #define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]"
+ #define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]"
+ 
++#define FLAGBIT_NOT_VXLAN "flags[1] == 0"
++
+ /* Returns an "enum ovn_stage" built from the arguments. */
+ static enum ovn_stage
+ ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline pipeline,
+@@ -1202,6 +1204,34 @@ ovn_port_allocate_key(struct ovn_datapath *od)
+                           1, (1u << 15) - 1, &od->port_key_hint);
+ }
+ 
++/* Returns true if the logical switch port 'enabled' column is empty or
++ * set to true.  Otherwise, returns false. */
++static bool
++lsp_is_enabled(const struct nbrec_logical_switch_port *lsp)
++{
++    return !lsp->n_enabled || *lsp->enabled;
++}
++
++/* Returns true only if the logical switch port 'up' column is set to true.
++ * Otherwise, if the column is not set or set to false, returns false. */
++static bool
++lsp_is_up(const struct nbrec_logical_switch_port *lsp)
++{
++    return lsp->n_up && *lsp->up;
++}
++
++static bool
++lsp_is_external(const struct nbrec_logical_switch_port *nbsp)
++{
++    return !strcmp(nbsp->type, "external");
++}
++
++static bool
++lrport_is_enabled(const struct nbrec_logical_router_port *lrport)
++{
++    return !lrport->enabled || *lrport->enabled;
++}
++
+ static char *
+ chassis_redirect_name(const char *port_name)
+ {
+@@ -3744,28 +3774,6 @@ build_port_security_ip(enum ovn_pipeline pipeline, struct ovn_port *op,
+ 
+ }
+ 
+-/* Returns true if the logical switch port 'enabled' column is empty or
+- * set to true.  Otherwise, returns false. */
+-static bool
+-lsp_is_enabled(const struct nbrec_logical_switch_port *lsp)
+-{
+-    return !lsp->n_enabled || *lsp->enabled;
+-}
+-
+-/* Returns true only if the logical switch port 'up' column is set to true.
+- * Otherwise, if the column is not set or set to false, returns false. */
+-static bool
+-lsp_is_up(const struct nbrec_logical_switch_port *lsp)
+-{
+-    return lsp->n_up && *lsp->up;
+-}
+-
+-static bool
+-lsp_is_external(const struct nbrec_logical_switch_port *nbsp)
+-{
+-    return !strcmp(nbsp->type, "external");
+-}
+-
+ static bool
+ build_dhcpv4_action(struct ovn_port *op, ovs_be32 offer_ip,
+                     struct ds *options_action, struct ds *response_action,
+@@ -5168,6 +5176,170 @@ build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list)
+     }
+ }
+ 
++/*
++ * Ingress table 17: Flows that flood self originated ARP/ND packets in the
++ * switching domain.
++ */
++static void
++build_lswitch_rport_arp_req_self_orig_flow(struct ovn_port *op,
++                                           uint32_t priority,
++                                           struct ovn_datapath *od,
++                                           struct hmap *lflows)
++{
++    struct ds match = DS_EMPTY_INITIALIZER;
++    struct ds eth_src = DS_EMPTY_INITIALIZER;
++
++    /* Self originated (G)ARP requests/ND need to be flooded as usual.
++     * Determine that packets are self originated by also matching on
++     * source MAC. Matching on ingress port is not reliable in case this
++     * is a VLAN-backed network.
++     * Priority: 80.
++     */
++    ds_put_format(&eth_src, "{ %s, ", op->lrp_networks.ea_s);
++    for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
++        const struct nbrec_nat *nat = op->od->nbr->nat[i];
++
++        if (!nat->external_mac) {
++            continue;
++        }
++
++        ds_put_format(&eth_src, "%s, ", nat->external_mac);
++    }
++    ds_chomp(&eth_src, ' ');
++    ds_chomp(&eth_src, ',');
++    ds_put_cstr(&eth_src, "}");
++
++    ds_put_format(&match, "eth.src == %s && (arp.op == 1 || nd_ns)",
++                  ds_cstr(&eth_src));
++    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
++                  ds_cstr(&match),
++                  "outport = \""MC_FLOOD"\"; output;");
++
++    ds_destroy(&match);
++    ds_destroy(&eth_src);
++}
++
++/*
++ * Ingress table 17: Flows that forward ARP/ND requests only to the routers
++ * that own the addresses. Other ARP/ND packets are still flooded in the
++ * switching domain as regular broadcast.
++ */
++static void
++build_lswitch_rport_arp_req_flow_for_ip(struct sset *ips,
++                                        int addr_family,
++                                        struct ovn_port *patch_op,
++                                        struct ovn_datapath *od,
++                                        uint32_t priority,
++                                        struct hmap *lflows)
++{
++    struct ds match   = DS_EMPTY_INITIALIZER;
++    struct ds actions = DS_EMPTY_INITIALIZER;
++
++    /* Packets received from VXLAN tunnels have already been through the
++     * router pipeline so we should skip them. Normally this is done by the
++     * multicast_group implementation (VXLAN packets skip table 32 which
++     * delivers to patch ports) but we're bypassing multicast_groups.
++     */
++    ds_put_cstr(&match, FLAGBIT_NOT_VXLAN " && ");
++
++    if (addr_family == AF_INET) {
++        ds_put_cstr(&match, "arp.op == 1 && arp.tpa == { ");
++    } else {
++        ds_put_cstr(&match, "nd_ns && nd.target == { ");
++    }
++
++    const char *ip_address;
++    SSET_FOR_EACH (ip_address, ips) {
++        ds_put_format(&match, "%s, ", ip_address);
++    }
++
++    ds_chomp(&match, ' ');
++    ds_chomp(&match, ',');
++    ds_put_cstr(&match, "}");
++
++    /* Send a the packet only to the router pipeline and skip flooding it
++     * in the broadcast domain.
++     */
++    ds_put_format(&actions, "outport = %s; output;", patch_op->json_key);
++    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, priority,
++                  ds_cstr(&match), ds_cstr(&actions));
++
++    ds_destroy(&match);
++    ds_destroy(&actions);
++}
++
++/*
++ * Ingress table 17: Flows that forward ARP/ND requests only to the routers
++ * that own the addresses.
++ * Priorities:
++ * - 80: self originated GARPs that need to follow regular processing.
++ * - 75: ARP requests to router owned IPs (interface IP/LB/NAT).
++ */
++static void
++build_lswitch_rport_arp_req_flows(struct ovn_port *op,
++                                  struct ovn_datapath *sw_od,
++                                  struct ovn_port *sw_op,
++                                  struct hmap *lflows)
++{
++    if (!op || !op->nbrp) {
++        return;
++    }
++
++    if (!lrport_is_enabled(op->nbrp)) {
++        return;
++    }
++
++    /* Self originated (G)ARP requests/ND need to be flooded as usual.
++     * Priority: 80.
++     */
++    build_lswitch_rport_arp_req_self_orig_flow(op, 80, sw_od, lflows);
++
++    /* Forward ARP requests for owned IP addresses (L3, VIP, NAT) only to this
++     * router port.
++     * Priority: 75.
++     */
++    struct sset all_ips_v4 = SSET_INITIALIZER(&all_ips_v4);
++    struct sset all_ips_v6 = SSET_INITIALIZER(&all_ips_v6);
++
++    for (size_t i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
++        sset_add(&all_ips_v4, op->lrp_networks.ipv4_addrs[i].addr_s);
++    }
++    for (size_t i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
++        sset_add(&all_ips_v6, op->lrp_networks.ipv6_addrs[i].addr_s);
++    }
++
++    get_router_load_balancer_ips(op->od, &all_ips_v4, &all_ips_v6);
++
++    for (size_t i = 0; i < op->od->nbr->n_nat; i++) {
++        const struct nbrec_nat *nat = op->od->nbr->nat[i];
++
++        if (!strcmp(nat->type, "snat")) {
++            continue;
++        }
++
++        ovs_be32 ip;
++        ovs_be32 mask;
++        struct in6_addr ipv6;
++        struct in6_addr mask_v6;
++
++        if (ip_parse_masked(nat->external_ip, &ip, &mask)) {
++            if (!ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6)) {
++                sset_add(&all_ips_v6, nat->external_ip);
++            }
++        } else {
++            sset_add(&all_ips_v4, nat->external_ip);
++        }
++    }
++
++    build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
++                                            sw_od, 75, lflows);
++    build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
++                                            sw_od, 75, lflows);
++
++    sset_destroy(&all_ips_v4);
++    sset_destroy(&all_ips_v6);
++}
++
+ static void
+ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+                     struct hmap *port_groups, struct hmap *lflows,
+@@ -5755,6 +5927,14 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
++        /* For ports connected to logical routers add flows to bypass the
++         * broadcast flooding of ARP/ND requests in table 17. We direct the
++         * requests only to the router port that owns the IP address.
++         */
++        if (!strcmp(op->nbsp->type, "router")) {
++            build_lswitch_rport_arp_req_flows(op->peer, op->od, op, lflows);
++        }
++
+         for (size_t i = 0; i < op->nbsp->n_addresses; i++) {
+             /* Addresses are owned by the logical port.
+              * Ethernet address followed by zero or more IPv4
+@@ -5886,12 +6066,6 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+     ds_destroy(&actions);
+ }
+ 
+-static bool
+-lrport_is_enabled(const struct nbrec_logical_router_port *lrport)
+-{
+-    return !lrport->enabled || *lrport->enabled;
+-}
+-
+ /* Returns a string of the IP address of the router port 'op' that
+  * overlaps with 'ip_s".  If one is not found, returns NULL.
+  *
+diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
+index 2a375c8..771edb8 100644
+--- a/ovn/ovn-architecture.7.xml
++++ b/ovn/ovn-architecture.7.xml
+@@ -1390,6 +1390,25 @@
+     http://docs.openvswitch.org/en/latest/topics/high-availability.
+   </p>
+ 
++  <h3>ARP request and ND NS packet processing</h3>
++
++  <p>
++    Due to the fact that ARP requests and ND NA packets are usually broadcast
++    packets, for performance reasons, OVN deals with requests that target OVN
++    owned IP addresses (i.e., IP addresses configured on the router ports,
++    VIPs, NAT IPs) in a specific way and only forwards them to the logical
++    router that owns the target IP address. This behavior is different than
++    that of traditional swithces and implies that other routers/hosts
++    connected to the logical switch will not learn the MAC/IP binding from
++    the request packet.
++  </p>
++
++  <p>
++    All other ARP and ND packets are flooded in the L2 broadcast domain and
++    to all attached logical patch ports.
++  </p>
++
++
+   <h2>Multiple localnet logical switches connected to a Logical Router</h2>
+ 
+   <p>
+diff --git a/tests/ovn.at b/tests/ovn.at
+index a95a057..33989e6 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -2861,7 +2861,7 @@ test_ip() {
+     done
+ }
+ 
+-# test_arp INPORT SHA SPA TPA [REPLY_HA]
++# test_arp INPORT SHA SPA TPA FLOOD [REPLY_HA]
+ #
+ # Causes a packet to be received on INPORT.  The packet is an ARP
+ # request with SHA, SPA, and TPA as specified.  If REPLY_HA is provided, then
+@@ -2872,21 +2872,25 @@ test_ip() {
+ # SHA and REPLY_HA are each 12 hex digits.
+ # SPA and TPA are each 8 hex digits.
+ test_arp() {
+-    local inport=$1 sha=$2 spa=$3 tpa=$4 reply_ha=$5
++    local inport=$1 sha=$2 spa=$3 tpa=$4 flood=$5 reply_ha=$6
+     local request=ffffffffffff${sha}08060001080006040001${sha}${spa}ffffffffffff${tpa}
+     hv=hv`vif_to_hv $inport`
+     as $hv ovs-appctl netdev-dummy/receive vif$inport $request
+     as $hv ovs-appctl ofproto/trace br-int in_port=$inport $request
+ 
+     # Expect to receive the broadcast ARP on the other logical switch ports if
+-    # IP address is not configured to the switch patch port.
++    # IP address is not configured on the switch patch port or on the router
++    # port (i.e, $flood == 1).
+     local i=`vif_to_ls $inport`
+     local j k
+     for j in 1 2 3; do
+         for k in 1 2 3; do
+-            # 192.168.33.254 is configured to the switch patch port for lrp33,
+-            # so no ARP flooding expected for it.
+-            if test $i$j$k != $inport && test $tpa != `ip_to_hex 192 168 33 254`; then
++            # Skip ingress port.
++            if test $i$j$k == $inport; then
++                continue
++            fi
++
++            if test X$flood == X1; then
+                 echo $request >> $i$j$k.expected
+             fi
+         done
+@@ -3023,9 +3027,9 @@ for i in 1 2 3; do
+       otherip=`ip_to_hex 192 168 $i$j 55` # Some other IP in subnet
+       externalip=`ip_to_hex 1 2 3 4`      # Some other IP not in subnet
+ 
+-      test_arp $i$j$k $smac $sip        $rip        $rmac      #4
+-      test_arp $i$j$k $smac $otherip    $rip        $rmac      #5
+-      test_arp $i$j$k $smac $sip        $otherip               #6
++      test_arp $i$j$k $smac $sip        $rip       0     $rmac       #4
++      test_arp $i$j$k $smac $otherip    $rip       0     $rmac       #5
++      test_arp $i$j$k $smac $sip        $otherip   1                 #6
+ 
+       # When rip is 192.168.33.254, ARP request from externalip won't be
+       # filtered, because 192.168.33.254 is configured to switch peer port
+@@ -3034,7 +3038,7 @@ for i in 1 2 3; do
+       if test $i = 3 && test $j = 3; then
+         lrp33_rsp=$rmac
+       fi
+-      test_arp $i$j$k $smac $externalip $rip        $lrp33_rsp #7
++      test_arp $i$j$k $smac $externalip $rip       0      $lrp33_rsp #7
+ 
+       # MAC binding should be learned from ARP request.
+       host_mac_pretty=f0:00:00:00:0$i:$j$k
+@@ -9579,7 +9583,7 @@ ovn-nbctl --wait=hv --timeout=3 sync
+ # Check that there is a logical flow in logical switch foo's pipeline
+ # to set the outport to rp-foo (which is expected).
+ OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
+-grep rp-foo | grep -v is_chassis_resident | wc -l`])
++grep rp-foo | grep -v is_chassis_resident | grep priority=50 -c`])
+ 
+ # Set the option 'reside-on-redirect-chassis' for foo
+ ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true
+@@ -9587,7 +9591,7 @@ ovn-nbctl set logical_router_port foo options:reside-on-redirect-chassis=true
+ # to set the outport to rp-foo with the condition is_chassis_redirect.
+ ovn-sbctl dump-flows foo
+ OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
+-grep rp-foo | grep is_chassis_resident | wc -l`])
++grep rp-foo | grep is_chassis_resident | grep priority=50 -c`])
+ 
+ echo "---------NB dump-----"
+ ovn-nbctl show
+@@ -15678,3 +15682,282 @@ AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
+ 
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
++
++AT_SETUP([ovn -- ARP/ND request broadcast limiting])
++AT_SKIP_IF([test $HAVE_PYTHON = no])
++ovn_start
++
++ip_to_hex() {
++    printf "%02x%02x%02x%02x" "$@"
++}
++
++send_arp_request() {
++    local hv=$1 inport=$2 eth_src=$3 spa=$4 tpa=$5
++    local eth_dst=ffffffffffff
++    local eth_type=0806
++    local eth=${eth_dst}${eth_src}${eth_type}
++
++    local arp=0001080006040001${eth_src}${spa}${eth_dst}${tpa}
++
++    local request=${eth}${arp}
++    as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
++}
++
++send_nd_ns() {
++    local hv=$1 inport=$2 eth_src=$3 spa=$4 tpa=$5 cksum=$6
++
++    local eth_dst=ffffffffffff
++    local eth_type=86dd
++    local eth=${eth_dst}${eth_src}${eth_type}
++
++    local ip_vhlen=60000000
++    local ip_plen=0020
++    local ip_next=3a
++    local ip_ttl=ff
++    local ip=${ip_vhlen}${ip_plen}${ip_next}${ip_ttl}${spa}${tpa}
++
++    # Neighbor Solicitation
++    local icmp6_type=87
++    local icmp6_code=00
++    local icmp6_rsvd=00000000
++    # ICMPv6 source lla option
++    local icmp6_opt=01
++    local icmp6_optlen=01
++    local icmp6=${icmp6_type}${icmp6_code}${cksum}${icmp6_rsvd}${tpa}${icmp6_opt}${icmp6_optlen}${eth_src}
++
++    local request=${eth}${ip}${icmp6}
++
++    as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
++}
++
++src_mac=000000000001
++
++net_add n1
++sim_add hv1
++as hv1
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.1
++
++ovs-vsctl -- add-port br-int hv1-vif1 -- \
++    set interface hv1-vif1 external-ids:iface-id=sw-agg-ext \
++    options:tx_pcap=hv1/vif1-tx.pcap \
++    options:rxq_pcap=hv1/vif1-rx.pcap \
++    ofport-request=1
++
++# One Aggregation Switch connected to two Logical networks (routers).
++ovn-nbctl ls-add sw-agg
++ovn-nbctl lsp-add sw-agg sw-agg-ext \
++    -- lsp-set-addresses sw-agg-ext 00:00:00:00:00:01
++
++ovn-nbctl lsp-add sw-agg sw-rtr1                   \
++    -- lsp-set-type sw-rtr1 router                 \
++    -- lsp-set-addresses sw-rtr1 00:00:00:00:01:00 \
++    -- lsp-set-options sw-rtr1 router-port=rtr1-sw
++ovn-nbctl lsp-add sw-agg sw-rtr2                   \
++    -- lsp-set-type sw-rtr2 router                 \
++    -- lsp-set-addresses sw-rtr2 00:00:00:00:02:00 \
++    -- lsp-set-options sw-rtr2 router-port=rtr2-sw
++
++# Configure L3 interface IPv4 & IPv6 on both routers
++ovn-nbctl lr-add rtr1
++ovn-nbctl lrp-add rtr1 rtr1-sw 00:00:00:00:01:00 10.0.0.1/24 10::1/64
++
++ovn-nbctl lr-add rtr2
++ovn-nbctl lrp-add rtr2 rtr2-sw 00:00:00:00:02:00 10.0.0.2/24 10::2/64
++
++OVN_POPULATE_ARP
++ovn-nbctl --wait=hv sync
++
++sw_dp_uuid=$(ovn-sbctl --bare --columns _uuid list datapath_binding sw-agg)
++sw_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding sw-agg)
++
++r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1)
++r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2)
++
++mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${sw_dp_uuid} name="_MC_flood")
++mc_key=$(printf "%04x" $mc_key)
++
++match_sw_metadata="metadata=0x${sw_dp_key}"
++
++# Inject ARP request for first router owned IP address.
++send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 1)
++
++# Verify that the ARP request is sent only to rtr1.
++match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.1,arp_op=1"
++match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15"
++match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \
++    grep n_packets=1 -c)
++    test "1" = "${pkts_to_rtr1}"
++])
++OVS_WAIT_UNTIL([
++    pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \
++    grep n_packets=1 -c)
++    test "0" = "${pkts_to_rtr2}"
++])
++OVS_WAIT_UNTIL([
++    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
++    test "0" = "${pkts_flooded}"
++])
++
++# Inject ND_NS for ofirst router owned IP address.
++src_ipv6=00100000000000000000000000000254
++dst_ipv6=00100000000000000000000000000001
++send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
++
++# Verify that the ND_NS is sent only to rtr1.
++match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::1"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \
++    grep n_packets=1 -c)
++    test "1" = "${pkts_to_rtr1}"
++])
++OVS_WAIT_UNTIL([
++    pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \
++    grep n_packets=1 -c)
++    test "0" = "${pkts_to_rtr2}"
++])
++OVS_WAIT_UNTIL([
++    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
++    test "0" = "${pkts_flooded}"
++])
++
++# Configure load balancing on both routers.
++ovn-nbctl lb-add lb1-v4 10.0.0.11 42.42.42.1
++ovn-nbctl lb-add lb1-v6 10::11 42::1
++ovn-nbctl lr-lb-add rtr1 lb1-v4
++ovn-nbctl lr-lb-add rtr1 lb1-v6
++
++ovn-nbctl lb-add lb2-v4 10.0.0.22 42.42.42.2
++ovn-nbctl lb-add lb2-v6 10::22 42::2
++ovn-nbctl lr-lb-add rtr2 lb2-v4
++ovn-nbctl lr-lb-add rtr2 lb2-v6
++ovn-nbctl --wait=hv sync
++
++# Inject ARP request for first router owned VIP address.
++send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 11)
++
++# Verify that the ARP request is sent only to rtr1.
++match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.11,arp_op=1"
++match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15"
++match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \
++    grep n_packets=1 -c)
++    test "1" = "${pkts_to_rtr1}"
++])
++OVS_WAIT_UNTIL([
++    pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \
++    grep n_packets=1 -c)
++    test "0" = "${pkts_to_rtr2}"
++])
++OVS_WAIT_UNTIL([
++    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
++    test "0" = "${pkts_flooded}"
++])
++
++# Inject ND_NS for first router owned VIP address.
++src_ipv6=00100000000000000000000000000254
++dst_ipv6=00100000000000000000000000000011
++send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
++
++# Verify that the ND_NS is sent only to rtr1.
++match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::11"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \
++    grep n_packets=1 -c)
++    test "1" = "${pkts_to_rtr1}"
++])
++OVS_WAIT_UNTIL([
++    pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \
++    grep n_packets=1 -c)
++    test "0" = "${pkts_to_rtr2}"
++])
++OVS_WAIT_UNTIL([
++    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
++    test "0" = "${pkts_flooded}"
++])
++
++# Configure NAT on both routers
++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1
++ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10::111 42::1
++ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10.0.0.222 42.42.42.2
++ovn-nbctl lr-nat-add rtr2 dnat_and_snat 10::222 42::2
++
++# Inject ARP request for first router owned NAT address.
++send_arp_request 1 1 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 111)
++
++# Verify that the ARP request is sent only to rtr1.
++match_arp_req="priority=75.*${match_sw_metadata}.*arp_tpa=10.0.0.111,arp_op=1"
++match_send_rtr1="load:0x${r1_tnl_key}->NXM_NX_REG15"
++match_send_rtr2="load:0x${r2_tnl_key}->NXM_NX_REG15"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep "${match_send_rtr1}" | \
++    grep n_packets=1 -c)
++    test "1" = "${pkts_to_rtr1}"
++])
++OVS_WAIT_UNTIL([
++    pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_arp_req}" | grep "${match_send_rtr2}" | \
++    grep n_packets=1 -c)
++    test "0" = "${pkts_to_rtr2}"
++])
++OVS_WAIT_UNTIL([
++    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
++    test "0" = "${pkts_flooded}"
++])
++
++# Inject ND_NS for first router owned IP address.
++src_ipv6=00100000000000000000000000000254
++dst_ipv6=00100000000000000000000000000111
++send_nd_ns 1 1 ${src_mac} ${src_ipv6} ${dst_ipv6} 751d
++
++# Verify that the ND_NS is sent only to rtr1.
++match_nd_ns="priority=75.*${match_sw_metadata}.*icmp_type=135.*nd_target=10::111"
++
++as hv1
++OVS_WAIT_UNTIL([
++    pkts_to_rtr1=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep "${match_send_rtr1}" | \
++    grep n_packets=1 -c)
++    test "1" = "${pkts_to_rtr1}"
++])
++OVS_WAIT_UNTIL([
++    pkts_to_rtr2=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_nd_ns}" | grep "${match_send_rtr2}" | \
++    grep n_packets=1 -c)
++    test "0" = "${pkts_to_rtr2}"
++])
++OVS_WAIT_UNTIL([
++    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
++    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
++    test "0" = "${pkts_flooded}"
++])
++
++OVN_CLEANUP([hv1])
++AT_CLEANUP
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch b/SOURCES/0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch
new file mode 100644
index 0000000..68b190c
--- /dev/null
+++ b/SOURCES/0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch
@@ -0,0 +1,113 @@
+From 96f740501f7eb026f9fbbf7de921c28dca2c9693 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Tue, 24 Mar 2020 20:37:17 +0100
+Subject: [PATCH 2/2] ovn.at: Fix ARP test that fails due to timing.
+
+The test for "ARP/ND request broadcast limiting" checks that injected
+ARP packets are not flooded using the MC_FLOOD multicast group. However,
+this introduces a race condition in the test because GARPs generated by
+OVN would also hit the same openflow rules.
+
+Remove the checks that use the MC_FLOOD group. They are also redundant
+as the rest of the test checks that packets are forwarded according to
+the newly added, higher priority rules.
+
+Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+(cherry picked from upstream commit 598a07cd240d7d01de3d7f04ca7abc58a33977a1)
+
+Change-Id: Ie526595b017bcfcc4744a014f19db351d98ee658
+---
+ tests/ovn.at | 33 ---------------------------------
+ 1 file changed, 33 deletions(-)
+
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 551c399..72f4ebd 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -15943,9 +15943,6 @@ r1_dp_key=$(ovn-sbctl --bare --columns tunnel_key list datapath_binding rtr1)
+ r1_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr1)
+ r2_tnl_key=$(ovn-sbctl --bare --columns tunnel_key list port_binding sw-rtr2)
+ 
+-mc_key=$(ovn-sbctl --bare --columns tunnel_key find multicast_group datapath=${sw_dp_uuid} name="_MC_flood")
+-mc_key=$(printf "%04x" $mc_key)
+-
+ match_sw_metadata="metadata=0x${sw_dp_key}"
+ match_r1_metadata="metadata=0x${r1_dp_key}"
+ 
+@@ -15970,11 +15967,6 @@ OVS_WAIT_UNTIL([
+     grep n_packets=1 -c)
+     test "0" = "${pkts_to_rtr2}"
+ ])
+-OVS_WAIT_UNTIL([
+-    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
+-    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
+-    test "0" = "${pkts_flooded}"
+-])
+ 
+ # Inject ND_NS for ofirst router owned IP address.
+ src_ipv6=00100000000000000000000000000254
+@@ -15997,11 +15989,6 @@ OVS_WAIT_UNTIL([
+     grep n_packets=1 -c)
+     test "0" = "${pkts_to_rtr2}"
+ ])
+-OVS_WAIT_UNTIL([
+-    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
+-    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
+-    test "0" = "${pkts_flooded}"
+-])
+ 
+ # Configure load balancing on both routers.
+ ovn-nbctl lb-add lb1-v4 10.0.0.11 42.42.42.1
+@@ -16036,11 +16023,6 @@ OVS_WAIT_UNTIL([
+     grep n_packets=1 -c)
+     test "0" = "${pkts_to_rtr2}"
+ ])
+-OVS_WAIT_UNTIL([
+-    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
+-    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
+-    test "0" = "${pkts_flooded}"
+-])
+ 
+ # Inject ND_NS for first router owned VIP address.
+ src_ipv6=00100000000000000000000000000254
+@@ -16063,11 +16045,6 @@ OVS_WAIT_UNTIL([
+     grep n_packets=1 -c)
+     test "0" = "${pkts_to_rtr2}"
+ ])
+-OVS_WAIT_UNTIL([
+-    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
+-    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
+-    test "0" = "${pkts_flooded}"
+-])
+ 
+ # Configure NAT on both routers.
+ ovn-nbctl lr-nat-add rtr1 dnat_and_snat 10.0.0.111 42.42.42.1
+@@ -16103,11 +16080,6 @@ OVS_WAIT_UNTIL([
+     grep n_packets=1 -c)
+     test "0" = "${pkts_to_rtr2}"
+ ])
+-OVS_WAIT_UNTIL([
+-    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
+-    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
+-    test "0" = "${pkts_flooded}"
+-])
+ 
+ # Inject ARP request for FIP1.
+ send_arp_request 1 0 ${src_mac} $(ip_to_hex 10 0 0 254) $(ip_to_hex 10 0 0 121)
+@@ -16170,11 +16142,6 @@ OVS_WAIT_UNTIL([
+     grep n_packets=1 -c)
+     test "0" = "${pkts_to_rtr2}"
+ ])
+-OVS_WAIT_UNTIL([
+-    pkts_flooded=$(ovs-ofctl dump-flows br-int | \
+-    grep -E "${match_sw_metadata}" | grep ${mc_key} | grep -v n_packets=0 -c)
+-    test "0" = "${pkts_flooded}"
+-])
+ 
+ # Inject ND_NS for FIP1.
+ src_ipv6=00100000000000000000000000000254
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0003-Learn-the-mac-binding-only-if-required.patch b/SOURCES/0003-Learn-the-mac-binding-only-if-required.patch
new file mode 100644
index 0000000..0dc98a0
--- /dev/null
+++ b/SOURCES/0003-Learn-the-mac-binding-only-if-required.patch
@@ -0,0 +1,1548 @@
+From 036599bdfa4c10d69c1bcae407fb75ea841fa834 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <nusiddiq@redhat.com>
+Date: Thu, 5 Sep 2019 00:15:38 +0530
+Subject: [PATCH 3/4] Learn the mac binding only if required
+
+OVN has the actions - put_arp and put_nd to learn the mac bindings from the
+ARP/ND packets. These actions update the Southbound MAC_Binding table.
+These actions translates to controller actions. Whenever pinctrl thread
+receives such packets, it wakes up the main ovn-controller thread.
+If the MAC_Binding table is already upto date, this results
+in unnecessary CPU cyles. There are some security implications as well.
+A rogue VM can flood broadcast ARP request/reply packets and this
+could cause DoS issues. A physical switch may send periodic GARPs
+and these packets hit ovn-controllers.
+
+This patch solves these problems by learning the mac bindings only if
+required. There is no need to apply the put_arp/put_nd action if the
+Southbound MAC_Binding row is upto date.
+
+New actions - lookup_arp and lookup_nd are added which looks up the
+IP, MAC pair in the mac_binding table and stores the result in a
+register. 1 if lookup is successful, 0 otherwise.
+
+ovn-northd adds 2 new stages - LOOKUP_NEIGHBOR and LEARN_NEIGHBOR before
+IP_INPUT in the router ingress pipeline.c. The LOOKUP_NEIGHBOR stage
+adds flows to do the lookup in the mac_binding table and the LEARN_NEIGHBOR
+adds flows to learn the neighbors only if require.
+
+The lflow module of ovn-controller adds OF flows in table 67 (OFTABLE_MAC_LOOKUP)
+for each mac_binding entry with the match reg0 = ip && eth.src = mac with
+the action - load:1->reg10[6]
+
+Eg:
+table=31, priority=100,arp,reg0=0xaca8006f,reg14=0x3,metadata=0x3,dl_src=00:44:00:00:00:04
+          actions=load:1->NXM_NX_REG10[6]
+
+This patch should also address the issue reported in 'Reported-at'
+
+Reported-at: https://bugzilla.redhat.com/1729846
+Reported-by: Haidong Li <haili@redhat.com>
+CC: Han ZHou <hzhou8@ebay.com>
+CC: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Han ZHou <hzhou8@ebay.com>
+Tested-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ include/ovn/actions.h        |  13 ++
+ include/ovn/logical-fields.h |   4 +
+ ovn/controller/lflow.c       |  37 ++++-
+ ovn/controller/lflow.h       |   1 +
+ ovn/lib/actions.c            | 114 ++++++++++++++
+ ovn/northd/ovn-northd.8.xml  | 212 ++++++++++++++++---------
+ ovn/northd/ovn-northd.c      | 205 ++++++++++++++-----------
+ ovn/ovn-architecture.7.xml   |  18 +++
+ ovn/ovn-sb.xml               |  57 +++++++
+ ovn/utilities/ovn-trace.c    |  69 +++++++++
+ tests/ovn.at                 | 290 ++++++++++++++++++++++++++++++++++-
+ tests/test-ovn.c             |   1 +
+ 12 files changed, 844 insertions(+), 177 deletions(-)
+
+diff --git a/include/ovn/actions.h b/include/ovn/actions.h
+index 145f27f25..4e2f4d28d 100644
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -73,8 +73,10 @@ struct ovn_extend_table;
+     OVNACT(ND_NA_ROUTER,      ovnact_nest)            \
+     OVNACT(GET_ARP,           ovnact_get_mac_bind)    \
+     OVNACT(PUT_ARP,           ovnact_put_mac_bind)    \
++    OVNACT(LOOKUP_ARP,        ovnact_lookup_mac_bind) \
+     OVNACT(GET_ND,            ovnact_get_mac_bind)    \
+     OVNACT(PUT_ND,            ovnact_put_mac_bind)    \
++    OVNACT(LOOKUP_ND,         ovnact_lookup_mac_bind) \
+     OVNACT(PUT_DHCPV4_OPTS,   ovnact_put_opts)        \
+     OVNACT(PUT_DHCPV6_OPTS,   ovnact_put_opts)        \
+     OVNACT(SET_QUEUE,         ovnact_set_queue)       \
+@@ -266,6 +268,15 @@ struct ovnact_put_mac_bind {
+     struct expr_field mac;      /* 48-bit Ethernet address. */
+ };
+ 
++/* OVNACT_LOOKUP_ARP, OVNACT_LOOKUP_ND. */
++struct ovnact_lookup_mac_bind {
++    struct ovnact ovnact;
++    struct expr_field dst;      /* 1-bit destination field. */
++    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;
+@@ -628,6 +639,8 @@ struct ovnact_encode_params {
+     uint8_t output_ptable;      /* OpenFlow table for 'output' to resubmit. */
+     uint8_t mac_bind_ptable;    /* OpenFlow table for 'get_arp'/'get_nd' to
+                                    resubmit. */
++    uint8_t mac_lookup_ptable;  /* OpenFlow table for
++                                   'lookup_arp'/'lookup_nd' to resubmit. */
+ };
+ 
+ void ovnacts_encode(const struct ovnact[], size_t ovnacts_len,
+diff --git a/include/ovn/logical-fields.h b/include/ovn/logical-fields.h
+index 9bac8e027..9b7c34fb7 100644
+--- a/include/ovn/logical-fields.h
++++ b/include/ovn/logical-fields.h
+@@ -56,6 +56,7 @@ enum mff_log_flags_bits {
+     MLF_FORCE_SNAT_FOR_LB_BIT = 3,
+     MLF_LOCAL_ONLY_BIT = 4,
+     MLF_NESTED_CONTAINER_BIT = 5,
++    MLF_LOOKUP_MAC_BIT = 6,
+ };
+ 
+ /* MFF_LOG_FLAGS_REG flag assignments */
+@@ -84,6 +85,9 @@ enum mff_log_flags {
+ 
+     /* Indicate that a packet was received from a nested container. */
+     MLF_NESTED_CONTAINER = (1 << MLF_NESTED_CONTAINER_BIT),
++
++    /* Indicate that the lookup in the mac binding table was successful. */
++    MLF_LOOKUP_MAC = (1 << MLF_LOOKUP_MAC_BIT),
+ };
+ 
+ /* OVN logical fields
+diff --git a/ovn/controller/lflow.c b/ovn/controller/lflow.c
+index 1aafafb33..733564fd1 100644
+--- a/ovn/controller/lflow.c
++++ b/ovn/controller/lflow.c
+@@ -687,6 +687,7 @@ consider_logical_flow(
+         .egress_ptable = OFTABLE_LOG_EGRESS_PIPELINE,
+         .output_ptable = output_ptable,
+         .mac_bind_ptable = OFTABLE_MAC_BINDING,
++        .mac_lookup_ptable = OFTABLE_MAC_LOOKUP,
+     };
+     ovnacts_encode(ovnacts.data, ovnacts.size, &ep, &ofpacts);
+     ovnacts_free(ovnacts.data, ovnacts.size);
+@@ -777,7 +778,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+         return;
+     }
+ 
+-    struct match match = MATCH_CATCHALL_INITIALIZER;
++    struct match get_arp_match = MATCH_CATCHALL_INITIALIZER;
++    struct match lookup_arp_match = MATCH_CATCHALL_INITIALIZER;
++
+     if (strchr(b->ip, '.')) {
+         ovs_be32 ip;
+         if (!ip_parse(b->ip, &ip)) {
+@@ -785,7 +788,9 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+             VLOG_WARN_RL(&rl, "bad 'ip' %s", b->ip);
+             return;
+         }
+-        match_set_reg(&match, 0, ntohl(ip));
++        match_set_reg(&get_arp_match, 0, ntohl(ip));
++        match_set_reg(&lookup_arp_match, 0, ntohl(ip));
++        match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_ARP));
+     } else {
+         struct in6_addr ip6;
+         if (!ipv6_parse(b->ip, &ip6)) {
+@@ -795,17 +800,35 @@ consider_neighbor_flow(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+         }
+         ovs_be128 value;
+         memcpy(&value, &ip6, sizeof(value));
+-        match_set_xxreg(&match, 0, ntoh128(value));
++        match_set_xxreg(&get_arp_match, 0, ntoh128(value));
++
++        match_set_xxreg(&lookup_arp_match, 0, ntoh128(value));
++        match_set_dl_type(&lookup_arp_match, htons(ETH_TYPE_IPV6));
++        match_set_nw_proto(&lookup_arp_match, 58);
++        match_set_icmp_code(&lookup_arp_match, 0);
+     }
+ 
+-    match_set_metadata(&match, htonll(pb->datapath->tunnel_key));
+-    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
++    match_set_metadata(&get_arp_match, htonll(pb->datapath->tunnel_key));
++    match_set_reg(&get_arp_match, MFF_LOG_OUTPORT - MFF_REG0, pb->tunnel_key);
++
++    match_set_metadata(&lookup_arp_match, htonll(pb->datapath->tunnel_key));
++    match_set_reg(&lookup_arp_match, MFF_LOG_INPORT - 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);
++    ofctrl_add_flow(flow_table, OFTABLE_MAC_BINDING, 100, 0, &get_arp_match,
++                    &ofpacts, &b->header_.uuid);
++
++    ofpbuf_clear(&ofpacts);
++    uint8_t value = 1;
++    put_load(&value, sizeof value, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1,
++             &ofpacts);
++    match_set_dl_src(&lookup_arp_match, mac);
++    ofctrl_add_flow(flow_table, OFTABLE_MAC_LOOKUP, 100, 0, &lookup_arp_match,
++                    &ofpacts, &b->header_.uuid);
++
+     ofpbuf_uninit(&ofpacts);
+ }
+ 
+diff --git a/ovn/controller/lflow.h b/ovn/controller/lflow.h
+index 4e1086eb6..9cb2afb86 100644
+--- a/ovn/controller/lflow.h
++++ b/ovn/controller/lflow.h
+@@ -65,6 +65,7 @@ struct uuid;
+ #define OFTABLE_SAVE_INPORT          64
+ #define OFTABLE_LOG_TO_PHY           65
+ #define OFTABLE_MAC_BINDING          66
++#define OFTABLE_MAC_LOOKUP           67
+ 
+ /* The number of tables for the ingress and egress pipelines. */
+ #define LOG_PIPELINE_LEN 24
+diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
+index 0bb76cb27..45f8715cf 100644
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -1607,6 +1607,112 @@ ovnact_put_mac_bind_free(struct ovnact_put_mac_bind *put_mac OVS_UNUSED)
+ {
+ }
+ 
++static void format_lookup_mac(const struct ovnact_lookup_mac_bind *lookup_mac,
++                              struct ds *s, const char *name)
++{
++    expr_field_format(&lookup_mac->dst, s);
++    ds_put_format(s, " = %s(", name);
++    expr_field_format(&lookup_mac->port, s);
++    ds_put_cstr(s, ", ");
++    expr_field_format(&lookup_mac->ip, s);
++    ds_put_cstr(s, ", ");
++    expr_field_format(&lookup_mac->mac, s);
++    ds_put_cstr(s, ");");
++}
++
++static void
++format_LOOKUP_ARP(const struct ovnact_lookup_mac_bind *lookup_mac,
++                         struct ds *s)
++{
++    format_lookup_mac(lookup_mac, s, "lookup_arp");
++}
++
++static void
++format_LOOKUP_ND(const struct ovnact_lookup_mac_bind *lookup_mac,
++                        struct ds *s)
++{
++    format_lookup_mac(lookup_mac, s, "lookup_nd");
++}
++
++static void
++encode_lookup_mac(const struct ovnact_lookup_mac_bind *lookup_mac,
++                  enum mf_field_id ip_field,
++                  const struct ovnact_encode_params *ep,
++                  struct ofpbuf *ofpacts)
++{
++    const struct arg args[] = {
++        { expr_resolve_field(&lookup_mac->port), MFF_LOG_INPORT },
++        { expr_resolve_field(&lookup_mac->ip), ip_field },
++        { expr_resolve_field(&lookup_mac->mac),  MFF_ETH_SRC},
++    };
++
++    encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
++
++    struct mf_subfield dst = expr_resolve_field(&lookup_mac->dst);
++    ovs_assert(dst.field);
++
++    put_load(0, MFF_LOG_FLAGS, MLF_LOOKUP_MAC_BIT, 1, ofpacts);
++    emit_resubmit(ofpacts, ep->mac_lookup_ptable);
++
++    struct ofpact_reg_move *orm = ofpact_put_REG_MOVE(ofpacts);
++    orm->dst = dst;
++    orm->src.field = mf_from_id(MFF_LOG_FLAGS);
++    orm->src.ofs = MLF_LOOKUP_MAC_BIT;
++    orm->src.n_bits = 1;
++
++    encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
++}
++
++static void
++encode_LOOKUP_ARP(const struct ovnact_lookup_mac_bind *lookup_mac,
++                  const struct ovnact_encode_params *ep,
++                  struct ofpbuf *ofpacts)
++{
++    encode_lookup_mac(lookup_mac, MFF_REG0, ep, ofpacts);
++}
++
++static void
++encode_LOOKUP_ND(const struct ovnact_lookup_mac_bind *lookup_mac,
++                        const struct ovnact_encode_params *ep,
++                        struct ofpbuf *ofpacts)
++{
++    encode_lookup_mac(lookup_mac, MFF_XXREG0, ep, ofpacts);
++}
++
++static void
++parse_lookup_mac_bind(struct action_context *ctx,
++                      const struct expr_field *dst,
++                      int width,
++                      struct ovnact_lookup_mac_bind *lookup_mac)
++{
++    /* Validate that the destination is a 1-bit, modifiable field. */
++    char *error = expr_type_check(dst, 1, true);
++    if (error) {
++        lexer_error(ctx->lexer, "%s", error);
++        free(error);
++        return;
++    }
++
++    lexer_get(ctx->lexer); /* Skip lookup_arp/lookup_nd. */
++    lexer_get(ctx->lexer); /* Skip '('. * */
++
++    action_parse_field(ctx, 0, false, &lookup_mac->port);
++    lexer_force_match(ctx->lexer, LEX_T_COMMA);
++    action_parse_field(ctx, width, false, &lookup_mac->ip);
++    lexer_force_match(ctx->lexer, LEX_T_COMMA);
++    action_parse_field(ctx, 48, false, &lookup_mac->mac);
++    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
++    lookup_mac->dst = *dst;
++}
++
++static void
++ovnact_lookup_mac_bind_free(
++    struct ovnact_lookup_mac_bind *lookup_mac OVS_UNUSED)
++{
++
++}
++
++
+ static void
+ parse_gen_opt(struct action_context *ctx, struct ovnact_gen_option *o,
+               const struct hmap *gen_opts, const char *opts_type)
+@@ -2722,6 +2828,14 @@ parse_set_action(struct action_context *ctx)
+                 && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
+             parse_check_pkt_larger(ctx, &lhs,
+                                    ovnact_put_CHECK_PKT_LARGER(ctx->ovnacts));
++        } else if (!strcmp(ctx->lexer->token.s, "lookup_arp")
++                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
++            parse_lookup_mac_bind(ctx, &lhs, 32,
++                                  ovnact_put_LOOKUP_ARP(ctx->ovnacts));
++        } else if (!strcmp(ctx->lexer->token.s, "lookup_nd")
++                && lexer_lookahead(ctx->lexer) == LEX_T_LPAREN) {
++            parse_lookup_mac_bind(ctx, &lhs, 128,
++                                  ovnact_put_LOOKUP_ND(ctx->ovnacts));
+         } else {
+             parse_assignment_action(ctx, false, &lhs);
+         }
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index ec2b6454c..ec9020d2a 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1223,7 +1223,126 @@ output;
+       Other packets are implicitly dropped.
+     </p>
+ 
+-    <h3>Ingress Table 1: IP Input</h3>
++    <h3>Ingress Table 1: Neighbor lookup</h3>
++
++    <p>
++      For ARP and IPv6 Neighbor Discovery packets, this table looks into the
++      <ref db="OVN_Southbound" table="MAC_Binding"/> records to determine
++      if OVN needs to learn the mac bindings. Following flows are added:
++    </p>
++
++    <ul>
++      <li>
++        <p>
++          For each router port <var>P</var> that owns IP address <var>A</var>,
++          which belongs to subnet <var>S</var> with prefix length <var>L</var>,
++          a priority-100 flow is added which matches
++          <code>inport == <var>P</var> &amp;&amp;
++          arp.spa == <var>S</var>/<var>L</var> &amp;&amp; arp.op == 1</code>
++          (ARP request) with the
++          following actions:
++        </p>
++
++        <pre>
++reg9[4] = lookup_arp(inport, arp.spa, arp.sha);
++next;
++        </pre>
++
++        <p>
++          If the logical router port <var>P</var> is a distributed gateway
++          router port, additional match
++          <code>is_chassis_resident(cr-<var>P</var>)</code> is added so that
++          the resident gateway chassis handles the neighbor lookup.
++        </p>
++      </li>
++
++      <li>
++        <p>
++          A priority-100 flow which matches on ARP reply packets and applies
++          the actions:
++        </p>
++
++        <pre>
++reg9[4] = lookup_arp(inport, arp.spa, arp.sha);
++next;
++        </pre>
++      </li>
++
++      <li>
++        <p>
++          A priority-100 flow which matches on IPv6 Neighbor Discovery
++          advertisement packet and applies the actions:
++        </p>
++
++        <pre>
++reg9[4] = lookup_nd(inport, nd.target, nd.tll);
++next;
++        </pre>
++      </li>
++
++      <li>
++        <p>
++          A priority-100 flow which matches on IPv6 Neighbor Discovery
++          solicitation packet and applies the actions:
++        </p>
++
++        <pre>
++reg9[4] = lookup_nd(inport, ip6.src, nd.sll);
++next;
++        </pre>
++      </li>
++
++      <li>
++        A priority-0 fallback flow that matches all packets and applies
++        the action <code>reg9[5] = 1; next;</code>
++        advancing the packet to the next table.
++      </li>
++    </ul>
++
++    <h3>Ingress Table 2: Neighbor learning</h3>
++
++    <p>
++      This table adds flows to learn the mac bindings from the ARP and
++      IPv6 Neighbor Solicitation/Advertisement packets if ARP/ND lookup
++      failed in the previous table.
++    </p>
++
++    <p>
++      reg9[4] will be <code>1</code> if the <code>lookup_arp/lookup_nd</code>
++      in the previous table was successful.
++    </p>
++
++    <p>
++      reg9[5] will be <code>1</code> if there was no need to do the lookup.
++    </p>
++
++    <ul>
++      <li>
++        A priority-100 flow with the match
++        <code>reg9[4] == 1 || reg9[5] == 1</code> and advances the packet
++        to the next table as there is no need to learn the neighbor.
++      </li>
++
++      <li>
++        A priority-90 flow with the match <code>arp</code> and
++        applies the action
++        <code>put_arp(inport, arp.spa, arp.sha); next;</code>
++      </li>
++
++      <li>
++        A priority-90 flow with the match <code>nd_na</code> and
++        applies the action
++        <code>put_nd(inport, nd.target, nd.tll); next;</code>
++      </li>
++
++      <li>
++        A priority-90 flow with the match <code>nd_ns</code> and
++        applies the action
++        <code>put_nd(inport, ip6.src, nd.sll); next;</code>
++      </li>
++    </ul>
++
++    <h3>Ingress Table 3: IP Input</h3>
+ 
+     <p>
+       This table is the core of the logical router datapath functionality.  It
+@@ -1320,8 +1439,7 @@ next;
+         </p>
+ 
+         <p>
+-          These flows reply to ARP requests for the router's own IP address
+-          and populates mac binding table of the logical router port.
++          These flows reply to ARP requests for the router's own IP address.
+           The ARP requests are handled only if the requestor's IP belongs
+           to the same subnets of the logical router port.
+           For each router port <var>P</var> that owns IP address <var>A</var>,
+@@ -1334,7 +1452,6 @@ next;
+         </p>
+ 
+         <pre>
+-put_arp(inport, arp.spa, arp.sha);
+ eth.dst = eth.src;
+ eth.src = <var>E</var>;
+ arp.op = 2; /* ARP reply. */
+@@ -1370,17 +1487,6 @@ output;
+         </p>
+       </li>
+ 
+-      <li>
+-        <p>
+-          These flows handles ARP requests not for router's own IP address.
+-          They use the SPA and SHA to populate the logical router port's
+-          mac binding table, with priority 80.  The typical use case of
+-          these flows are GARP requests handling.  For the gateway port
+-          on a distributed logical router, these flows are only programmed
+-          on the gateway port instance on the <code>redirect-chassis</code>.
+-        </p>
+-      </li>
+-
+       <li>
+         <p>
+           These flows reply to ARP requests for the virtual IP addresses
+@@ -1451,36 +1557,6 @@ arp.sha = <var>external_mac</var>;
+         </ul>
+       </li>
+ 
+-      <li>
+-        <p>
+-          ARP reply handling.  Following flows are added to handle ARP replies.
+-        </p>
+-
+-        <p>
+-          For each distributed gateway logical router port a priority-92 flow
+-          with match <code>inport == <var>P</var> &amp;&amp;
+-          is_chassis_resident(cr-<var>P</var>) &amp;&amp; eth.bcast &amp;&amp;
+-          arp.op == 2 &amp;&amp; arp.spa == <var>I</var></code> with the
+-          action <code>put_arp(inport, arp.spa, arp.sha);</code> so that the
+-          resident gateway chassis can learn the GARP reply, where
+-          <var>P</var> is the distributed gateway router port name,
+-          <var>I</var> is the logical router port's network address.
+-        </p>
+-
+-        <p>
+-          For each distributed gateway logical router port a priority-92 flow
+-          with match <code>inport == <var>P</var> &amp;&amp;
+-          !is_chassis_resident(cr-<var>P</var>) &amp;&amp; eth.bcast &amp;&amp;
+-          arp.op == 2 &amp;&amp; arp.spa == <var>I</var></code> with the action
+-          <code>drop;</code> so that other chassis drop this packet.
+-        </p>
+-
+-        <p>
+-          A priority-90 flow with match <code>arp.op == 2</code> has actions
+-          <code>put_arp(inport, arp.spa, arp.sha);</code>.
+-        </p>
+-      </li>
+-
+       <li>
+         <p>
+           Reply to IPv6 Neighbor Solicitations.  These flows reply to
+@@ -1499,7 +1575,6 @@ arp.sha = <var>external_mac</var>;
+         </p>
+ 
+         <pre>
+-put_nd(inport, ip6.src, nd.sll);
+ nd_na_router {
+     eth.src = <var>E</var>;
+     ip6.src = <var>A</var>;
+@@ -1521,7 +1596,6 @@ nd_na_router {
+         </p>
+ 
+         <pre>
+-put_nd(inport, ip6.src, nd.sll);
+ nd_na {
+     eth.src = <var>E</var>;
+     ip6.src = <var>A</var>;
+@@ -1546,20 +1620,8 @@ nd_na {
+       </li>
+ 
+       <li>
+-        IPv6 neighbor advertisement handling.  This flow uses neighbor
+-        advertisements to populate the logical router's mac binding
+-        table.  A priority-90 flow with match <code>nd_na</code>
+-        has actions <code>put_nd(inport, nd.target, nd.tll);</code>.
+-      </li>
+-
+-      <li>
+-        IPv6 neighbor solicitation for non-hosted addresses handling.
+-        This flow uses neighbor solicitations to populate the logical
+-        router's mac binding table (ones that were directed at the
+-        logical router would have matched the priority-90 neighbor
+-        solicitation flow already).  A priority-80 flow with match
+-        <code>nd_ns</code> has actions
+-        <code>put_nd(inport, ip6.src, nd.sll);</code>.
++        Priority-85 flows which drops the ARP and IPv6 Neighbor Discovery
++        packets.
+       </li>
+ 
+       <li>
+@@ -1675,7 +1737,7 @@ icmp6 {
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 2: DEFRAG</h3>
++    <h3>Ingress Table 4: DEFRAG</h3>
+ 
+     <p>
+       This is to send packets to connection tracker for tracking and
+@@ -1733,7 +1795,7 @@ icmp6 {
+       </li>
+     </ul>
+ 
+-    <p>Ingress Table 3: UNSNAT on Distributed Routers</p>
++    <p>Ingress Table 5: UNSNAT on Distributed Routers</p>
+ 
+     <ul>
+       <li>
+@@ -1772,7 +1834,7 @@ icmp6 {
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 4: DNAT</h3>
++    <h3>Ingress Table 6: DNAT</h3>
+ 
+     <p>
+       Packets enter the pipeline with destination IP address that needs to
+@@ -1780,7 +1842,7 @@ icmp6 {
+       in the reverse direction needs to be unDNATed.
+     </p>
+ 
+-    <p>Ingress Table 4: Load balancing DNAT rules</p>
++    <p>Ingress Table 6: Load balancing DNAT rules</p>
+ 
+     <p>
+       Following load balancing DNAT flows are added for Gateway router or
+@@ -1851,7 +1913,7 @@ icmp6 {
+       </li>
+     </ul>
+ 
+-    <p>Ingress Table 4: DNAT on Gateway Routers</p>
++    <p>Ingress Table 6: DNAT on Gateway Routers</p>
+ 
+     <ul>
+       <li>
+@@ -1877,7 +1939,7 @@ icmp6 {
+       </li>
+     </ul>
+ 
+-    <p>Ingress Table 4: DNAT on Distributed Routers</p>
++    <p>Ingress Table 6: DNAT on Distributed Routers</p>
+ 
+     <p>
+       On distributed routers, the DNAT table only handles packets
+@@ -1924,7 +1986,7 @@ icmp6 {
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 5: IPv6 ND RA option processing</h3>
++    <h3>Ingress Table 7: IPv6 ND RA option processing</h3>
+ 
+     <ul>
+       <li>
+@@ -1954,7 +2016,7 @@ reg0[5] = put_nd_ra_opts(<var>options</var>);next;
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 6: IPv6 ND RA responder</h3>
++    <h3>Ingress Table 8: IPv6 ND RA responder</h3>
+ 
+     <p>
+       This table implements IPv6 ND RA responder for the IPv6 ND RA replies
+@@ -1999,7 +2061,7 @@ output;
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 7: IP Routing</h3>
++    <h3>Ingress Table 9: IP Routing</h3>
+ 
+     <p>
+       A packet that arrives at this table is an IP packet that should be
+@@ -2149,7 +2211,7 @@ next;
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 8: ARP/ND Resolution</h3>
++    <h3>Ingress Table 10: ARP/ND Resolution</h3>
+ 
+     <p>
+       Any packet that reaches this table is an IP packet whose next-hop
+@@ -2284,7 +2346,7 @@ next;
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 9: Check packet length</h3>
++    <h3>Ingress Table 11: Check packet length</h3>
+ 
+     <p>
+       For distributed logical routers with distributed gateway port configured
+@@ -2314,7 +2376,7 @@ REGBIT_PKT_LARGER = check_pkt_larger(<var>L</var>); next;
+       and advances to the next table.
+     </p>
+ 
+-    <h3>Ingress Table 10: Handle larger packets</h3>
++    <h3>Ingress Table 12: Handle larger packets</h3>
+ 
+     <p>
+       For distributed logical routers with distributed gateway port configured
+@@ -2363,7 +2425,7 @@ icmp4 {
+       and advances to the next table.
+     </p>
+ 
+-    <h3>Ingress Table 11: Gateway Redirect</h3>
++    <h3>Ingress Table 13: Gateway Redirect</h3>
+ 
+     <p>
+       For distributed logical routers where one of the logical router
+@@ -2425,7 +2487,7 @@ icmp4 {
+       </li>
+     </ul>
+ 
+-    <h3>Ingress Table 12: ARP Request</h3>
++    <h3>Ingress Table 14: ARP Request</h3>
+ 
+     <p>
+       In the common case where the Ethernet destination has been resolved, this
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 83a7ec14f..2f6826f17 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -144,20 +144,22 @@ enum ovn_stage {
+     PIPELINE_STAGE(SWITCH, OUT, PORT_SEC_L2,  9, "ls_out_port_sec_l2")    \
+                                                                       \
+     /* Logical router ingress stages. */                              \
+-    PIPELINE_STAGE(ROUTER, IN,  ADMISSION,      0, "lr_in_admission")    \
+-    PIPELINE_STAGE(ROUTER, IN,  IP_INPUT,       1, "lr_in_ip_input")     \
+-    PIPELINE_STAGE(ROUTER, IN,  DEFRAG,         2, "lr_in_defrag")       \
+-    PIPELINE_STAGE(ROUTER, IN,  UNSNAT,         3, "lr_in_unsnat")       \
+-    PIPELINE_STAGE(ROUTER, IN,  DNAT,           4, "lr_in_dnat")         \
+-    PIPELINE_STAGE(ROUTER, IN,  ND_RA_OPTIONS,  5, "lr_in_nd_ra_options") \
+-    PIPELINE_STAGE(ROUTER, IN,  ND_RA_RESPONSE, 6, "lr_in_nd_ra_response") \
+-    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,     7, "lr_in_ip_routing")   \
+-    PIPELINE_STAGE(ROUTER, IN,  POLICY,         8, "lr_in_policy")       \
+-    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE,    9, "lr_in_arp_resolve")  \
+-    PIPELINE_STAGE(ROUTER, IN,  CHK_PKT_LEN   , 10, "lr_in_chk_pkt_len")   \
+-    PIPELINE_STAGE(ROUTER, IN,  LARGER_PKTS,    11,"lr_in_larger_pkts")   \
+-    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT,    12, "lr_in_gw_redirect")  \
+-    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST,    13, "lr_in_arp_request")  \
++    PIPELINE_STAGE(ROUTER, IN,  ADMISSION,       0, "lr_in_admission")    \
++    PIPELINE_STAGE(ROUTER, IN,  LOOKUP_NEIGHBOR, 1, "lr_in_lookup_neighbor") \
++    PIPELINE_STAGE(ROUTER, IN,  LEARN_NEIGHBOR,  2, "lr_in_learn_neighbor") \
++    PIPELINE_STAGE(ROUTER, IN,  IP_INPUT,        3, "lr_in_ip_input")     \
++    PIPELINE_STAGE(ROUTER, IN,  DEFRAG,          4, "lr_in_defrag")       \
++    PIPELINE_STAGE(ROUTER, IN,  UNSNAT,          5, "lr_in_unsnat")       \
++    PIPELINE_STAGE(ROUTER, IN,  DNAT,            6, "lr_in_dnat")         \
++    PIPELINE_STAGE(ROUTER, IN,  ND_RA_OPTIONS,   7, "lr_in_nd_ra_options") \
++    PIPELINE_STAGE(ROUTER, IN,  ND_RA_RESPONSE,  8, "lr_in_nd_ra_response") \
++    PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,      9, "lr_in_ip_routing")   \
++    PIPELINE_STAGE(ROUTER, IN,  POLICY,          10, "lr_in_policy")       \
++    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE,     11, "lr_in_arp_resolve")  \
++    PIPELINE_STAGE(ROUTER, IN,  CHK_PKT_LEN   ,  12, "lr_in_chk_pkt_len")   \
++    PIPELINE_STAGE(ROUTER, IN,  LARGER_PKTS,     13,"lr_in_larger_pkts")   \
++    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT,     14, "lr_in_gw_redirect")  \
++    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST,     15, "lr_in_arp_request")  \
+                                                                       \
+     /* Logical router egress stages. */                               \
+     PIPELINE_STAGE(ROUTER, OUT, UNDNAT,    0, "lr_out_undnat")        \
+@@ -196,6 +198,8 @@ enum ovn_stage {
+ #define REGBIT_DISTRIBUTED_NAT  "reg9[2]"
+ /* Register to store the result of check_pkt_larger action. */
+ #define REGBIT_PKT_LARGER        "reg9[3]"
++#define REGBIT_LOOKUP_NEIGHBOR_RESULT "reg9[4]"
++#define REGBIT_SKIP_LOOKUP_NEIGHBOR "reg9[5]"
+ 
+ /* Returns an "enum ovn_stage" built from the arguments. */
+ static enum ovn_stage
+@@ -6420,7 +6424,96 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                       ds_cstr(&match), "next;");
+     }
+ 
+-    /* Logical router ingress table 1: IP Input. */
++    /* Logical router ingress table 1: LOOKUP_NEIGHBOR and
++     * table 2: LEARN_NEIGHBOR. */
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++        if (!od->nbr) {
++            continue;
++        }
++
++        /* Learn MAC bindings from ARP/IPv6 ND.
++         *
++         * For ARP packets, table LOOKUP_NEIGHBOR does a lookup for the
++         * (arp.spa, arp.sha) in the mac binding table using the 'lookup_arp'
++         * action and stores the result in REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
++         *
++         * For IPv6 ND NA packets, table LOOKUP_NEIGHBOR does a lookup
++         * for the (nd.target, nd.tll) in the mac binding table using the
++         * 'lookup_nd' action and stores the result in
++         * REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
++         *
++         * For IPv6 ND NS packets, table LOOKUP_NEIGHBOR does a lookup
++         * for the (ip6.src, nd.sll) in the mac binding table using the
++         * 'lookup_nd' action and stores the result in
++         * REGBIT_LOOKUP_NEIGHBOR_RESULT bit.
++         *
++         * Table LEARN_NEIGHBOR learns the mac-binding using the action
++         * - 'put_arp/put_nd' only if REGBIT_LOOKUP_NEIGHBOR_RESULT bit
++         * is not set.
++         *
++         * */
++
++        /* Flows for LOOKUP_NEIGHBOR. */
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
++                      "arp.op == 2",
++                      REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
++                      "lookup_arp(inport, arp.spa, arp.sha); next;");
++
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_na",
++                      REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
++                      "lookup_nd(inport, nd.target, nd.tll); next;");
++
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100, "nd_ns",
++                      REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
++                      "lookup_nd(inport, ip6.src, nd.sll); next;");
++
++        /* For other packet types, we can skip neighbor learning.
++         * So set REGBIT_SKIP_LOOKUP_NEIGHBOR to 1. */
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 0, "1",
++                      REGBIT_SKIP_LOOKUP_NEIGHBOR" = 1; next;");
++
++        /* Flows for LEARN_NEIGHBOR. */
++        /* Skip Neighbor learning if not required. */
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 100,
++                      REGBIT_SKIP_LOOKUP_NEIGHBOR" == 1 || "
++                      REGBIT_LOOKUP_NEIGHBOR_RESULT" == 1", "next;");
++
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
++                      "arp", "put_arp(inport, arp.spa, arp.sha); next;");
++
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
++                      "nd_na", "put_nd(inport, nd.target, nd.tll); next;");
++
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_LEARN_NEIGHBOR, 90,
++                      "nd_ns", "put_nd(inport, ip6.src, nd.sll); next;");
++    }
++
++    HMAP_FOR_EACH (op, key_node, ports) {
++        if (!op->nbrp) {
++            continue;
++        }
++
++        /* Check if we need to learn mac-binding from ARP requests. */
++        for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
++            ds_clear(&match);
++            ds_put_format(&match,
++                          "inport == %s && arp.spa == %s/%u && arp.op == 1",
++                          op->json_key,
++                          op->lrp_networks.ipv4_addrs[i].network_s,
++                          op->lrp_networks.ipv4_addrs[i].plen);
++            if (op->od->l3dgw_port && op == op->od->l3dgw_port
++                && op->od->l3redirect_port) {
++                ds_put_format(&match, " && is_chassis_resident(%s)",
++                              op->od->l3redirect_port->json_key);
++            }
++            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_LOOKUP_NEIGHBOR, 100,
++                          ds_cstr(&match),
++                          REGBIT_LOOKUP_NEIGHBOR_RESULT" = "
++                          "lookup_arp(inport, arp.spa, arp.sha); next;");
++        }
++    }
++
++    /* Logical router ingress table 3: IP Input. */
+     HMAP_FOR_EACH (od, key_node, datapaths) {
+         if (!od->nbr) {
+             continue;
+@@ -6442,10 +6535,13 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 95, "ip4.mcast",
+                       od->mcast_info.rtr.relay ? "next;" : "drop;");
+ 
+-        /* ARP reply handling.  Use ARP replies to populate the logical
+-         * router's ARP table. */
+-        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "arp.op == 2",
+-                      "put_arp(inport, arp.spa, arp.sha);");
++        /* Drop ARP packets (priority 85). ARP request packets for router's own
++         * IPs are handled with priority-90 flows.
++         * Drop IPv6 ND packets (priority 85). ND NA packets for router's own
++         * IPs are handled with priority-90 flows.
++         */
++        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 85,
++                      "arp || nd", "drop;");
+ 
+         /* Drop Ethernet local broadcast.  By definition this traffic should
+          * not be forwarded.*/
+@@ -6458,23 +6554,12 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 30,
+                       ds_cstr(&match), "drop;");
+ 
+-        /* ND advertisement handling.  Use advertisements to populate
+-         * the logical router's ARP/ND table. */
+-        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 90, "nd_na",
+-                      "put_nd(inport, nd.target, nd.tll);");
+-
+-        /* Lean from neighbor solicitations that were not directed at
+-         * us.  (A priority-90 flow will respond to requests to us and
+-         * learn the sender's mac address. */
+-        ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 80, "nd_ns",
+-                      "put_nd(inport, ip6.src, nd.sll);");
+-
+         /* Pass other traffic not already handled to the next table for
+          * routing. */
+         ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_INPUT, 0, "1", "next;");
+     }
+ 
+-    /* Logical router ingress table 1: IP Input for IPv4. */
++    /* Logical router ingress table 3: IP Input for IPv4. */
+     HMAP_FOR_EACH (op, key_node, ports) {
+         if (!op->nbrp) {
+             continue;
+@@ -6584,7 +6669,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+             ds_clear(&actions);
+             ds_put_format(&actions,
+-                "put_arp(inport, arp.spa, arp.sha); "
+                 "eth.dst = eth.src; "
+                 "eth.src = %s; "
+                 "arp.op = 2; /* ARP reply */ "
+@@ -6603,62 +6687,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                           ds_cstr(&match), ds_cstr(&actions));
+         }
+ 
+-        /* Learn from ARP requests that were not directed at us. A typical
+-         * use case is GARP request handling.  (A priority-90 flow will
+-         * respond to request to us and learn the sender's mac address.) */
+-        for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+-            ds_clear(&match);
+-            ds_put_format(&match,
+-                          "inport == %s && arp.spa == %s/%u && arp.op == 1",
+-                          op->json_key,
+-                          op->lrp_networks.ipv4_addrs[i].network_s,
+-                          op->lrp_networks.ipv4_addrs[i].plen);
+-            if (op->od->l3dgw_port && op == op->od->l3dgw_port
+-                && op->od->l3redirect_port) {
+-                ds_put_format(&match, " && is_chassis_resident(%s)",
+-                              op->od->l3redirect_port->json_key);
+-            }
+-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 80,
+-                          ds_cstr(&match),
+-                          "put_arp(inport, arp.spa, arp.sha);");
+-
+-        }
+-
+-        /* Handle GARP reply packets received on a distributed router gateway
+-         * port. GARP reply broadcast packets could be sent by external
+-         * switches. We don't want them to be handled by all the
+-         * ovn-controllers if they receive it. So add a priority-92 flow to
+-         * apply the put_arp action on a redirect chassis and drop it on
+-         * other chassis.
+-         * Note that we are already adding a priority-90 logical flow in the
+-         * table S_ROUTER_IN_IP_INPUT to apply the put_arp action if
+-         * arp.op == 2.
+-         * */
+-        if (op->od->l3dgw_port && op == op->od->l3dgw_port
+-                && op->od->l3redirect_port) {
+-            for (int i = 0; i < op->lrp_networks.n_ipv4_addrs; i++) {
+-                ds_clear(&match);
+-                ds_put_format(&match,
+-                              "inport == %s && is_chassis_resident(%s) && "
+-                              "eth.bcast && arp.op == 2 && arp.spa == %s/%u",
+-                              op->json_key, op->od->l3redirect_port->json_key,
+-                              op->lrp_networks.ipv4_addrs[i].network_s,
+-                              op->lrp_networks.ipv4_addrs[i].plen);
+-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92,
+-                              ds_cstr(&match),
+-                              "put_arp(inport, arp.spa, arp.sha);");
+-                ds_clear(&match);
+-                ds_put_format(&match,
+-                              "inport == %s && !is_chassis_resident(%s) && "
+-                              "eth.bcast && arp.op == 2 && arp.spa == %s/%u",
+-                              op->json_key, op->od->l3redirect_port->json_key,
+-                              op->lrp_networks.ipv4_addrs[i].network_s,
+-                              op->lrp_networks.ipv4_addrs[i].plen);
+-                ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 92,
+-                              ds_cstr(&match), "drop;");
+-            }
+-        }
+-
+         /* A set to hold all load-balancer vips that need ARP responses. */
+         struct sset all_ips = SSET_INITIALIZER(&all_ips);
+         int addr_family;
+@@ -6969,7 +6997,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+             ds_clear(&actions);
+             ds_put_format(&actions,
+-                          "put_nd(inport, ip6.src, nd.sll); "
+                           "nd_na_router { "
+                           "eth.src = %s; "
+                           "ip6.src = %s; "
+diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
+index c4099f25a..2a375c8b3 100644
+--- a/ovn/ovn-architecture.7.xml
++++ b/ovn/ovn-architecture.7.xml
+@@ -970,6 +970,24 @@
+             this temporary use.)
+           </p>
+         </dd>
++
++        <dt><code><var>R</var> = lookup_arp(<var>P</var>, <var>A</var>, <var>M</var>);</code></dt>
++        <dt><code><var>R</var> = lookup_nd(<var>P</var>, <var>A</var>, <var>M</var>);</code></dt>
++        <dd>
++          <p>
++            Implemented by storing arguments into OpenFlow fields, then
++            resubmitting to table 67, which <code>ovn-controller</code>
++            populates with flows generated from the <code>MAC_Binding</code>
++            table in the OVN Southbound database.  If there is a match in table
++            67, then its actions set the logical flow flag <code>MLF_LOOKUP_MAC</code>.
++          </p>
++
++          <p>
++            (The OpenFlow actions save and restore the OpenFlow fields used for
++            the arguments, so that the OVN actions do not have to be aware of
++            this temporary use.)
++          </p>
++        </dd>
+       </dl>
+     </li>
+ 
+diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
+index 477e7bc7a..e5fb51a9d 100644
+--- a/ovn/ovn-sb.xml
++++ b/ovn/ovn-sb.xml
+@@ -1397,6 +1397,35 @@
+           <p><b>Example:</b> <code>put_arp(inport, arp.spa, arp.sha);</code></p>
+         </dd>
+ 
++        <dt>
++          <code><var>R</var> = lookup_arp(<var>P</var>, <var>A</var>, <var>M</var>);</code>
++        </dt>
++
++        <dd>
++          <p>
++            <b>Parameters</b>: logical port string field <var>P</var>, 32-bit
++            IP address field <var>A</var>, 48-bit MAC address field
++            <var>M</var>.
++          </p>
++
++          <p>
++            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
++          </p>
++
++          <p>
++            Looks up <var>A</var> and <var>M</var> in <var>P</var>'s mac
++            binding table. If an entry is found, stores <code>1</code> in
++            the 1-bit subfield <var>R</var>, else 0.
++          </p>
++
++          <p>
++            <b>Example:</b>
++            <code>
++              reg0[0] = lookup_arp(inport, arp.spa, arp.sha);
++            </code>
++          </p>
++        </dd>
++
+         <dt><code>nd_ns { <var>action</var>; </code>...<code> };</code></dt>
+         <dd>
+           <p>
+@@ -1553,6 +1582,34 @@
+           <p><b>Example:</b> <code>put_nd(inport, nd.target, nd.tll);</code></p>
+         </dd>
+ 
++        <dt><code><var>R</var> = lookup_nd(<var>P</var>, <var>A</var>, <var>M</var>);</code>
++        </dt>
++
++        <dd>
++          <p>
++            <b>Parameters</b>: logical port string field <var>P</var>, 128-bit
++            IP address field <var>A</var>, 48-bit MAC address field
++            <var>M</var>.
++          </p>
++
++          <p>
++            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
++          </p>
++
++          <p>
++            Looks up <var>A</var> and <var>M</var> in <var>P</var>'s mac
++            binding table. If an entry is found, stores <code>1</code> in
++            the 1-bit subfield <var>R</var>, else 0.
++          </p>
++
++          <p>
++            <b>Example:</b>
++            <code>
++              reg0[0] = lookup_nd(inport, ip6.src, eth.src);
++            </code>
++          </p>
++        </dd>
++
+         <dt>
+           <code><var>R</var> = put_dhcp_opts(<var>D1</var> = <var>V1</var>, <var>D2</var> = <var>V2</var>, ..., <var>Dn</var> = <var>Vn</var>);</code>
+         </dt>
+diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
+index b532b8eaf..103b25891 100644
+--- a/ovn/utilities/ovn-trace.c
++++ b/ovn/utilities/ovn-trace.c
+@@ -556,6 +556,22 @@ ovntrace_mac_binding_find(const struct ovntrace_datapath *dp,
+     return NULL;
+ }
+ 
++static const struct ovntrace_mac_binding *
++ovntrace_mac_binding_find_mac_ip(const struct ovntrace_datapath *dp,
++                                 uint16_t port_key, const struct in6_addr *ip,
++                                 struct eth_addr mac)
++{
++    const struct ovntrace_mac_binding *bind;
++    HMAP_FOR_EACH_WITH_HASH (bind, node, hash_mac_binding(port_key, ip),
++                             &dp->mac_bindings) {
++        if (bind->port_key == port_key && ipv6_addr_equals(ip, &bind->ip)
++            && eth_addr_equals(bind->mac, mac)) {
++            return bind;
++        }
++    }
++    return NULL;
++}
++
+ /* If 's' ends with a UUID, returns a copy of it with the UUID truncated to
+  * just the first 6 characters; otherwise, returns a copy of 's'. */
+ static char *
+@@ -1704,6 +1720,51 @@ execute_get_mac_bind(const struct ovnact_get_mac_bind *bind,
+                          ETH_ADDR_ARGS(uflow->dl_dst));
+ }
+ 
++static void
++execute_lookup_mac(const struct ovnact_lookup_mac_bind *bind OVS_UNUSED,
++                   const struct ovntrace_datapath *dp OVS_UNUSED,
++                   struct flow *uflow OVS_UNUSED,
++                   struct ovs_list *super OVS_UNUSED)
++{
++    /* Get logical port number.*/
++    struct mf_subfield port_sf = expr_resolve_field(&bind->port);
++    ovs_assert(port_sf.n_bits == 32);
++    uint32_t port_key = mf_get_subfield(&port_sf, uflow);
++
++    /* Get IP address. */
++    struct mf_subfield ip_sf = expr_resolve_field(&bind->ip);
++    ovs_assert(ip_sf.n_bits == 32 || ip_sf.n_bits == 128);
++    union mf_subvalue ip_sv;
++    mf_read_subfield(&ip_sf, uflow, &ip_sv);
++    struct in6_addr ip = (ip_sf.n_bits == 32
++                          ? in6_addr_mapped_ipv4(ip_sv.ipv4)
++                          : ip_sv.ipv6);
++
++    /* Get MAC. */
++    struct mf_subfield mac_sf = expr_resolve_field(&bind->mac);
++    ovs_assert(mac_sf.n_bits == 48);
++    union mf_subvalue mac_sv;
++    mf_read_subfield(&mac_sf, uflow, &mac_sv);
++
++    const struct ovntrace_mac_binding *binding
++        = ovntrace_mac_binding_find_mac_ip(dp, port_key, &ip, mac_sv.mac);
++
++    struct mf_subfield dst = expr_resolve_field(&bind->dst);
++    uint8_t val = 0;
++
++    if (binding) {
++        val = 1;
++        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
++                             "/* MAC binding to "ETH_ADDR_FMT" found. */",
++                             ETH_ADDR_ARGS(uflow->dl_dst));
++    } else {
++        ovntrace_node_append(super, OVNTRACE_NODE_ACTION,
++                             "/* lookup failed - No MAC binding. */");
++    }
++    union mf_subvalue sv = { .u8_val = val };
++    mf_write_subfield_flow(&dst, &sv, uflow);
++}
++
+ static void
+ execute_put_opts(const struct ovnact_put_opts *po,
+                  const char *name, struct flow *uflow,
+@@ -2072,6 +2133,14 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
+             /* Nothing to do for tracing. */
+             break;
+ 
++        case OVNACT_LOOKUP_ARP:
++            execute_lookup_mac(ovnact_get_LOOKUP_ARP(a), dp, uflow, super);
++            break;
++
++        case OVNACT_LOOKUP_ND:
++            execute_lookup_mac(ovnact_get_LOOKUP_ND(a), dp, uflow, super);
++            break;
++
+         case OVNACT_PUT_DHCPV4_OPTS:
+             execute_put_dhcp_opts(ovnact_get_PUT_DHCPV4_OPTS(a),
+                                   "put_dhcp_opts", uflow, super);
+diff --git a/tests/ovn.at b/tests/ovn.at
+index df41a7549..d52c97541 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -2231,6 +2231,33 @@ put_arp(inport, arp.spa, arp.sha);
+     encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],controller(userdata=00.00.00.01.00.00.00.00),pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[]
+     has prereqs eth.type == 0x806 && eth.type == 0x806
+ 
++# lookup_arp
++reg0[0] = lookup_arp(inport, ip4.dst, eth.src);
++    encodes as push:NXM_NX_REG0[],push:NXM_OF_IP_DST[],pop:NXM_NX_REG0[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[96],pop:NXM_NX_REG0[]
++    has prereqs eth.type == 0x800
++reg1[1] = lookup_arp(inport, arp.spa, arp.sha);
++    encodes as push:NXM_NX_REG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ARP_SHA[],push:NXM_OF_ARP_SPA[],pop:NXM_NX_REG0[],pop:NXM_OF_ETH_SRC[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[65],pop:NXM_OF_ETH_SRC[],pop:NXM_NX_REG0[]
++    has prereqs eth.type == 0x806 && eth.type == 0x806
++
++lookup_arp;
++    Syntax error at `lookup_arp' expecting action.
++reg0[0] = lookup_arp;
++    Syntax error at `lookup_arp' expecting field name.
++reg0[0] = lookup_arp();
++    Syntax error at `)' expecting field name.
++reg0[0] = lookup_arp(inport);
++    Syntax error at `)' expecting `,'.
++reg0[0] = lookup_arp(inport ip4.dst);
++    Syntax error at `ip4.dst' expecting `,'.
++reg0[0] = lookup_arp(inport, ip4.dst;
++    Syntax error at `;' expecting `,'.
++reg0[0] = lookup_arp(inport, ip4.dst, eth.src;
++    Syntax error at `;' expecting `)'.
++reg0[0] = lookup_arp(inport, eth.dst);
++    Cannot use 48-bit field eth.dst[0..47] where 32-bit field is required.
++reg0[0] = lookup_arp(inport, ip4.src, ip4.dst);
++    Cannot use 32-bit field ip4.dst[0..31] where 48-bit field is required.
++
+ # put_dhcp_opts
+ reg1[0] = put_dhcp_opts(offerip = 1.2.3.4, router = 10.0.0.1);
+     encodes as controller(userdata=00.00.00.02.00.00.00.00.00.01.de.10.00.00.00.40.01.02.03.04.03.04.0a.00.00.01,pause)
+@@ -2331,6 +2358,35 @@ reg1[0] = put_dhcpv6_opts(ia_addr="ae70::4");
+ reg1[0] = put_dhcpv6_opts(ia_addr=ae70::4, domain_search=ae70::1);
+     DHCPv6 option domain_search requires string value.
+ 
++# lookup_nd
++reg2[0] = lookup_nd(inport, ip6.dst, eth.src);
++    encodes as push:NXM_NX_XXREG0[],push:NXM_NX_IPV6_DST[],pop:NXM_NX_XXREG0[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[32],pop:NXM_NX_XXREG0[]
++    has prereqs eth.type == 0x86dd
++reg3[0] = lookup_nd(inport, nd.target, nd.tll);
++    encodes as push:NXM_NX_XXREG0[],push:NXM_OF_ETH_SRC[],push:NXM_NX_ND_TLL[],push:NXM_NX_ND_TARGET[],pop:NXM_NX_XXREG0[],pop:NXM_OF_ETH_SRC[],set_field:0/0x40->reg10,resubmit(,67),move:NXM_NX_REG10[6]->NXM_NX_XXREG0[0],pop:NXM_OF_ETH_SRC[],pop:NXM_NX_XXREG0[]
++    has prereqs (icmp6.type == 0x87 || icmp6.type == 0x88) && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.type == 0x88 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && icmp6.code == 0 && eth.type == 0x86dd && ip.proto == 0x3a && (eth.type == 0x800 || eth.type == 0x86dd) && ip.ttl == 0xff && (eth.type == 0x800 || eth.type == 0x86dd)
++
++lookup_nd;
++    Syntax error at `lookup_nd' expecting action.
++reg0[0] = lookup_nd;
++    Syntax error at `lookup_nd' expecting field name.
++reg0[0] = lookup_nd();
++    Syntax error at `)' expecting field name.
++reg0[0] = lookup_nd(inport);
++    Syntax error at `)' expecting `,'.
++reg0[0] = lookup_nd(inport ip6.dst);
++    Syntax error at `ip6.dst' expecting `,'.
++reg0[0] = lookup_nd(inport, ip6.dst;
++    Syntax error at `;' expecting `,'.
++reg0[0] = lookup_nd(inport, ip6.dst, eth.src;
++    Syntax error at `;' expecting `)'.
++reg0[0] = lookup_nd(inport, eth.dst);
++    Cannot use 48-bit field eth.dst[0..47] where 128-bit field is required.
++reg0[0] = lookup_nd(inport, ip4.src, ip4.dst);
++    Cannot use 32-bit field ip4.src[0..31] where 128-bit field is required.
++reg0[0] = lookup_nd(inport, ip6.src, ip6.dst);
++    Cannot use 128-bit field ip6.dst[0..127] where 48-bit field is required.
++
+ # set_queue
+ set_queue(0);
+     encodes as set_queue:0
+@@ -15608,7 +15664,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ # Since the sw0-vir is not claimed by any chassis, eth.dst should be set to
+ # zero if the ip4.dst is the virtual ip in the router pipeline.
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=9 (lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
+ ])
+ 
+ ip_to_hex() {
+@@ -15644,7 +15700,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ # There should be an arp resolve flow to resolve the virtual_ip with the
+ # sw0-p1's MAC.
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=9 (lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
+ ])
+ 
+ # send the garp from sw0-p2 (in hv2). hv2 should claim sw0-vir
+@@ -15667,7 +15723,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ # There should be an arp resolve flow to resolve the virtual_ip with the
+ # sw0-p2's MAC.
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=9 (lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
+ ])
+ 
+ # Now send arp reply from sw0-p1. hv1 should claim sw0-vir
+@@ -15688,7 +15744,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ > lflows.txt
+ 
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=9 (lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:03; next;)
+ ])
+ 
+ # Delete hv1-vif1 port. hv1 should release sw0-vir
+@@ -15706,7 +15762,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ > lflows.txt
+ 
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=9 (lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 00:00:00:00:00:00; next;)
+ ])
+ 
+ # Now send arp reply from sw0-p2. hv2 should claim sw0-vir
+@@ -15727,7 +15783,7 @@ ovn-sbctl dump-flows lr0 | grep lr_in_arp_resolve | grep "reg0 == 10.0.0.10" \
+ > lflows.txt
+ 
+ AT_CHECK([cat lflows.txt], [0], [dnl
+-  table=9 (lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
++  table=11(lr_in_arp_resolve  ), priority=100  , match=(outport == "lr0-sw0" && reg0 == 10.0.0.10), action=(eth.dst = 50:54:00:00:00:04; next;)
+ ])
+ 
+ # Delete sw0-p2 logical port
+@@ -16332,3 +16388,225 @@ OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
+ 
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
++
++AT_SETUP([ovn -- ARP lookup before learning])
++AT_KEYWORDS([virtual ports])
++AT_SKIP_IF([test $HAVE_PYTHON = no])
++ovn_start
++
++send_garp() {
++    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
++    local request=${eth_dst}${eth_src}08060001080006040001${eth_src}${spa}${eth_dst}${tpa}
++    as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
++}
++
++send_arp_reply() {
++    local hv=$1 inport=$2 eth_src=$3 eth_dst=$4 spa=$5 tpa=$6
++    local request=${eth_dst}${eth_src}08060001080006040002${eth_src}${spa}${eth_dst}${tpa}
++    as hv$hv ovs-appctl netdev-dummy/receive hv${hv}-vif$inport $request
++}
++
++net_add n1
++
++sim_add hv1
++as hv1
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.1
++ovs-vsctl -- add-port br-int hv1-vif1 -- \
++    set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
++    options:tx_pcap=hv1/vif1-tx.pcap \
++    options:rxq_pcap=hv1/vif1-rx.pcap \
++    ofport-request=1
++ovs-vsctl -- add-port br-int hv1-vif2 -- \
++    set interface hv1-vif2 external-ids:iface-id=sw0-p3 \
++    options:tx_pcap=hv1/vif2-tx.pcap \
++    options:rxq_pcap=hv1/vif2-rx.pcap \
++    ofport-request=2
++
++sim_add hv2
++as hv2
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.2
++ovs-vsctl -- add-port br-int hv2-vif1 -- \
++    set interface hv2-vif1 external-ids:iface-id=sw1-p1 \
++    options:tx_pcap=hv2/vif1-tx.pcap \
++    options:rxq_pcap=hv2/vif1-rx.pcap \
++    ofport-request=1
++
++ovn-nbctl ls-add sw0
++
++ovn-nbctl lsp-add sw0 sw0-p1
++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03"
++
++# Create the second logical switch with one port
++ovn-nbctl ls-add sw1
++ovn-nbctl lsp-add sw1 sw1-p1
++ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
++ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
++
++# Create a logical router and attach both logical switches
++ovn-nbctl lr-add lr0
++ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
++ovn-nbctl lsp-add sw0 sw0-lr0
++ovn-nbctl lsp-set-type sw0-lr0 router
++ovn-nbctl lsp-set-addresses sw0-lr0 00:00:00:00:ff:01
++ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24
++ovn-nbctl lsp-add sw1 sw1-lr0
++ovn-nbctl lsp-set-type sw1-lr0 router
++ovn-nbctl lsp-set-addresses sw1-lr0 00:00:00:00:ff:02
++ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++OVN_POPULATE_ARP
++ovn-nbctl --wait=hv sync
++
++as hv1 ovs-appctl -t ovn-controller vlog/set dbg
++
++ip_to_hex() {
++    printf "%02x%02x%02x%02x" "$@"
++}
++
++# From sw0-p1 send GARP for 10.0.0.30.
++# ovn-controller should learn the
++#   mac_binding entry
++#     port - lr0-sw0
++#     ip - 10.0.0.30
++#     mac - 50:54:00:00:00:03
++
++AT_CHECK([test 0 = `ovn-sbctl list mac_binding | wc -l`])
++eth_src=505400000003
++eth_dst=ffffffffffff
++spa=$(ip_to_hex 10 0 0 30)
++tpa=$(ip_to_hex 10 0 0 30)
++send_garp 1 1 $eth_src $eth_dst $spa $tpa
++
++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`])
++
++AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
++list mac_binding], [0], [lr0-sw0
++10.0.0.30
++50:54:00:00:00:03
++])
++
++AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \
++grep controller | grep -v n_packets=0 | wc -l`])
++
++# Wait for an entry in table=67
++OVS_WAIT_UNTIL(
++    [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_packets=0 \
++| wc -l`]
++)
++
++# Send garp again. This time the packet should not be sent to ovn-controller.
++send_garp 1 1 $eth_src $eth_dst $spa $tpa
++# Wait for an entry in table=67
++OVS_WAIT_UNTIL([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep n_packets=1 | wc -l`])
++
++# The packet should not be sent to ovn-controller. The packet
++count should be 1 only.
++AT_CHECK([test 1 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++AT_CHECK([test 1 = `as hv1 ovs-ofctl dump-flows br-int table=10 | grep arp | \
++grep controller | grep -v n_packets=0 | wc -l`])
++
++# Now send garp packet with different mac.
++eth_src=505400000013
++eth_dst=ffffffffffff
++spa=$(ip_to_hex 10 0 0 30)
++tpa=$(ip_to_hex 10 0 0 30)
++send_garp 1 1 $eth_src $eth_dst $spa $tpa
++
++# The garp packet should be sent to ovn-controller and the mac_binding entry
++# should be updated.
++OVS_WAIT_UNTIL([test 2 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++
++AT_CHECK([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`])
++
++AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
++list mac_binding], [0], [lr0-sw0
++10.0.0.30
++50:54:00:00:00:13
++])
++
++# Send ARP request to lrp - lr0-sw1 (20.0.0.1) using src mac 50:54:00:00:00:33
++# and src ip - 10.0.0.50.from sw0-p1.
++# ovn-controller should add the mac_binding entry
++#   logical_port - lr0
++#   IP           - 10.0.0.50
++#   MAC          - 50:54:00:00:00:33
++eth_src=505400000033
++eth_dst=ffffffffffff
++spa=$(ip_to_hex 10 0 0 50)
++tpa=$(ip_to_hex 20 0 0 1)
++
++send_garp 1 1 $eth_src $eth_dst $spa $tpa
++
++# The garp packet should be sent to ovn-controller and the mac_binding entry
++# should be updated.
++OVS_WAIT_UNTIL([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++
++OVS_WAIT_UNTIL(
++    [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \
++| wc -l`]
++)
++
++AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
++find mac_binding ip=10.0.0.50], [0], [lr0-sw0
++10.0.0.50
++50:54:00:00:00:33
++])
++
++# Send the same packet again.
++send_garp 1 1 $eth_src $eth_dst $spa $tpa
++
++OVS_WAIT_UNTIL(
++    [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:33 \
++| grep n_packets=1 | wc -l`]
++)
++
++AT_CHECK([test 3 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++
++# Now send ARP reply packet with IP - 10.0.0.40 and mac 505400000023
++eth_src=505400000023
++eth_dst=ffffffffffff
++spa=$(ip_to_hex 10 0 0 40)
++tpa=$(ip_to_hex 10 0 0 50)
++send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
++
++# ovn-controller should add the
++#   mac_binding entry
++#     port - lr0-sw0
++#     ip - 10.0.0.40
++#     mac - 50:54:00:00:00:23
++
++# The garp packet should be sent to ovn-controller and the mac_binding entry
++# should be updated.
++OVS_WAIT_UNTIL([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++
++# Wait for an entry in table=67 for the learnt mac_binding entry.
++
++OVS_WAIT_UNTIL(
++    [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \
++| wc -l`]
++)
++
++# Send the same garp reply. This time it should not be sent to ovn-controller.
++send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
++OVS_WAIT_UNTIL(
++    [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \
++| grep n_packets=1 | wc -l`]
++)
++
++AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++
++send_arp_reply 1 1 $eth_src $eth_dst $spa $tpa
++OVS_WAIT_UNTIL(
++    [test 1 = `as hv1 ovs-ofctl dump-flows br-int table=67 | grep dl_src=50:54:00:00:00:23 \
++| grep n_packets=2 | wc -l`]
++)
++
++AT_CHECK([test 4 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
++
++OVN_CLEANUP([hv1], [hv2])
++AT_CLEANUP
+diff --git a/tests/test-ovn.c b/tests/test-ovn.c
+index cf1bc5432..a7adc1c9a 100644
+--- a/tests/test-ovn.c
++++ b/tests/test-ovn.c
+@@ -1297,6 +1297,7 @@ test_parse_actions(struct ovs_cmdl_context *ctx OVS_UNUSED)
+                 .egress_ptable = 40,
+                 .output_ptable = 64,
+                 .mac_bind_ptable = 65,
++                .mac_lookup_ptable = 67,
+             };
+             struct ofpbuf ofpacts;
+             ofpbuf_init(&ofpacts, 0);
+-- 
+2.21.0
+
diff --git a/SOURCES/0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch b/SOURCES/0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch
new file mode 100644
index 0000000..75b8fdf
--- /dev/null
+++ b/SOURCES/0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch
@@ -0,0 +1,116 @@
+From e00d6083ad1ee972ec3705401dc9c8d43cd43f7d Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Wed, 28 Aug 2019 01:55:29 +0000
+Subject: [PATCH 03/12] OVN: Vlan backed DVR N-S, avoid get_arp on non redirect
+ chassis.
+
+Background:
+With 795d7f24ce0e ("OVN: Enable E-W Traffic, Vlan backed DVR"), we have added
+support for E-W workflow for vlan backed DVRs.
+
+This series enables N-S workflow for vlan backed DVRs.
+
+Key difference between E-W and N-S traffic flow is that
+N-S flow requires a gateway chassis. A gateway chassis
+will be respondible for following:
+a. Doing Network Address Translation (NAT).
+b. Becoming entry and exit point for North->South
+   and South->North traffic respectively.
+
+OVN by default always uses overlay encapsulation to redirect
+the packet to gateway chassis. This series will enable
+the redirection to gateway chassis in the absence of encapsulation.
+
+This patch:
+a. Make sure that ARP request for endpoint behind the gateway
+   router port is sent from gateway chassis only and not from
+   host(compute) chassis.
+
+b. This is achieved by adding a new logical flow in
+   lr_in_arp_resolve at priority=50.
+
+c. This flow run on non gateway chassis and sets the destination
+   mac to router port mac, if outport is a gateway chassis attached
+   router port and redirect-type is set as "bridged".
+   Example logical flow:
+   table=9 (lr_in_arp_resolve  ), priority=50   , match=(outport == "router-to-underlay" && !is_chassis_resident("cr-router-to-underlay")), action=(eth.dst = 00:00:01:01:02:04; next;)
+
+d. This change is needed because other wise for non resolved ARPs,
+   we will end up doing get_arp in host chassis. Doing so will
+   have following issues:
+   i. We want all the interation with North bound endpoints via
+      gateway chassis only, doing so on host chassis will violate
+      that.
+
+  ii. With get_arp, ovn-controller will generate the ARP using router
+      port's mac as source mac, which will lead us to the same issue,
+      where router port mac will be going through continous mac moves
+      in physical network. Worst, it would affect the redirection,
+      since it uses router port mac as destination mac.
+
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/northd/ovn-northd.8.xml | 12 ++++++++++++
+ ovn/northd/ovn-northd.c     | 22 ++++++++++++++++++++++
+ 2 files changed, 34 insertions(+)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index dc5fdf471..83c90d680 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -2422,6 +2422,18 @@ next;
+           <code>get_nd(outport, xxreg0); next;</code>.
+         </p>
+       </li>
++
++      <li>
++        <p>
++          For logical router port with redirect-chassis and redirect-type
++          being set as <code>bridged</code>, a priority-50 flow will match
++          <code>outport == "ROUTER_PORT" and !is_chassis_resident
++          ("cr-ROUTER_PORT")</code> has actions <code>eth.dst = <var>E</var>;
++          next;</code>, where <var>E</var> is the ethernet address of the
++          logical router port.
++        </p>
++      </li>
++
+     </ul>
+ 
+     <h3>Ingress Table 11: Check packet length</h3>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index cd13f308e..27e90fcb2 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -8459,6 +8459,28 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                                   100, ds_cstr(&match), ds_cstr(&actions));
+                 }
+             }
++
++            if (!op->derived && op->od->l3redirect_port) {
++                const char *redirect_type = smap_get(&op->nbrp->options,
++                                                     "redirect-type");
++                if (redirect_type && !strcasecmp(redirect_type, "bridged")) {
++                    /* Packet is on a non gateway chassis and
++                     * has an unresolved ARP on a network behind gateway
++                     * chassis attached router port. Since, redirect type
++                     * is set to vlan, hence instead of calling "get_arp"
++                     * on this node, we will redirect the packet to gateway
++                     * chassis, by setting destination mac router port mac.*/
++                    ds_clear(&match);
++                    ds_put_format(&match, "outport == %s && "
++                                  "!is_chassis_resident(%s)", op->json_key,
++                                  op->od->l3redirect_port->json_key);
++                    ds_clear(&actions);
++                    ds_put_format(&actions, "eth.dst = %s; next;",
++                                  op->lrp_networks.ea_s);
++                    ovn_lflow_add(lflows, op->od, S_ROUTER_IN_ARP_RESOLVE,
++                                  50, ds_cstr(&match), ds_cstr(&actions));
++                }
++            }
+         } else if (op->od->n_router_ports && strcmp(op->nbsp->type, "router")
+                    && strcmp(op->nbsp->type, "virtual")) {
+             /* This is a logical switch port that backs a VM or a container.
+-- 
+2.23.0
+
diff --git a/SOURCES/0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch b/SOURCES/0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch
new file mode 100644
index 0000000..494afd9
--- /dev/null
+++ b/SOURCES/0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch
@@ -0,0 +1,88 @@
+From 3fb3e512bc90714907a520fb4a7ef84582bce6a7 Mon Sep 17 00:00:00 2001
+Message-Id: <3fb3e512bc90714907a520fb4a7ef84582bce6a7.1578588395.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com>
+References: <600a018ea1136e184b421c86da170b35d05e949f.1578588395.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 7 Jan 2020 17:50:06 +0100
+Subject: [PATCH 3/3] RA Route Info Option: copy route info string in order to
+ avoid truncated value
+
+ipv6_ra_send can run 2 times in a row before prepare_ipv6_ras updates
+the route info. Copy route info string in packet_put_ra_route_info_opt
+in order to avoid sending truncated route info on the wire.
+Moreover move ip6_hdr definition just before accessing it because the
+packet can be reallocated if the data area is not big enough for packet
+content
+
+Fixes: 9f7f466af ("Add support for Route Info Option in RA - RFC 4191")
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/pinctrl.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2473,13 +2473,14 @@ out:
+ 
+ static void
+ packet_put_ra_route_info_opt(struct dp_packet *b, ovs_be32 lifetime,
+-                             char *route_list)
++                             char *route_data)
+ {
+     size_t prev_l4_size = dp_packet_l4_size(b);
+-    struct ip6_hdr *nh = dp_packet_l3(b);
+-    char *t0, *r0 = NULL;
++    char *route_list, *t0, *r0 = NULL;
+     size_t size = 0;
+ 
++    route_list = xstrdup(route_data);
++
+     for (t0 = strtok_r(route_list, ",", &r0); t0;
+          t0 = strtok_r(NULL, ",", &r0)) {
+         struct ovs_nd_route_info nd_rinfo;
+@@ -2507,11 +2508,11 @@ packet_put_ra_route_info_opt(struct dp_p
+                 uint8_t plen;
+ 
+                 if (!extract_ip_addresses(t1, &route)) {
+-                    return;
++                    goto out;
+                 }
+                 if (!route.n_ipv6_addrs) {
+                     destroy_lport_addresses(&route);
+-                    return;
++                    goto out;
+                 }
+ 
+                 nd_rinfo.prefix_len = route.ipv6_addrs->plen;
+@@ -2526,17 +2527,20 @@ packet_put_ra_route_info_opt(struct dp_p
+                 break;
+             }
+             default:
+-                return;
++                goto out;
+             }
+         }
+     }
+ 
++    struct ip6_hdr *nh = dp_packet_l3(b);
+     nh->ip6_plen = htons(prev_l4_size + size);
+     struct ovs_ra_msg *ra = dp_packet_l4(b);
+     ra->icmph.icmp6_cksum = 0;
+     uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+     ra->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ra,
+                                                       prev_l4_size + size));
++out:
++    free(route_list);
+ }
+ 
+ /* Called with in the pinctrl_handler thread context. */
+@@ -2580,7 +2584,7 @@ ipv6_ra_send(struct rconn *swconn, struc
+     }
+     if (ra->config->route_info.length) {
+         packet_put_ra_route_info_opt(&packet, htonl(0xffffffff),
+-                                     ra->config->route_info.string);
++                                     ds_cstr(&ra->config->route_info));
+     }
+ 
+     uint64_t ofpacts_stub[4096 / 8];
diff --git a/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch b/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch
new file mode 100644
index 0000000..e94d6eb
--- /dev/null
+++ b/SOURCES/0003-controller-fix-ip-buffering-with-static-routes.patch
@@ -0,0 +1,61 @@
+From 754d5581fa9d5de97f7c2acf8c2900e105f588c9 Mon Sep 17 00:00:00 2001
+Message-Id: <754d5581fa9d5de97f7c2acf8c2900e105f588c9.1590585469.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
+References: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Wed, 20 May 2020 22:01:16 +0200
+Subject: [PATCH ovn 2/3] controller: fix ip buffering with static routes
+
+When the arp request is sent to a gw router and not to the final
+destination of the packet buffered_packets_map needs to be updated using
+next-hop ip address and not the destionation one.
+
+Fixes: 2e5cdb4b1392 ("OVN: add buffering support for ip packets")
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ ovn/controller/pinctrl.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -693,8 +693,7 @@ pinctrl_find_buffered_packets(const stru
+ 
+ /* Called with in the pinctrl_handler thread context. */
+ static int
+-pinctrl_handle_buffered_packets(const struct flow *ip_flow,
+-                                struct dp_packet *pkt_in,
++pinctrl_handle_buffered_packets(struct dp_packet *pkt_in,
+                                 const struct match *md, bool is_arp)
+     OVS_REQUIRES(pinctrl_mutex)
+ {
+@@ -703,9 +702,10 @@ pinctrl_handle_buffered_packets(const st
+     struct in6_addr addr;
+ 
+     if (is_arp) {
+-        addr = in6_addr_mapped_ipv4(ip_flow->nw_dst);
++        addr = in6_addr_mapped_ipv4(htonl(md->flow.regs[0]));
+     } else {
+-        addr = ip_flow->ipv6_dst;
++        ovs_be128 ip6 = hton128(flow_get_xxreg(&md->flow, 0));
++        memcpy(&addr, &ip6, sizeof addr);
+     }
+ 
+     uint32_t hash = hash_bytes(&addr, sizeof addr, 0);
+@@ -746,7 +746,7 @@ pinctrl_handle_arp(struct rconn *swconn,
+     }
+ 
+     ovs_mutex_lock(&pinctrl_mutex);
+-    pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, true);
++    pinctrl_handle_buffered_packets(pkt_in, md, true);
+     ovs_mutex_unlock(&pinctrl_mutex);
+ 
+     /* Compose an ARP packet. */
+@@ -4338,7 +4338,7 @@ pinctrl_handle_nd_ns(struct rconn *swcon
+     }
+ 
+     ovs_mutex_lock(&pinctrl_mutex);
+-    pinctrl_handle_buffered_packets(ip_flow, pkt_in, md, false);
++    pinctrl_handle_buffered_packets(pkt_in, md, false);
+     ovs_mutex_unlock(&pinctrl_mutex);
+ 
+     uint64_t packet_stub[128 / 8];
diff --git a/SOURCES/0003-northd-Load-config-before-processing-nbdb-contents.patch b/SOURCES/0003-northd-Load-config-before-processing-nbdb-contents.patch
new file mode 100644
index 0000000..f0bdad0
--- /dev/null
+++ b/SOURCES/0003-northd-Load-config-before-processing-nbdb-contents.patch
@@ -0,0 +1,133 @@
+From f482ce2cbc0e76f1048927077021945ba0e1c216 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Mon, 9 Dec 2019 10:56:03 -0500
+Subject: [PATCH ovn 3/4] northd: Load config before processing nbdb contents
+
+Reorder ovnnb_db_run() such that configuration parameters are loaded
+or initialized before processing the nbdb contents.
+
+I found this bug because I noticed dynamic MAC addresses being
+assigned at ovn-northd startup with an empty prefix.  Later, it would
+switch to allocating MAC addresses with the random prefix that was
+generated.
+
+The impact of this bug is particularly bad if ovn-northd restarts in
+an existing environment.  ovn-northd will check previously assigned
+dynamic addresses for validity.  At startup, previously assigned MAC
+addresses will all appear invalid because they have a non-empty
+prefix, so it will reset them all.  In the case of IPv6, this also
+causes the IPv6 addresses change, since OVN assigned dynamic IPv6
+addresses are based on the MAC address.
+
+With ovn-kubernetes, whatever first set of addresses were assigned is
+what ends up cached on the Node object and used by the Pod.  This bug
+can cause all of this to get out of sync, breaking network
+connectivity for Pods on an OVN virtual network.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+
+(cherry picked from upstream commit b441bf3a7211461552242d50e6b0c41e68d4799d)
+---
+ ovn/northd/ovn-northd.c | 78 ++++++++++++++++++++++++-------------------------
+ 1 file changed, 39 insertions(+), 39 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 40835a0..af67f63 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -10145,45 +10145,6 @@ ovnnb_db_run(struct northd_context *ctx,
+     struct shash meter_groups = SHASH_INITIALIZER(&meter_groups);
+     struct hmap lbs;
+ 
+-    build_datapaths(ctx, datapaths, lr_list);
+-    build_ports(ctx, sbrec_chassis_by_name, datapaths, ports);
+-    build_ovn_lbs(ctx, ports, &lbs);
+-    build_ipam(datapaths, ports);
+-    build_port_group_lswitches(ctx, &port_groups, ports);
+-    build_lrouter_groups(ports, lr_list);
+-    build_ip_mcast(ctx, datapaths);
+-    build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups);
+-    build_meter_groups(ctx, &meter_groups);
+-    build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
+-                 &igmp_groups, &meter_groups, &lbs);
+-
+-    sync_address_sets(ctx);
+-    sync_port_groups(ctx);
+-    sync_meters(ctx);
+-    sync_dns_entries(ctx, datapaths);
+-    destroy_ovn_lbs(&lbs);
+-    hmap_destroy(&lbs);
+-
+-    struct ovn_igmp_group *igmp_group, *next_igmp_group;
+-
+-    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, &igmp_groups) {
+-        ovn_igmp_group_destroy(&igmp_groups, igmp_group);
+-    }
+-
+-    struct ovn_port_group *pg, *next_pg;
+-    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) {
+-        ovn_port_group_destroy(&port_groups, pg);
+-    }
+-    hmap_destroy(&igmp_groups);
+-    hmap_destroy(&mcast_groups);
+-    hmap_destroy(&port_groups);
+-
+-    struct shash_node *node, *next;
+-    SHASH_FOR_EACH_SAFE (node, next, &meter_groups) {
+-        shash_delete(&meter_groups, node);
+-    }
+-    shash_destroy(&meter_groups);
+-
+     /* Sync ipsec configuration.
+      * Copy nb_cfg from northbound to southbound database.
+      * Also set up to update sb_cfg once our southbound transaction commits. */
+@@ -10257,6 +10218,45 @@ ovnnb_db_run(struct northd_context *ctx,
+     controller_event_en = smap_get_bool(&nb->options,
+                                         "controller_event", false);
+ 
++    build_datapaths(ctx, datapaths, lr_list);
++    build_ports(ctx, sbrec_chassis_by_name, datapaths, ports);
++    build_ovn_lbs(ctx, ports, &lbs);
++    build_ipam(datapaths, ports);
++    build_port_group_lswitches(ctx, &port_groups, ports);
++    build_lrouter_groups(ports, lr_list);
++    build_ip_mcast(ctx, datapaths);
++    build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups);
++    build_meter_groups(ctx, &meter_groups);
++    build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
++                 &igmp_groups, &meter_groups, &lbs);
++
++    sync_address_sets(ctx);
++    sync_port_groups(ctx);
++    sync_meters(ctx);
++    sync_dns_entries(ctx, datapaths);
++    destroy_ovn_lbs(&lbs);
++    hmap_destroy(&lbs);
++
++    struct ovn_igmp_group *igmp_group, *next_igmp_group;
++
++    HMAP_FOR_EACH_SAFE (igmp_group, next_igmp_group, hmap_node, &igmp_groups) {
++        ovn_igmp_group_destroy(&igmp_groups, igmp_group);
++    }
++
++    struct ovn_port_group *pg, *next_pg;
++    HMAP_FOR_EACH_SAFE (pg, next_pg, key_node, &port_groups) {
++        ovn_port_group_destroy(&port_groups, pg);
++    }
++    hmap_destroy(&igmp_groups);
++    hmap_destroy(&mcast_groups);
++    hmap_destroy(&port_groups);
++
++    struct shash_node *node, *next;
++    SHASH_FOR_EACH_SAFE (node, next, &meter_groups) {
++        shash_delete(&meter_groups, node);
++    }
++    shash_destroy(&meter_groups);
++
+     cleanup_macam(&macam);
+ }
+ 
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0003-northd-add-rate-limiting-support-for-SB-controller-e.patch b/SOURCES/0003-northd-add-rate-limiting-support-for-SB-controller-e.patch
new file mode 100644
index 0000000..bd9352e
--- /dev/null
+++ b/SOURCES/0003-northd-add-rate-limiting-support-for-SB-controller-e.patch
@@ -0,0 +1,209 @@
+From 0cb57d3e670d841510f911fbbf8a3db57239bf61 Mon Sep 17 00:00:00 2001
+Message-Id: <0cb57d3e670d841510f911fbbf8a3db57239bf61.1568637354.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com>
+References: <3624362cb395b6bc90f6fd69e12988979db95b7e.1568637354.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 3 Sep 2019 16:22:53 +0200
+Subject: [PATCH ovn 3/3] northd: add rate limiting support for SB controller
+ events
+
+Introduce the capability to associate a meter to each controller event
+type in order to not overload the pinctrl thread under heavy load.
+Each event type relies on a meter with a defined name:
+- empty_lb_backends: event-elb
+
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/northd/ovn-northd.8.xml |  5 ++++
+ ovn/northd/ovn-northd.c     | 55 ++++++++++++++++++++++++++++++-----------
+ ovn/ovn-nb.xml              |  8 ++++++
+ tests/ovn.at            |  1 +
+ 4 files changed, 55 insertions(+), 14 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -301,6 +301,11 @@
+       <code>Pre-stateful</code> to send IP packets to the connection tracker
+       for packet de-fragmentation before eventually advancing to ingress table
+       <code>LB</code>.
++      If controller_event has been enabled and load balancing rules with
++      empty backends have been added in <code>OVN_Northbound</code>, a 130 flow
++      is added to trigger ovn-controller events whenever the chassis receives a
++      packet for that particular VIP. If <code>event-elb</code> meter has been
++      previously created, it will be associated to the empty_lb logical flow
+     </p>
+ 
+     <h3>Ingress Table 5: Pre-stateful</h3>
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -3885,14 +3885,18 @@ static void
+ build_empty_lb_event_flow(struct ovn_datapath *od, struct hmap *lflows,
+                           struct smap_node *node, char *ip_address,
+                           struct nbrec_load_balancer *lb, uint16_t port,
+-                          int addr_family, int pl)
++                          int addr_family, int pl, struct shash *meter_groups)
+ {
+     if (!controller_event_en || node->value[0]) {
+         return;
+     }
+ 
+     struct ds match = DS_EMPTY_INITIALIZER;
+-    char *action;
++    char *meter = "", *action;
++
++    if (meter_groups && shash_find(meter_groups, "event-elb")) {
++        meter = "event-elb";
++    }
+ 
+     if (addr_family == AF_INET) {
+         ds_put_format(&match, "ip4.dst == %s && %s",
+@@ -3906,18 +3910,20 @@ build_empty_lb_event_flow(struct ovn_dat
+                       port);
+     }
+     action = xasprintf("trigger_event(event = \"%s\", "
+-                   "vip = \"%s\", protocol = \"%s\", "
+-                   "load_balancer = \"" UUID_FMT "\");",
+-                   event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
+-                   node->key, lb->protocol,
+-                   UUID_ARGS(&lb->header_.uuid));
++                       "meter = \"%s\", vip = \"%s\", "
++                       "protocol = \"%s\", "
++                       "load_balancer = \"" UUID_FMT "\");",
++                       event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
++                       meter, node->key, lb->protocol,
++                       UUID_ARGS(&lb->header_.uuid));
+     ovn_lflow_add(lflows, od, pl, 130, ds_cstr(&match), action);
+     ds_destroy(&match);
+     free(action);
+ }
+ 
+ static void
+-build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
++build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
++             struct shash *meter_groups)
+ {
+     /* Do not send ND packets to conntrack */
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
+@@ -3954,7 +3960,8 @@ build_pre_lb(struct ovn_datapath *od, st
+             }
+ 
+             build_empty_lb_event_flow(od, lflows, node, ip_address, lb,
+-                                      port, addr_family, S_SWITCH_IN_PRE_LB);
++                                      port, addr_family, S_SWITCH_IN_PRE_LB,
++                                      meter_groups);
+ 
+             free(ip_address);
+ 
+@@ -4774,7 +4781,8 @@ build_lrouter_groups(struct hmap *ports,
+ static void
+ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+                     struct hmap *port_groups, struct hmap *lflows,
+-                    struct hmap *mcgroups, struct hmap *igmp_groups)
++                    struct hmap *mcgroups, struct hmap *igmp_groups,
++                    struct shash *meter_groups)
+ {
+     /* This flow table structure is documented in ovn-northd(8), so please
+      * update ovn-northd.8.xml if you change anything. */
+@@ -4791,7 +4799,7 @@ build_lswitch_flows(struct hmap *datapat
+         }
+ 
+         build_pre_acls(od, lflows);
+-        build_pre_lb(od, lflows);
++        build_pre_lb(od, lflows, meter_groups);
+         build_pre_stateful(od, lflows);
+         build_acls(od, lflows, port_groups);
+         build_qos(od, lflows);
+@@ -8084,12 +8092,13 @@ build_lrouter_flows(struct hmap *datapat
+ static void
+ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
+              struct hmap *ports, struct hmap *port_groups,
+-             struct hmap *mcgroups, struct hmap *igmp_groups)
++             struct hmap *mcgroups, struct hmap *igmp_groups,
++             struct shash *meter_groups)
+ {
+     struct hmap lflows = HMAP_INITIALIZER(&lflows);
+ 
+     build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups,
+-                        igmp_groups);
++                        igmp_groups, meter_groups);
+     build_lrouter_flows(datapaths, ports, &lflows);
+ 
+     /* Push changes to the Logical_Flow table to database. */
+@@ -8703,6 +8712,16 @@ build_mcast_groups(struct northd_context
+ }
+ 
+ static void
++build_meter_groups(struct northd_context *ctx,
++                   struct shash *meter_groups)
++{
++    const struct nbrec_meter *nb_meter;
++    NBREC_METER_FOR_EACH (nb_meter, ctx->ovnnb_idl) {
++        shash_add(meter_groups, nb_meter->name, nb_meter);
++    }
++}
++
++static void
+ ovnnb_db_run(struct northd_context *ctx,
+              struct ovsdb_idl_index *sbrec_chassis_by_name,
+              struct ovsdb_idl_loop *sb_loop,
+@@ -8715,6 +8734,7 @@ ovnnb_db_run(struct northd_context *ctx,
+     struct hmap port_groups;
+     struct hmap mcast_groups;
+     struct hmap igmp_groups;
++    struct shash meter_groups = SHASH_INITIALIZER(&meter_groups);
+ 
+     build_datapaths(ctx, datapaths, lr_list);
+     build_ports(ctx, sbrec_chassis_by_name, datapaths, ports);
+@@ -8723,8 +8743,9 @@ ovnnb_db_run(struct northd_context *ctx,
+     build_lrouter_groups(ports, lr_list);
+     build_ip_mcast(ctx, datapaths);
+     build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups);
++    build_meter_groups(ctx, &meter_groups);
+     build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
+-                 &igmp_groups);
++                 &igmp_groups, &meter_groups);
+ 
+     sync_address_sets(ctx);
+     sync_port_groups(ctx);
+@@ -8745,6 +8766,12 @@ ovnnb_db_run(struct northd_context *ctx,
+     hmap_destroy(&mcast_groups);
+     hmap_destroy(&port_groups);
+ 
++    struct shash_node *node, *next;
++    SHASH_FOR_EACH_SAFE (node, next, &meter_groups) {
++        shash_delete(&meter_groups, node);
++    }
++    shash_destroy(&meter_groups);
++
+     /* Sync ipsec configuration.
+      * Copy nb_cfg from northbound to southbound database.
+      * Also set up to update sb_cfg once our southbound transaction commits. */
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -117,6 +117,14 @@
+         <ref table="Controller_Event"/> table.
+         The intention is for a CMS to see the events and take some sort of
+         action. Please see the <ref table="Controller_Event"/> table in SBDB.
++        It is possible to associate a meter to each controller event type
++        in order to not overload the pinctrl thread under heavy load.
++        Each event type relies on a meter with a defined name:
++
++        <ul>
++          <li>empty_lb_backends: event-elb</li>
++        </ul>
++
+       </column>
+     </group>
+ 
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14719,6 +14719,7 @@ ovn-nbctl --wait=hv set NB_Global . opti
+ ovn-nbctl lb-add lb0 192.168.1.100:80 ""
+ ovn-nbctl ls-lb-add sw0 lb0
+ uuid_lb=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0)
++ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10
+ 
+ OVN_POPULATE_ARP
+ ovn-nbctl --timeout=3 --wait=hv sync
diff --git a/SOURCES/0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch b/SOURCES/0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch
new file mode 100644
index 0000000..f8e6413
--- /dev/null
+++ b/SOURCES/0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch
@@ -0,0 +1,299 @@
+From 84d337ec6c4a15154de2e8dd7b53d3626814d7a9 Mon Sep 17 00:00:00 2001
+Message-Id: <84d337ec6c4a15154de2e8dd7b53d3626814d7a9.1569932887.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com>
+References: <9c04a4b62484895dc301875ad9da7c77c17a99c7.1569932887.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 24 Sep 2019 18:39:56 +0200
+Subject: [PATCH ovn 3/3] northd: introduce logical flow for localnet egress
+ shaping
+
+Add set_queue() action for qos capable localnet port in
+S_SWITCH_OUT_PORT_SEC_L2 stage of logical swith pipeline
+Introduce build_lswitch_{input,outpur}_port_sec and refactor
+lswitch_port_security code in order to remove duplicated code
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+---
+ ovn/northd/ovn-northd.8.xml |   7 +-
+ ovn/northd/ovn-northd.c     | 231 ++++++++++++++++++++++++----------------
+ 2 files changed, 143 insertions(+), 95 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1120,10 +1120,15 @@ output;
+       <code>eth.dst</code> are always accepted instead of being subject to the
+       port security rules; this is implemented through a priority-100 flow that
+       matches on <code>eth.mcast</code> with action <code>output;</code>.
+-      Finally, to ensure that even broadcast and multicast packets are not
++      Moreover, to ensure that even broadcast and multicast packets are not
+       delivered to disabled logical ports, a priority-150 flow for each
+       disabled logical <code>outport</code> overrides the priority-100 flow
+       with a <code>drop;</code> action.
++      Finally if egress qos has been enabled on a localnet port, the outgoing
++      queue id is set through <code>set_queue</code> action. Please remember to
++      mark the corresponding physical interface with
++      <code>ovn-egress-iface</code> set to true in <ref column="external_ids"
++      table="Interface" db="Open_vSwitch"/>
+     </p>
+ 
+     <h2>Logical Router Datapaths</h2>
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -3771,6 +3771,140 @@ has_stateful_acl(struct ovn_datapath *od
+ }
+ 
+ static void
++build_lswitch_input_port_sec(struct hmap *ports, struct hmap *datapaths,
++                             struct hmap *lflows)
++{
++    /* Logical switch ingress table 0: Ingress port security - L2
++     *  (priority 50).
++     *  Ingress table 1: Ingress port security - IP (priority 90 and 80)
++     *  Ingress table 2: Ingress port security - ND (priority 90 and 80)
++     */
++    struct ds actions = DS_EMPTY_INITIALIZER;
++    struct ds match = DS_EMPTY_INITIALIZER;
++    struct ovn_port *op;
++
++    HMAP_FOR_EACH (op, key_node, ports) {
++        if (!op->nbsp) {
++            continue;
++        }
++
++        if (!lsp_is_enabled(op->nbsp)) {
++            /* Drop packets from disabled logical ports (since logical flow
++             * tables are default-drop). */
++            continue;
++        }
++
++        if (lsp_is_external(op->nbsp)) {
++            continue;
++        }
++
++        ds_clear(&match);
++        ds_clear(&actions);
++        ds_put_format(&match, "inport == %s", op->json_key);
++        build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs,
++                               &match);
++
++        const char *queue_id = smap_get(&op->sb->options, "qdisc_queue_id");
++        if (queue_id) {
++            ds_put_format(&actions, "set_queue(%s); ", queue_id);
++        }
++        ds_put_cstr(&actions, "next;");
++        ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
++                      ds_cstr(&match), ds_cstr(&actions));
++
++        if (op->nbsp->n_port_security) {
++            build_port_security_ip(P_IN, op, lflows);
++            build_port_security_nd(op, lflows);
++        }
++    }
++
++    /* Ingress table 1 and 2: Port security - IP and ND, by default
++     * goto next. (priority 0)
++     */
++    struct ovn_datapath *od;
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++        if (!od->nbs) {
++            continue;
++        }
++
++        ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;");
++        ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;");
++    }
++
++    ds_destroy(&match);
++    ds_destroy(&actions);
++}
++
++static void
++build_lswitch_output_port_sec(struct hmap *ports, struct hmap *datapaths,
++                              struct hmap *lflows)
++{
++    struct ds actions = DS_EMPTY_INITIALIZER;
++    struct ds match = DS_EMPTY_INITIALIZER;
++    struct ovn_port *op;
++
++    /* Egress table 8: Egress port security - IP (priorities 90 and 80)
++     * if port security enabled.
++     *
++     * Egress table 9: Egress port security - L2 (priorities 50 and 150).
++     *
++     * Priority 50 rules implement port security for enabled logical port.
++     *
++     * Priority 150 rules drop packets to disabled logical ports, so that
++     * they don't even receive multicast or broadcast packets.
++     */
++    HMAP_FOR_EACH (op, key_node, ports) {
++        if (!op->nbsp || lsp_is_external(op->nbsp)) {
++            continue;
++        }
++
++        ds_clear(&actions);
++        ds_clear(&match);
++
++        ds_put_format(&match, "outport == %s", op->json_key);
++        if (lsp_is_enabled(op->nbsp)) {
++            build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs,
++                                   &match);
++
++            if (!strcmp(op->nbsp->type, "localnet")) {
++                const char *queue_id = smap_get(&op->sb->options,
++                                                "qdisc_queue_id");
++                if (queue_id) {
++                    ds_put_format(&actions, "set_queue(%s); ", queue_id);
++                }
++            }
++            ds_put_cstr(&actions, "output;");
++            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
++                              ds_cstr(&match), ds_cstr(&actions));
++        } else {
++            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
++                          ds_cstr(&match), "drop;");
++        }
++
++        if (op->nbsp->n_port_security) {
++            build_port_security_ip(P_OUT, op, lflows);
++        }
++    }
++
++    /* Egress tables 8: Egress port security - IP (priority 0)
++     * Egress table 9: Egress port security L2 - multicast/broadcast
++     *                 (priority 100). */
++    struct ovn_datapath *od;
++    HMAP_FOR_EACH (od, key_node, datapaths) {
++        if (!od->nbs) {
++            continue;
++        }
++
++        ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;");
++        ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast",
++                      "output;");
++    }
++
++    ds_destroy(&match);
++    ds_destroy(&actions);
++}
++
++static void
+ build_pre_acls(struct ovn_datapath *od, struct hmap *lflows)
+ {
+     bool has_stateful = has_stateful_acl(od);
+@@ -4841,61 +4975,12 @@ build_lswitch_flows(struct hmap *datapat
+          * to the next table if packet source is acceptable. */
+     }
+ 
+-    /* Logical switch ingress table 0: Ingress port security - L2
+-     *  (priority 50).
+-     *  Ingress table 1: Ingress port security - IP (priority 90 and 80)
+-     *  Ingress table 2: Ingress port security - ND (priority 90 and 80)
+-     */
+-    struct ovn_port *op;
+-    HMAP_FOR_EACH (op, key_node, ports) {
+-        if (!op->nbsp) {
+-            continue;
+-        }
+-
+-        if (!lsp_is_enabled(op->nbsp)) {
+-            /* Drop packets from disabled logical ports (since logical flow
+-             * tables are default-drop). */
+-            continue;
+-        }
+-
+-        if (lsp_is_external(op->nbsp)) {
+-            continue;
+-        }
+-
+-        ds_clear(&match);
+-        ds_clear(&actions);
+-        ds_put_format(&match, "inport == %s", op->json_key);
+-        build_port_security_l2("eth.src", op->ps_addrs, op->n_ps_addrs,
+-                               &match);
+-
+-        const char *queue_id = smap_get(&op->sb->options, "qdisc_queue_id");
+-        if (queue_id) {
+-            ds_put_format(&actions, "set_queue(%s); ", queue_id);
+-        }
+-        ds_put_cstr(&actions, "next;");
+-        ovn_lflow_add(lflows, op->od, S_SWITCH_IN_PORT_SEC_L2, 50,
+-                      ds_cstr(&match), ds_cstr(&actions));
+-
+-        if (op->nbsp->n_port_security) {
+-            build_port_security_ip(P_IN, op, lflows);
+-            build_port_security_nd(op, lflows);
+-        }
+-    }
+-
+-    /* Ingress table 1 and 2: Port security - IP and ND, by default goto next.
+-     * (priority 0)*/
+-    HMAP_FOR_EACH (od, key_node, datapaths) {
+-        if (!od->nbs) {
+-            continue;
+-        }
+-
+-        ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_ND, 0, "1", "next;");
+-        ovn_lflow_add(lflows, od, S_SWITCH_IN_PORT_SEC_IP, 0, "1", "next;");
+-    }
++    build_lswitch_input_port_sec(ports, datapaths, lflows);
+ 
+     /* Ingress table 11: ARP/ND responder, skip requests coming from localnet
+      * and vtep ports. (priority 100); see ovn-northd.8.xml for the
+      * rationale. */
++    struct ovn_port *op;
+     HMAP_FOR_EACH (op, key_node, ports) {
+         if (!op->nbsp) {
+             continue;
+@@ -5501,49 +5586,7 @@ build_lswitch_flows(struct hmap *datapat
+         }
+     }
+ 
+-    /* Egress tables 8: Egress port security - IP (priority 0)
+-     * Egress table 9: Egress port security L2 - multicast/broadcast
+-     *                 (priority 100). */
+-    HMAP_FOR_EACH (od, key_node, datapaths) {
+-        if (!od->nbs) {
+-            continue;
+-        }
+-
+-        ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_IP, 0, "1", "next;");
+-        ovn_lflow_add(lflows, od, S_SWITCH_OUT_PORT_SEC_L2, 100, "eth.mcast",
+-                      "output;");
+-    }
+-
+-    /* Egress table 8: Egress port security - IP (priorities 90 and 80)
+-     * if port security enabled.
+-     *
+-     * Egress table 9: Egress port security - L2 (priorities 50 and 150).
+-     *
+-     * Priority 50 rules implement port security for enabled logical port.
+-     *
+-     * Priority 150 rules drop packets to disabled logical ports, so that they
+-     * don't even receive multicast or broadcast packets. */
+-    HMAP_FOR_EACH (op, key_node, ports) {
+-        if (!op->nbsp || lsp_is_external(op->nbsp)) {
+-            continue;
+-        }
+-
+-        ds_clear(&match);
+-        ds_put_format(&match, "outport == %s", op->json_key);
+-        if (lsp_is_enabled(op->nbsp)) {
+-            build_port_security_l2("eth.dst", op->ps_addrs, op->n_ps_addrs,
+-                                   &match);
+-            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 50,
+-                          ds_cstr(&match), "output;");
+-        } else {
+-            ovn_lflow_add(lflows, op->od, S_SWITCH_OUT_PORT_SEC_L2, 150,
+-                          ds_cstr(&match), "drop;");
+-        }
+-
+-        if (op->nbsp->n_port_security) {
+-            build_port_security_ip(P_OUT, op, lflows);
+-        }
+-    }
++    build_lswitch_output_port_sec(ports, datapaths, lflows);
+ 
+     ds_destroy(&match);
+     ds_destroy(&actions);
diff --git a/SOURCES/0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch b/SOURCES/0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch
new file mode 100644
index 0000000..b9f13b1
--- /dev/null
+++ b/SOURCES/0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch
@@ -0,0 +1,272 @@
+From f4984801e417fc2c9c92d5d34f9621a4a338aae3 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Wed, 27 Nov 2019 14:15:38 +0100
+Subject: [PATCH ovn 3/4] ovn-controller: Add separate I-P engine node for
+ processing ct-zones.
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit 2117ba0a91f36206d0f3665e8680c15f1f6fa0a0)
+
+Change-Id: I4980ad2641e02356c85c259e71a905bc94dcb833
+---
+ ovn/controller/ovn-controller.c | 117 ++++++++++++++++++++++++++--------------
+ 1 file changed, 78 insertions(+), 39 deletions(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index 8c474e9..20e5ce4 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -897,11 +897,6 @@ struct ed_type_runtime_data {
+      * <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
+@@ -909,24 +904,11 @@ 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
+@@ -946,9 +928,6 @@ en_runtime_data_cleanup(struct engine_node *node)
+         free(cur_node);
+     }
+     hmap_destroy(&data->local_datapaths);
+-
+-    simap_destroy(&data->ct_zones);
+-    shash_destroy(&data->pending_ct_zones);
+ }
+ 
+ static void
+@@ -960,9 +939,6 @@ en_runtime_data_run(struct engine_node *node)
+     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) {
+@@ -1056,9 +1032,6 @@ en_runtime_data_run(struct engine_node *node)
+                 ovs_table, local_datapaths,
+                 local_lports, local_lport_ids);
+ 
+-    update_ct_zones(local_lports, local_datapaths, ct_zones,
+-                    ct_zone_bitmap, pending_ct_zones);
+-
+     engine_set_node_state(node, EN_UPDATED);
+ }
+ 
+@@ -1100,6 +1073,55 @@ runtime_data_sb_port_binding_handler(struct engine_node *node)
+     return !changed;
+ }
+ 
++/* Connection tracking zones. */
++struct ed_type_ct_zones {
++    unsigned long bitmap[BITMAP_N_LONGS(MAX_CT_ZONES)];
++    struct shash pending;
++    struct simap current;
++};
++
++static void
++en_ct_zones_init(struct engine_node *node)
++{
++    struct ed_type_ct_zones *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));
++
++    shash_init(&data->pending);
++    simap_init(&data->current);
++
++    memset(data->bitmap, 0, sizeof data->bitmap);
++    bitmap_set1(data->bitmap, 0); /* Zone 0 is reserved. */
++    restore_ct_zones(bridge_table, ovs_table, &data->current, data->bitmap);
++}
++
++static void
++en_ct_zones_cleanup(struct engine_node *node)
++{
++    struct ed_type_ct_zones *data = node->data;
++
++    simap_destroy(&data->current);
++    shash_destroy(&data->pending);
++}
++
++static void
++en_ct_zones_run(struct engine_node *node)
++{
++    struct ed_type_ct_zones *data = node->data;
++    struct ed_type_runtime_data *rt_data =
++        (struct ed_type_runtime_data *)engine_get_input(
++            "runtime_data", node)->data;
++
++    update_ct_zones(&rt_data->local_lports, &rt_data->local_datapaths,
++                    &data->current, data->bitmap, &data->pending);
++
++    engine_set_node_state(node, EN_UPDATED);
++}
++
+ struct ed_type_mff_ovn_geneve {
+     enum mf_field_id mff_ovn_geneve;
+ };
+@@ -1177,7 +1199,11 @@ en_flow_output_run(struct engine_node *node)
+     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_ct_zones *ct_zones_data =
++        (struct ed_type_ct_zones *)engine_get_input(
++            "ct_zones", node)->data;
++    struct simap *ct_zones = &ct_zones_data->current;
+ 
+     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+         (struct ed_type_mff_ovn_geneve *)engine_get_input(
+@@ -1407,7 +1433,11 @@ flow_output_sb_port_binding_handler(struct engine_node *node)
+                 "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_ct_zones *ct_zones_data =
++        (struct ed_type_ct_zones *)engine_get_input(
++            "ct_zones", node)->data;
++    struct simap *ct_zones = &ct_zones_data->current;
+ 
+     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+         (struct ed_type_mff_ovn_geneve *)engine_get_input(
+@@ -1510,7 +1540,11 @@ flow_output_sb_multicast_group_handler(struct engine_node *node)
+         (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_ct_zones *ct_zones_data =
++        (struct ed_type_ct_zones *)engine_get_input(
++            "ct_zones", node)->data;
++    struct simap *ct_zones = &ct_zones_data->current;
+ 
+     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+         (struct ed_type_mff_ovn_geneve *)engine_get_input(
+@@ -1817,6 +1851,7 @@ main(int argc, char *argv[])
+     stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
+ 
+     /* Define inc-proc-engine nodes. */
++    struct ed_type_ct_zones ed_ct_zones;
+     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;
+@@ -1824,6 +1859,7 @@ main(int argc, char *argv[])
+     struct ed_type_addr_sets ed_addr_sets;
+     struct ed_type_port_groups ed_port_groups;
+ 
++    ENGINE_NODE(ct_zones, "ct_zones");
+     ENGINE_NODE(runtime_data, "runtime_data");
+     ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
+     ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
+@@ -1863,6 +1899,7 @@ main(int argc, char *argv[])
+     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_ct_zones, NULL);
+     engine_add_input(&en_flow_output, &en_mff_ovn_geneve, NULL);
+ 
+     engine_add_input(&en_flow_output, &en_ovs_open_vswitch, NULL);
+@@ -1882,6 +1919,10 @@ main(int argc, char *argv[])
+     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_ct_zones, &en_ovs_open_vswitch, NULL);
++    engine_add_input(&en_ct_zones, &en_ovs_bridge, NULL);
++    engine_add_input(&en_ct_zones, &en_runtime_data, NULL);
++
+     engine_add_input(&en_runtime_data, &en_ofctrl_is_connected, NULL);
+ 
+     engine_add_input(&en_runtime_data, &en_ovs_open_vswitch, NULL);
+@@ -1907,7 +1948,7 @@ main(int argc, char *argv[])
+                              meter_table_list, &ed_flow_output.meter_table);
+ 
+     unixctl_command_register("ct-zone-list", "", 0, 0,
+-                             ct_zone_list, &ed_runtime_data.ct_zones);
++                             ct_zone_list, &ed_ct_zones.current);
+ 
+     struct pending_pkt pending_pkt = { .conn = NULL };
+     unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
+@@ -1983,7 +2024,7 @@ main(int argc, char *argv[])
+             }
+ 
+             if (br_int) {
+-                ofctrl_run(br_int, &ed_runtime_data.pending_ct_zones);
++                ofctrl_run(br_int, &ed_ct_zones.pending);
+ 
+                 if (chassis) {
+                     patch_run(ovs_idl_txn,
+@@ -2024,8 +2065,7 @@ main(int argc, char *argv[])
+                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
+                                    time_msec());
+                     if (ovs_idl_txn) {
+-                        commit_ct_zones(br_int,
+-                                        &ed_runtime_data.pending_ct_zones);
++                        commit_ct_zones(br_int, &ed_ct_zones.pending);
+                         bfd_run(ovsrec_interface_table_get(ovs_idl_loop.idl),
+                                 br_int, chassis,
+                                 sbrec_ha_chassis_group_table_get(
+@@ -2033,7 +2073,7 @@ main(int argc, char *argv[])
+                                 sbrec_sb_global_table_get(ovnsb_idl_loop.idl));
+                     }
+                     ofctrl_put(&ed_flow_output.flow_table,
+-                               &ed_runtime_data.pending_ct_zones,
++                               &ed_ct_zones.pending,
+                                sbrec_meter_table_get(ovnsb_idl_loop.idl),
+                                get_nb_cfg(sbrec_sb_global_table_get(
+                                               ovnsb_idl_loop.idl)),
+@@ -2134,11 +2174,10 @@ main(int argc, char *argv[])
+ 
+         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) {
++            SHASH_FOR_EACH_SAFE (iter, iter_next, &ed_ct_zones.pending) {
+                 struct ct_zone_pending_entry *ctzpe = iter->data;
+                 if (ctzpe->state == CT_ZONE_DB_SENT) {
+-                    shash_delete(&ed_runtime_data.pending_ct_zones, iter);
++                    shash_delete(&ed_ct_zones.pending, iter);
+                     free(ctzpe);
+                 }
+             }
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch b/SOURCES/0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch
new file mode 100644
index 0000000..ea3e611
--- /dev/null
+++ b/SOURCES/0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch
@@ -0,0 +1,42 @@
+From bfed073bd6b2d4923286fe77b4813c330895bbfa Mon Sep 17 00:00:00 2001
+From: Han Zhou <hzhou@ovn.org>
+Date: Thu, 14 May 2020 22:27:39 +0530
+Subject: [PATCH 3/4] ovn-controller.c: Fix memory leak of
+ local_datapath->ports.
+
+Fixes: 89f5048f960c ("ovn-controller: Minimize SB DB port_binding lookups.")
+Acked-by: Numan Siddique <numans@ovn.org>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit dc0e10c068c20c4e59c9c86ecee26baf8ed50e90)
+
+Change-Id: I317fabe5ed0017893267524dc25881c20e764975
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/controller/ovn-controller.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index b80d6c0dc..8d7ea89c4 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -929,6 +929,7 @@ en_runtime_data_cleanup(void *data)
+     HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
+                         &rt_data->local_datapaths) {
+         free(cur_node->peer_ports);
++        free(cur_node->ports);
+         hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node);
+         free(cur_node);
+     }
+@@ -952,6 +953,7 @@ en_runtime_data_run(struct engine_node *node, void *data)
+         struct local_datapath *cur_node, *next_node;
+         HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) {
+             free(cur_node->peer_ports);
++            free(cur_node->ports);
+             hmap_remove(local_datapaths, &cur_node->hmap_node);
+             free(cur_node);
+         }
+-- 
+2.26.2
+
diff --git a/SOURCES/0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch b/SOURCES/0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch
new file mode 100644
index 0000000..c655f80
--- /dev/null
+++ b/SOURCES/0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch
@@ -0,0 +1,209 @@
+From b76dba2ee078b2b2d168b9ada5ddc01b05a9a9d1 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 25 Oct 2019 12:22:04 -0400
+Subject: [PATCH 3/5] ovn-nbctl: Allow IPv6 NAT rules to be added
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/utilities/ovn-nbctl.c | 49 +++++++++++++++++++++++++++++----------
+ tests/ovn-nbctl.at        | 41 +++++++++++++++++++++++++-------
+ 2 files changed, 69 insertions(+), 21 deletions(-)
+
+diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
+index 7a38b2bf7..497d6f231 100644
+--- a/ovn/utilities/ovn-nbctl.c
++++ b/ovn/utilities/ovn-nbctl.c
+@@ -3853,26 +3853,51 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
+ 
+     ovs_be32 ipv4 = 0;
+     unsigned int plen;
++    struct in6_addr ipv6;
++    bool is_v6 = false;
+     if (!ip_parse(external_ip, &ipv4)) {
+-        ctl_error(ctx, "%s: should be an IPv4 address.", external_ip);
+-        return;
++        if (ipv6_parse(external_ip, &ipv6)) {
++            is_v6 = true;
++        } else {
++            ctl_error(ctx, "%s: Not a valid IPv4 or IPv6 address.",
++                      external_ip);
++            return;
++        }
+     }
+ 
+     if (strcmp("snat", nat_type)) {
+-        if (!ip_parse(logical_ip, &ipv4)) {
+-            ctl_error(ctx, "%s: should be an IPv4 address.", logical_ip);
+-            return;
++        if (is_v6) {
++            if (!ipv6_parse(logical_ip, &ipv6)) {
++                ctl_error(ctx, "%s: Not a valid IPv6 address.", logical_ip);
++                return;
++            }
++        } else {
++            if (!ip_parse(logical_ip, &ipv4)) {
++                ctl_error(ctx, "%s: Not a valid IPv4 address.", logical_ip);
++                return;
++            }
+         }
+         new_logical_ip = xstrdup(logical_ip);
+     } else {
+-        error = ip_parse_cidr(logical_ip, &ipv4, &plen);
+-        if (error) {
+-            free(error);
+-            ctl_error(ctx, "%s: should be an IPv4 address or network.",
+-                      logical_ip);
+-            return;
++        if (is_v6) {
++            error = ipv6_parse_cidr(logical_ip, &ipv6, &plen);
++            if (error) {
++                free(error);
++                ctl_error(ctx, "%s: should be an IPv6 address or network.",
++                          logical_ip);
++                return;
++            }
++            new_logical_ip = normalize_ipv6_prefix(ipv6, plen);
++        } else {
++            error = ip_parse_cidr(logical_ip, &ipv4, &plen);
++            if (error) {
++                free(error);
++                ctl_error(ctx, "%s: should be an IPv4 address or network.",
++                          logical_ip);
++                return;
++            }
++            new_logical_ip = normalize_ipv4_prefix(ipv4, plen);
+         }
+-        new_logical_ip = normalize_ipv4_prefix(ipv4, plen);
+     }
+ 
+     const char *logical_port;
+diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
+index e87242c03..30849682c 100644
+--- a/tests/ovn-nbctl.at
++++ b/tests/ovn-nbctl.at
+@@ -407,16 +407,16 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [],
+ [ovn-nbctl: snatt: type must be one of "dnat", "snat" and "dnat_and_snat".
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2a 192.168.1.2], [1], [],
+-[ovn-nbctl: 30.0.0.2a: should be an IPv4 address.
++[ovn-nbctl: 30.0.0.2a: Not a valid IPv4 or IPv6 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0 192.168.1.2], [1], [],
+-[ovn-nbctl: 30.0.0: should be an IPv4 address.
++[ovn-nbctl: 30.0.0: Not a valid IPv4 or IPv6 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2/24 192.168.1.2], [1], [],
+-[ovn-nbctl: 30.0.0.2/24: should be an IPv4 address.
++[ovn-nbctl: 30.0.0.2/24: Not a valid IPv4 or IPv6 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2:80 192.168.1.2], [1], [],
+-[ovn-nbctl: 30.0.0.2:80: should be an IPv4 address.
++[ovn-nbctl: 30.0.0.2:80: Not a valid IPv4 or IPv6 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2a], [1], [],
+ [ovn-nbctl: 192.168.1.2a: should be an IPv4 address or network.
+@@ -431,19 +431,19 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2/a], [1], [],
+ [ovn-nbctl: 192.168.1.2/a: should be an IPv4 address or network.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2a], [1], [],
+-[ovn-nbctl: 192.168.1.2a: should be an IPv4 address.
++[ovn-nbctl: 192.168.1.2a: Not a valid IPv4 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1], [1], [],
+-[ovn-nbctl: 192.168.1: should be an IPv4 address.
++[ovn-nbctl: 192.168.1: Not a valid IPv4 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2:80], [1], [],
+-[ovn-nbctl: 192.168.1.2:80: should be an IPv4 address.
++[ovn-nbctl: 192.168.1.2:80: Not a valid IPv4 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2/24], [1], [],
+-[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address.
++[ovn-nbctl: 192.168.1.2/24: Not a valid IPv4 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2/24], [1], [],
+-[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address.
++[ovn-nbctl: 192.168.1.2/24: Not a valid IPv4 address.
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0], [1], [],
+ [ovn-nbctl: lr-nat-add with logical_port must also specify external_mac.
+@@ -465,15 +465,23 @@ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2 lp0 00:00:
+ 
+ dnl Add snat and dnat
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24])
++AT_CHECK([ovn-nbctl lr-nat-add lr0 snat fd01::1 fd11::/64])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2])
++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat fd01::1 fd11::2])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2])
++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat fd01::1 fd11::2])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3 lp0 00:00:00:01:02:03])
++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat fd01::2 fd11::3 lp0 00:00:00:01:02:03])
+ AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         LOGICAL_PORT
+ dnat             30.0.0.1           192.168.1.2
++dnat             fd01::1            fd11::2
+ dnat_and_snat    30.0.0.1           192.168.1.2
+ dnat_and_snat    30.0.0.2           192.168.1.3           00:00:00:01:02:03    lp0
++dnat_and_snat    fd01::1            fd11::2
++dnat_and_snat    fd01::2            fd11::3               00:00:00:01:02:03    lp0
+ snat             30.0.0.1           192.168.1.0/24
++snat             fd01::1            fd11::/64
+ ])
+ AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [],
+ [ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists
+@@ -503,17 +511,26 @@ AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.
+ AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         LOGICAL_PORT
+ dnat             30.0.0.1           192.168.1.2
++dnat             fd01::1            fd11::2
+ dnat_and_snat    30.0.0.1           192.168.1.2
+ dnat_and_snat    30.0.0.2           192.168.1.3           00:00:00:04:05:06    lp0
++dnat_and_snat    fd01::1            fd11::2
++dnat_and_snat    fd01::2            fd11::3               00:00:00:01:02:03    lp0
+ snat             30.0.0.1           192.168.1.0/24
++snat             fd01::1            fd11::/64
+ ])
+ AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.3])
++AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat fd01::2 fd11::3])
+ AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         LOGICAL_PORT
+ dnat             30.0.0.1           192.168.1.2
++dnat             fd01::1            fd11::2
+ dnat_and_snat    30.0.0.1           192.168.1.2
+ dnat_and_snat    30.0.0.2           192.168.1.3
++dnat_and_snat    fd01::1            fd11::2
++dnat_and_snat    fd01::2            fd11::3
+ snat             30.0.0.1           192.168.1.0/24
++snat             fd01::1            fd11::/64
+ ])
+ 
+ dnl Deletes the NATs
+@@ -529,18 +546,24 @@ AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 192.168.10.0/24], [1], [],
+ AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 192.168.10.0/24])
+ 
+ AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.1])
++AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat fd01::1])
+ AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         LOGICAL_PORT
+ dnat             30.0.0.1           192.168.1.2
++dnat             fd01::1            fd11::2
+ dnat_and_snat    30.0.0.2           192.168.1.3
++dnat_and_snat    fd01::2            fd11::3
+ snat             30.0.0.1           192.168.1.0/24
++snat             fd01::1            fd11::/64
+ ])
+ 
+ AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat])
+ AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         LOGICAL_PORT
+ dnat_and_snat    30.0.0.2           192.168.1.3
++dnat_and_snat    fd01::2            fd11::3
+ snat             30.0.0.1           192.168.1.0/24
++snat             fd01::1            fd11::/64
+ ])
+ 
+ AT_CHECK([ovn-nbctl lr-nat-del lr0])
+-- 
+2.23.0
+
diff --git a/SOURCES/0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch b/SOURCES/0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch
new file mode 100644
index 0000000..efe7a14
--- /dev/null
+++ b/SOURCES/0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch
@@ -0,0 +1,42 @@
+From a152a434074ee38e38be6593502ff0058e67c2f4 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Thu, 14 Nov 2019 13:43:49 +0100
+Subject: [PATCH ovn 3/3] ovn-northd: Avoid empty address list when limiting
+ ARP/ND broadcast.
+
+Reported-by: Numan Siddique <numans@ovn.org>
+Fixes: 32f5ebb06226 ("ovn-northd: Limit ARP/ND broadcast domain whenever possible.")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream commit 998617cd85bfa1f03f2e734e479fd87f3e833797)
+---
+ ovn/northd/ovn-northd.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 03eb7a4..0e6f8f6 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5331,10 +5331,14 @@ build_lswitch_rport_arp_req_flows(struct ovn_port *op,
+         }
+     }
+ 
+-    build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
+-                                            sw_od, 75, lflows);
+-    build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
+-                                            sw_od, 75, lflows);
++    if (!sset_is_empty(&all_ips_v4)) {
++        build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v4, AF_INET, sw_op,
++                                                sw_od, 75, lflows);
++    }
++    if (!sset_is_empty(&all_ips_v6)) {
++        build_lswitch_rport_arp_req_flow_for_ip(&all_ips_v6, AF_INET6, sw_op,
++                                                sw_od, 75, lflows);
++    }
+ 
+     sset_destroy(&all_ips_v4);
+     sset_destroy(&all_ips_v6);
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch b/SOURCES/0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch
new file mode 100644
index 0000000..cf61e84
--- /dev/null
+++ b/SOURCES/0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch
@@ -0,0 +1,848 @@
+From e392ac9d89e4a5f2d22a6a17a375cff41200d78c Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Wed, 28 Aug 2019 01:55:30 +0000
+Subject: [PATCH 04/12] OVN: Vlan backed DVR N-S, redirect packet via localnet
+ port
+
+Background:
+With 795d7f24ce0e ("OVN: Enable E-W Traffic, Vlan backed DVR"), we have added
+support for E-W workflow for vlan backed DVRs.
+
+This series enables N-S workflow for vlan backed DVRs.
+
+Key difference between E-W and N-S traffic flow is that
+N-S flow requires a gateway chassis. A gateway chassis
+will be respondible for following:
+a. Doing Network Address Translation (NAT).
+b. Becoming entry and exit point for North->South
+   and South->North traffic respectively.
+
+OVN by default always uses overlay encapsulation to redirect
+the packet to gateway chassis. This series will enable
+the redirection to gateway chassis in the absence of encapsulation.
+
+This patch:
+Achieves the bridged backed redirection by doing following:
+
+Sender Side:
+------------
+a. For a remote port of type "chassisredirect" and if it
+   has redirect type as "bridged", then do not add tunnel
+   based redirection flow in table=32.
+
+b. In table=33, add a flow with priority=100, that would do following:
+   i. Change the metadata to that of gateway logical switch
+      (i.e logical switch attached to gateway logical router port).
+  ii. Change REG15 to point to localnet port of gateway logical switch.
+ iii. send to packet to table=65.
+
+c. In Table=65, packet will hit the existing priority=150 flow to send
+   the packet to physical bridge, while attaching vlan header (if configured)
+   and changing source mac to chassis mac.
+
+Receiver Side:
+--------------
+a. No changes needed
+
+OVERALL PACKET FLOW:
+Sender Side:
+-----------
+a. logical flow in lr_in_gw_redirect stage will ensure that
+   outport of the packet is chassisredirect port.
+   For example:
+   table=12(lr_in_gw_redirect  ), priority=50   , match=(outport == "router-to-underlay"), action=(outport = "cr-router-to-underlay"; next;)
+
+b. After ingress pipeline, packet will enter the table=32, followed by table=33
+
+c. Table=33, will send the packet to table=65.
+
+d. Table=65, will send the packet to uplink bridge
+   with destination mac of chassisredirect port and vlan
+   id of peer logical switch.
+
+Receiver Side:
+-------------
+a. Packet is received by the pipeline of peer logical switch.
+b. Since destination mac is that of router port, hence packet will
+   enter the logical router pipeline.
+c. Now, packet will go through regular logical router pipeline
+   (both ingress and egress).
+
+One caveat with the approach is that ttl will be decremented twice,
+since the packets are going through logical router ingress pipeline
+twice (once on sender chassis and again on gateway chassis).
+
+No changes needed for the reverse path.
+
+Change-Id: I3c36cbd02ff53ed3eaa8ee474a31c7d9f284362d
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/controller/physical.c  | 255 +++++++++++++++++++++----------
+ ovn/lib/ovn-util.c         |  33 ++++
+ ovn/lib/ovn-util.h         |   5 +
+ ovn/ovn-architecture.7.xml |  72 +++++++++
+ tests/ovn.at               | 304 +++++++++++++++++++++++++++++++++++++
+ 5 files changed, 586 insertions(+), 83 deletions(-)
+
+diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
+index 425110d0b..bf1d0d716 100644
+--- a/ovn/controller/physical.c
++++ b/ovn/controller/physical.c
+@@ -227,6 +227,164 @@ get_zone_ids(const struct sbrec_port_binding *binding,
+     return zone_ids;
+ }
+ 
++static void
++put_remote_port_redirect_bridged(const struct
++                                 sbrec_port_binding *binding,
++                                 const struct hmap *local_datapaths,
++                                 struct local_datapath *ld,
++                                 struct match *match,
++                                 struct ofpbuf *ofpacts_p,
++                                 struct ovn_desired_flow_table *flow_table)
++{
++        if (strcmp(binding->type, "chassisredirect")) {
++            /* bridged based redirect is only supported for chassisredirect
++             * type remote ports. */
++            return;
++        }
++
++        struct eth_addr binding_mac;
++        bool  is_valid_mac = extract_sbrec_binding_first_mac(binding,
++                                                             &binding_mac);
++        if (!is_valid_mac) {
++            return;
++        }
++
++        uint32_t ls_dp_key = 0;
++        for (int i = 0; i < ld->n_peer_ports; i++) {
++            const struct sbrec_port_binding *sport_binding = ld->peer_ports[i];
++            const char *sport_peer_name = smap_get(&sport_binding->options,
++                                                   "peer");
++            const char *distributed_port = smap_get(&binding->options,
++                                                    "distributed-port");
++
++            if (!strcmp(sport_peer_name, distributed_port)) {
++                ls_dp_key = sport_binding->datapath->tunnel_key;
++                break;
++            }
++        }
++
++        if (!ls_dp_key) {
++            return;
++        }
++
++        union mf_value value;
++        struct ofpact_mac *src_mac;
++        const struct sbrec_port_binding *ls_localnet_port;
++
++        ls_localnet_port = get_localnet_port(local_datapaths, ls_dp_key);
++
++        src_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
++        src_mac->mac = binding_mac;
++
++        value.be64 = htonll(ls_dp_key);
++
++        ofpact_put_set_field(ofpacts_p, mf_from_id(MFF_METADATA),
++                             &value, NULL);
++
++        value.be32 = htonl(ls_localnet_port->tunnel_key);
++        ofpact_put_set_field(ofpacts_p, mf_from_id(MFF_REG15),
++                             &value, NULL);
++
++        put_resubmit(OFTABLE_LOG_TO_PHY, ofpacts_p);
++        ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
++                        match, ofpacts_p, &binding->header_.uuid);
++
++}
++
++static void
++put_remote_port_redirect_overlay(const struct
++                                 sbrec_port_binding *binding,
++                                 bool is_ha_remote,
++                                 struct ha_chassis_ordered *ha_ch_ordered,
++                                 enum mf_field_id mff_ovn_geneve,
++                                 const struct chassis_tunnel *tun,
++                                 uint32_t port_key,
++                                 struct match *match,
++                                 struct ofpbuf *ofpacts_p,
++                                 struct ovn_desired_flow_table *flow_table)
++{
++    if (!is_ha_remote) {
++        /* Setup encapsulation */
++        const struct chassis_tunnel *rem_tun =
++            get_port_binding_tun(binding);
++        if (!rem_tun) {
++            return;
++        }
++        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);
++                    return;
++                }
++            }
++        }
++        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);
++            return;
++        }
++
++        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);
++}
++
+ static void
+ put_replace_router_port_mac_flows(struct ovsdb_idl_index
+                                   *sbrec_port_binding_by_name,
+@@ -484,7 +642,8 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+ {
+     uint32_t dp_key = binding->datapath->tunnel_key;
+     uint32_t port_key = binding->tunnel_key;
+-    if (!get_local_datapath(local_datapaths, dp_key)) {
++    struct local_datapath *ld;
++    if (!(ld = get_local_datapath(local_datapaths, dp_key))) {
+         return;
+     }
+ 
+@@ -830,6 +989,10 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+         ofctrl_add_flow(flow_table, OFTABLE_LOCAL_OUTPUT, 100, 0,
+                         &match, ofpacts_p, &binding->header_.uuid);
+     } else {
++
++        const char *redirect_type = smap_get(&binding->options,
++                                             "redirect-type");
++
+         /* Remote port connected by tunnel */
+ 
+         /* Table 32, priority 100.
+@@ -846,90 +1009,16 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+         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;
++        if (redirect_type && !strcasecmp(redirect_type, "bridged")) {
++            put_remote_port_redirect_bridged(binding, local_datapaths,
++                                             ld, &match, ofpacts_p,
++                                             flow_table);
+         } 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);
++            put_remote_port_redirect_overlay(binding, is_ha_remote,
++                                             ha_ch_ordered, mff_ovn_geneve,
++                                             tun, port_key, &match, ofpacts_p,
++                                             flow_table);
+         }
+-        ofctrl_add_flow(flow_table, OFTABLE_REMOTE_OUTPUT, 100, 0,
+-                        &match, ofpacts_p, &binding->header_.uuid);
+     }
+ out:
+     if (ha_ch_ordered) {
+diff --git a/ovn/lib/ovn-util.c b/ovn/lib/ovn-util.c
+index de745d73f..91ec3d62e 100644
+--- a/ovn/lib/ovn-util.c
++++ b/ovn/lib/ovn-util.c
+@@ -16,6 +16,7 @@
+ #include "ovn-util.h"
+ #include "dirs.h"
+ #include "openvswitch/vlog.h"
++#include "openvswitch/ofp-parse.h"
+ #include "ovn/lib/ovn-nb-idl.h"
+ #include "ovn/lib/ovn-sb-idl.h"
+ 
+@@ -272,6 +273,38 @@ extract_lrp_networks(const struct nbrec_logical_router_port *lrp,
+     return true;
+ }
+ 
++bool
++extract_sbrec_binding_first_mac(const struct sbrec_port_binding *binding,
++                                struct eth_addr *ea)
++{
++    char *save_ptr = NULL;
++    bool ret = false;
++
++    if (!binding->n_mac) {
++        return ret;
++    }
++
++    char *tokstr = xstrdup(binding->mac[0]);
++
++    for (char *token = strtok_r(tokstr, " ", &save_ptr);
++         token != NULL;
++         token = strtok_r(NULL, " ", &save_ptr)) {
++
++        /* Return the first chassis mac. */
++        char *err_str = str_to_mac(token, ea);
++        if (err_str) {
++            free(err_str);
++            continue;
++        }
++
++        ret = true;
++        break;
++    }
++
++    free(tokstr);
++    return ret;
++}
++
+ void
+ destroy_lport_addresses(struct lport_addresses *laddrs)
+ {
+diff --git a/ovn/lib/ovn-util.h b/ovn/lib/ovn-util.h
+index 6d5e1dfb5..8461db505 100644
+--- a/ovn/lib/ovn-util.h
++++ b/ovn/lib/ovn-util.h
+@@ -21,6 +21,8 @@
+ struct nbrec_logical_router_port;
+ struct sbrec_logical_flow;
+ struct uuid;
++struct eth_addr;
++struct sbrec_port_binding;
+ 
+ struct ipv4_netaddr {
+     ovs_be32 addr;            /* 192.168.10.123 */
+@@ -61,6 +63,9 @@ bool extract_lsp_addresses(const char *address, struct lport_addresses *);
+ bool extract_ip_addresses(const char *address, struct lport_addresses *);
+ bool extract_lrp_networks(const struct nbrec_logical_router_port *,
+                           struct lport_addresses *);
++bool extract_sbrec_binding_first_mac(const struct sbrec_port_binding *binding,
++                                     struct eth_addr *ea);
++
+ void destroy_lport_addresses(struct lport_addresses *);
+ 
+ char *alloc_nat_zone_key(const struct uuid *key, const char *type);
+diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
+index 771edb8d8..fa163c023 100644
+--- a/ovn/ovn-architecture.7.xml
++++ b/ovn/ovn-architecture.7.xml
+@@ -1651,6 +1651,78 @@
+     </li>
+   </ol>
+ 
++  <p>
++    VLAN based redirection
++
++    As an enhancement to <code>reside-on-redirect-chassis</code> we support
++    VLAN based redirection as well. By setting
++    <code>options:redirect-type</code> to <code>vlan</code> to a gateway
++    chassis attached router port, user can enforce that redirected packet
++    should not use tunnel port but rather use localnet port of peer logical
++    switch to go out as vlan packet.
++  </p>
++
++  <p>
++    Following happens for a VLAN based redirection:
++  </p>
++  <ol>
++    <li>
++      On compute chassis, packet passes though logical router's
++      ingress pipeline.
++    </li>
++
++    <li>
++      If logical outport is gateway chassis attached router port
++      then packet is "redirected" to gateway chassis using peer logical
++      switch's localnet port.
++    </li>
++
++    <li>
++      This VLAN backed redirected packet has destination mac
++      as router port mac (the one to which gateway chassis is attached) and
++      vlan id is that of localnet port (peer logical switch of
++      the logical router port).
++    </li>
++
++    <li>
++      On the gateway chassis packet will enter the logical router pipeline
++      again and this time it will passthrough egress pipeline as well.
++    </li>
++
++    <li>
++      Reverse traffic packet flows stays the same.
++    </li>
++  </ol>
++
++  <p>
++    Some guidelines and expections with VLAN based redirection:
++  </p>
++
++  <ol>
++    <li>
++      Since router port mac is destination mac, hence it has to be ensured
++      that physical network learns it on ONLY from the gateway chassis.
++      Which means that <code>ovn-chassis-mac-mappings</code> should be
++      configure on all the compute nodes, so that physical network
++      never learn router port mac from compute nodes.
++    </li>
++
++    <li>
++      Since packet enters logical router ingress pipeline twice
++      (once on compute chassis and again on gateway chassis),
++      hence ttl will be decremented twice.
++    </li>
++
++    <li>
++      Default redirection type continues to be <code>overlay</code>.
++      User can switch the redirect-type between <code>vlan</code>
++      and <code>overlay</code> by changing the value of
++      <code>options:redirect-type</code>
++    </li>
++
++  </ol>
++
++
+   <h2>Life Cycle of a VTEP gateway</h2>
+ 
+   <p>
+diff --git a/tests/ovn.at b/tests/ovn.at
+index b7ee7beed..fe5d7f130 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -16296,3 +16296,307 @@ as hv4 ovs-appctl fdb/show br-phys
+ OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
+ 
+ AT_CLEANUP
++
++AT_SETUP([ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S Ping])
++ovn_start
++
++# In this test cases we create 3 switches, all connected to same
++# physical network (through br-phys on each HV). LS1 and LS2 have
++# 1 VIF each. Each HV has 1 VIF port. The first digit
++# of VIF port name indicates the hypervisor it is bound to, e.g.
++# lp23 means VIF 3 on hv2.
++#
++# All the switches are connected to a logical router "router".
++#
++# Each switch's VLAN tag and their logical switch ports are:
++#   - ls1:
++#       - tagged with VLAN 101
++#       - ports: lp11
++#   - ls2:
++#       - tagged with VLAN 201
++#       - ports: lp22
++#   - ls-underlay:
++#       - tagged with VLAN 1000
++# Note: a localnet port is created for each switch to connect to
++# physical network.
++
++for i in 1 2; do
++    ls_name=ls$i
++    ovn-nbctl ls-add $ls_name
++    ln_port_name=ln$i
++    if test $i -eq 1; then
++        ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
++    elif test $i -eq 2; then
++        ovn-nbctl lsp-add $ls_name $ln_port_name "" 201
++    fi
++    ovn-nbctl lsp-set-addresses $ln_port_name unknown
++    ovn-nbctl lsp-set-type $ln_port_name localnet
++    ovn-nbctl lsp-set-options $ln_port_name network_name=phys
++done
++
++# lsp_to_ls LSP
++#
++# Prints the name of the logical switch that contains LSP.
++lsp_to_ls () {
++    case $1 in dnl (
++        lp?[[11]]) echo ls1 ;; dnl (
++        lp?[[12]]) echo ls2 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_hv () {
++    case $1 in dnl (
++        vif[[1]]?) echo hv1 ;; dnl (
++        vif[[2]]?) echo hv2 ;; dnl (
++        vif?[[north]]?) echo hv4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++ip_to_hex() {
++       printf "%02x%02x%02x%02x" "$@"
++}
++
++net_add n1
++for i in 1 2; do
++    sim_add hv$i
++    as hv$i
++    ovs-vsctl add-br br-phys
++    ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++    ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
++    ovn_attach n1 br-phys 192.168.0.$i
++
++    ovs-vsctl add-port br-int vif$i$i -- \
++        set Interface vif$i$i external-ids:iface-id=lp$i$i \
++                              options:tx_pcap=hv$i/vif$i$i-tx.pcap \
++                              options:rxq_pcap=hv$i/vif$i$i-rx.pcap \
++                              ofport-request=$i$i
++
++    lsp_name=lp$i$i
++    ls_name=$(lsp_to_ls $lsp_name)
++
++    ovn-nbctl lsp-add $ls_name $lsp_name
++    ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i"
++    ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i
++
++    OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
++
++done
++
++ovn-nbctl ls-add ls-underlay
++ovn-nbctl lsp-add ls-underlay ln3 "" 1000
++ovn-nbctl lsp-set-addresses ln3 unknown
++ovn-nbctl lsp-set-type ln3 localnet
++ovn-nbctl lsp-set-options ln3 network_name=phys
++
++ovn-nbctl ls-add ls-north
++ovn-nbctl lsp-add ls-north ln4 "" 1000
++ovn-nbctl lsp-set-addresses ln4 unknown
++ovn-nbctl lsp-set-type ln4 localnet
++ovn-nbctl lsp-set-options ln4 network_name=phys
++
++# Add a VM on ls-north
++ovn-nbctl lsp-add ls-north lp-north
++ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10"
++ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11
++
++# Add 3rd hypervisor
++sim_add hv3
++as hv3 ovs-vsctl add-br br-phys
++as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33"
++as hv3 ovn_attach n1 br-phys 192.168.0.3
++
++# Add 4th hypervisor
++sim_add hv4
++as hv4 ovs-vsctl add-br br-phys
++as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44"
++as hv4 ovn_attach n1 br-phys 192.168.0.4
++
++as hv4 ovs-vsctl add-port br-int vif-north -- \
++        set Interface vif-north external-ids:iface-id=lp-north \
++                              options:tx_pcap=hv4/vif-north-tx.pcap \
++                              options:rxq_pcap=hv4/vif-north-rx.pcap \
++                              ofport-request=44
++
++ovn-nbctl lr-add router
++ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24
++ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24
++ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24
++
++ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \
++          options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router
++ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \
++          options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router
++ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \
++                              underlay-to-router type=router \
++                              options:router-port=router-to-underlay \
++                              -- lsp-set-addresses underlay-to-router router
++
++ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3
++ovn-nbctl lrp-set-redirect-type router-to-underlay bridged
++
++ovn-nbctl --wait=sb sync
++
++
++OVN_POPULATE_ARP
++
++# lsp_to_ls LSP
++#
++# Prints the name of the logical switch that contains LSP.
++lsp_to_ls () {
++    case $1 in dnl (
++        lp?[[11]]) echo ls1 ;; dnl (
++        lp?[[12]]) echo ls2 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_ls () {
++    case $1 in dnl (
++        vif?[[11]]) echo ls1 ;; dnl (
++        vif?[[12]]) echo ls2 ;; dnl (
++        vif-north) echo ls-north ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++hv_to_num () {
++    case $1 in dnl (
++        hv1) echo 1 ;; dnl (
++        hv2) echo 2 ;; dnl (
++        hv3) echo 3 ;; dnl (
++        hv4) echo 4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_num () {
++    case $1 in dnl (
++        vif22) echo 22 ;; dnl (
++        vif21) echo 21 ;; dnl (
++        vif11) echo 11 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_hv () {
++    case $1 in dnl (
++        vif[[1]]?) echo hv1 ;; dnl (
++        vif[[2]]?) echo hv2 ;; dnl (
++        vif-north) echo hv4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_lrp () {
++    echo router-to-`vif_to_ls $1`
++}
++
++ip_to_hex() {
++       printf "%02x%02x%02x%02x" "$@"
++}
++
++
++test_ip() {
++        # This packet has bad checksums but logical L3 routing doesn't check.
++        local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 outport=$6
++        local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
++        shift; shift; shift; shift; shift
++        hv=`vif_to_hv $inport`
++        as $hv ovs-appctl netdev-dummy/receive $inport $packet
++        in_ls=`vif_to_ls $inport`
++        for outport; do
++            out_ls=`vif_to_ls $outport`
++            if test $in_ls = $out_ls; then
++                # Ports on the same logical switch receive exactly the same packet.
++                echo $packet
++            else
++                # Routing decrements TTL and updates source and dest MAC
++                # (and checksum).
++                out_lrp=`vif_to_lrp $outport`
++                # For North-South, packet will come via gateway chassis, i.e hv3
++                if test $inport = vif-north; then
++                    echo f00000000011aabbccddee3308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected
++                fi
++                if test $outport = vif-north; then
++                    echo f0f00000001100000101020708004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 >> $outport.expected
++                fi
++            fi >> $outport.expected
++        done
++}
++
++# Dump a bunch of info helpful for debugging if there's a failure.
++
++echo "------ OVN dump ------"
++ovn-nbctl show
++ovn-sbctl show
++ovn-sbctl list port_binding
++ovn-sbctl list mac_binding
++
++echo "------ hv1 dump ------"
++as hv1 ovs-vsctl show
++as hv1 ovs-vsctl list Open_Vswitch
++
++echo "------ hv2 dump ------"
++as hv2 ovs-vsctl show
++as hv2 ovs-vsctl list Open_Vswitch
++
++echo "------ hv3 dump ------"
++as hv3 ovs-vsctl show
++as hv3 ovs-vsctl list Open_Vswitch
++
++echo "------ hv4 dump ------"
++as hv4 ovs-vsctl show
++as hv4 ovs-vsctl list Open_Vswitch
++
++echo "Send traffic North to South"
++
++sip=`ip_to_hex 172 31 0 10`
++dip=`ip_to_hex 192 168 1 1`
++test_ip vif-north f0f000000011 000001010207 $sip $dip vif11
++
++# Confirm that North to south traffic works fine.
++OVN_CHECK_PACKETS([hv1/vif11-tx.pcap], [vif11.expected])
++
++echo "Send traffic South to Nouth"
++sip=`ip_to_hex 192 168 1 1`
++dip=`ip_to_hex 172 31 0 10`
++test_ip vif11 f00000000011 000001010203 $sip $dip vif-north
++
++# Confirm that South to North traffic works fine.
++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected])
++
++# Confirm that packets did not go out via tunnel port.
++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=32 | grep NXM_NX_TUN_METADATA0 | grep n_packets=0 | wc -l], [0], [[0
++]])
++
++# Confirm that packet went out via localnet port
++AT_CHECK([as hv1 ovs-ofctl dump-flows br-int | grep table=65 | grep priority=150 | grep src=00:00:01:01:02:07 | grep n_packets=1 | wc -l], [0], [[1
++]])
++
++echo "----------- Post Traffic hv1 dump -----------"
++as hv1 ovs-ofctl dump-flows br-int
++as hv1 ovs-ofctl show br-phys
++as hv1 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv2 dump -----------"
++as hv2 ovs-ofctl dump-flows br-int
++as hv2 ovs-ofctl show br-phys
++as hv2 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv3 dump -----------"
++as hv3 ovs-ofctl dump-flows br-int
++as hv3 ovs-ofctl show br-phys
++as hv3 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv4 dump -----------"
++as hv4 ovs-ofctl dump-flows br-int
++as hv4 ovs-ofctl show br-phys
++as hv4 ovs-appctl fdb/show br-phys
++
++OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
++
++AT_CLEANUP
+-- 
+2.23.0
+
diff --git a/SOURCES/0004-northd-Add-lflows-for-IPv6-NAT.patch b/SOURCES/0004-northd-Add-lflows-for-IPv6-NAT.patch
new file mode 100644
index 0000000..48c80ff
--- /dev/null
+++ b/SOURCES/0004-northd-Add-lflows-for-IPv6-NAT.patch
@@ -0,0 +1,1064 @@
+From 0ed8c91b86b0d4bc01b4795155b25e85df1c6a9d Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 25 Oct 2019 16:47:21 -0400
+Subject: [PATCH 4/5] northd: Add lflows for IPv6 NAT.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml | 233 ++++++++++++----------
+ ovn/northd/ovn-northd.c     | 384 +++++++++++++++++++++++++++---------
+ 2 files changed, 418 insertions(+), 199 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index b9ae28e14..38e6ecec5 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1498,11 +1498,55 @@ output;
+ 
+       <li>
+         <p>
+-          These flows reply to ARP requests for the virtual IP addresses
+-          configured in the router for DNAT or load balancing.  For a
+-          configured DNAT IP address or a load balancer IPv4 VIP <var>A</var>,
+-          for each router port <var>P</var> with Ethernet
+-          address <var>E</var>, a priority-90 flow matches
++          Reply to IPv6 Neighbor Solicitations.  These flows reply to
++          Neighbor Solicitation requests for the router's own IPv6
++          address and populate the logical router's mac binding table.
++        </p>
++
++        <p>
++          For each router port <var>P</var> that
++          owns IPv6 address <var>A</var>, solicited node address <var>S</var>,
++          and Ethernet address <var>E</var>, a priority-90 flow matches
++          <code>inport == <var>P</var> &amp;&amp;
++          nd_ns &amp;&amp; ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp;
++          nd.target == <var>A</var></code> with the following actions:
++        </p>
++
++        <pre>
++nd_na_router {
++    eth.src = <var>E</var>;
++    ip6.src = <var>A</var>;
++    nd.target = <var>A</var>;
++    nd.tll = <var>E</var>;
++    outport = inport;
++    flags.loopback = 1;
++    output;
++};
++        </pre>
++
++        <p>
++          For the gateway port on a distributed logical router (where
++          one of the logical router ports specifies a
++          <code>redirect-chassis</code>), the above flows replying to
++          IPv6 Neighbor Solicitations are only programmed on the
++          gateway port instance on the <code>redirect-chassis</code>.
++          This behavior avoids generation of multiple replies from
++          different chassis, and allows upstream MAC learning to point
++          to the <code>redirect-chassis</code>.
++        </p>
++      </li>
++
++      <li>
++        <p>
++          These flows reply to ARP requests or IPv6 neighbor solicitation
++          for the virtual IP addresses configured in the router for DNAT
++          or load balancing.
++        </p>
++
++        <p>
++          IPv4: For a configured DNAT IP address or a load balancer
++          IPv4 VIP <var>A</var>, for each router port <var>P</var> with
++          Ethernet address <var>E</var>, a priority-90 flow matches
+           <code>inport == <var>P</var> &amp;&amp; arp.op == 1 &amp;&amp;
+           arp.tpa == <var>A</var></code> (ARP request)
+           with the following actions:
+@@ -1521,6 +1565,30 @@ flags.loopback = 1;
+ output;
+         </pre>
+ 
++        <p>
++          IPv6: For a configured DNAT IP address or a load balancer
++          IPv6 VIP <var>A</var>, solicited node address <var>S</var>,
++          for each router port <var>P</var> with
++          Ethernet address <var>E</var>, a priority-90 flow matches
++          <code>inport == <var>P</var> &amp;&amp; nd_ns &amp;&amp;
++          ip6.dst == {<var>A</var>, <var>S</var>} &amp;&amp;
++          nd.target == <var>A</var></code>
++          with the following actions:
++        </p>
++
++        <pre>
++eth.dst = eth.src;
++nd_na {
++    eth.src = <var>E</var>;
++    nd.tll = <var>E</var>;
++    ip6.src = <var>A</var>;
++    nd.target = <var>A</var>;
++    outport = <var>P</var>;
++    flags.loopback = 1;
++    output;
++}
++        </pre>
++
+         <p>
+           For the gateway port on a distributed logical router with NAT
+           (where one of the logical router ports specifies a
+@@ -1557,6 +1625,15 @@ eth.src = <var>external_mac</var>;
+ arp.sha = <var>external_mac</var>;
+             </pre>
+ 
++            <p>
++              or in the case of IPv6 neighbor solicition:
++            </p>
++
++            <pre>
++eth.src = <var>external_mac</var>;
++nd.tll = <var>external_mac</var>;
++            </pre>
++
+             <p>
+               This behavior avoids generation of multiple ARP responses
+               from different chassis, and allows upstream MAC learning to
+@@ -1566,68 +1643,6 @@ arp.sha = <var>external_mac</var>;
+         </ul>
+       </li>
+ 
+-      <li>
+-        <p>
+-          Reply to IPv6 Neighbor Solicitations.  These flows reply to
+-          Neighbor Solicitation requests for the router's own IPv6
+-          address and load balancing IPv6 VIPs and populate the logical
+-          router's mac binding table.
+-        </p>
+-
+-        <p>
+-          For each router port <var>P</var> that
+-          owns IPv6 address <var>A</var>, solicited node address <var>S</var>,
+-          and Ethernet address <var>E</var>, a priority-90 flow matches
+-          <code>inport == <var>P</var> &amp;&amp;
+-          nd_ns &amp;&amp; ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp;
+-          nd.target == <var>A</var></code> with the following actions:
+-        </p>
+-
+-        <pre>
+-nd_na_router {
+-    eth.src = <var>E</var>;
+-    ip6.src = <var>A</var>;
+-    nd.target = <var>A</var>;
+-    nd.tll = <var>E</var>;
+-    outport = inport;
+-    flags.loopback = 1;
+-    output;
+-};
+-        </pre>
+-
+-        <p>
+-          For each router port <var>P</var> that has load balancing VIP
+-          <var>A</var>, solicited node address <var>S</var>, and Ethernet
+-          address <var>E</var>, a priority-90 flow matches
+-          <code>inport == <var>P</var> &amp;&amp;
+-          nd_ns &amp;&amp; ip6.dst == {<var>A</var>, <var>E</var>} &amp;&amp;
+-          nd.target == <var>A</var></code> with the following actions:
+-        </p>
+-
+-        <pre>
+-nd_na {
+-    eth.src = <var>E</var>;
+-    ip6.src = <var>A</var>;
+-    nd.target = <var>A</var>;
+-    nd.tll = <var>E</var>;
+-    outport = inport;
+-    flags.loopback = 1;
+-    output;
+-};
+-        </pre>
+-
+-        <p>
+-          For the gateway port on a distributed logical router (where
+-          one of the logical router ports specifies a
+-          <code>redirect-chassis</code>), the above flows replying to
+-          IPv6 Neighbor Solicitations are only programmed on the
+-          gateway port instance on the <code>redirect-chassis</code>.
+-          This behavior avoids generation of multiple replies from
+-          different chassis, and allows upstream MAC learning to point
+-          to the <code>redirect-chassis</code>.
+-        </p>
+-      </li>
+-
+       <li>
+         Priority-85 flows which drops the ARP and IPv6 Neighbor Discovery
+         packets.
+@@ -1681,10 +1696,11 @@ nd_na {
+         handled by one of the flows above, which amounts to ICMP (other than
+         echo requests) and fragments with nonzero offsets.  For each IP address
+         <var>A</var> owned by the router, a priority-60 flow matches
+-        <code>ip4.dst == <var>A</var></code> and drops the traffic.  An
+-        exception is made and the above flow is not added if the router
+-        port's own IP address is used to SNAT packets passing through that
+-        router.
++        <code>ip4.dst == <var>A</var></code> or
++        <code>ip6.dst == <var>A</var></code>
++        and drops the traffic.  An exception is made and the above flow
++        is not added if the router port's own IP address is used to SNAT
++        packets passing through that router.
+       </li>
+     </ul>
+ 
+@@ -1778,23 +1794,26 @@ icmp6 {
+         <p>
+           If the Gateway router has been configured to force SNAT any
+           previously DNATted packets to <var>B</var>, a priority-110 flow
+-          matches <code>ip &amp;&amp; ip4.dst == <var>B</var></code> with
+-          an action <code>ct_snat; </code>.
++          matches <code>ip &amp;&amp; ip4.dst == <var>B</var></code> or
++          <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
++          with an action <code>ct_snat; </code>.
+         </p>
+ 
+         <p>
+           If the Gateway router has been configured to force SNAT any
+           previously load-balanced packets to <var>B</var>, a priority-100 flow
+-          matches <code>ip &amp;&amp; ip4.dst == <var>B</var></code> with
+-          an action <code>ct_snat; </code>.
++          matches <code>ip &amp;&amp; ip4.dst == <var>B</var></code> or
++          <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
++          with an action <code>ct_snat; </code>.
+         </p>
+ 
+         <p>
+           For each NAT configuration in the OVN Northbound database, that asks
+           to change the source IP address of a packet from <var>A</var> to
+-          <var>B</var>, a priority-90 flow matches <code>ip &amp;&amp;
+-          ip4.dst == <var>B</var></code> with an action
+-          <code>ct_snat; </code>.
++          <var>B</var>, a priority-90 flow matches
++          <code>ip &amp;&amp; ip4.dst == <var>B</var></code> or
++          <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
++          with an action <code>ct_snat; </code>.
+         </p>
+ 
+         <p>
+@@ -1812,7 +1831,9 @@ icmp6 {
+           For each configuration in the OVN Northbound database, that asks
+           to change the source IP address of a packet from <var>A</var> to
+           <var>B</var>, a priority-100 flow matches <code>ip &amp;&amp;
+-          ip4.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code>,
++          ip4.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code> or
++          <code>ip &amp;&amp;
++          ip6.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code>
+           where <var>GW</var> is the logical router gateway port, with an
+           action <code>ct_snat;</code>.
+         </p>
+@@ -1826,10 +1847,12 @@ icmp6 {
+         <p>
+           For each configuration in the OVN Northbound database, that asks
+           to change the source IP address of a packet from <var>A</var> to
+-          <var>B</var>, a priority-50 flow matches <code>ip &amp;&amp;
+-          ip4.dst == <var>B</var></code> with an action
++          <var>B</var>, a priority-50 flow matches
++          <code>ip &amp;&amp; ip4.dst == <var>B</var></code> or
++          <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
++          with an action
+           <code>REGBIT_NAT_REDIRECT = 1; next;</code>.  This flow is for
+-          east/west traffic to a NAT destination IPv4 address.  By
++          east/west traffic to a NAT destination IPv4/IPv6 address.  By
+           setting the <code>REGBIT_NAT_REDIRECT</code> flag, in the
+           ingress table <code>Gateway Redirect</code> this will trigger a
+           redirect to the instance of the gateway port on the
+@@ -1875,25 +1898,27 @@ icmp6 {
+         For all the configured load balancing rules for a Gateway router or
+         Router with gateway port in <code>OVN_Northbound</code> database that
+         includes a L4 port <var>PORT</var> of protocol <var>P</var> and IPv4
+-        address <var>VIP</var>, a priority-120 flow that matches on
++        or IPv6 address <var>VIP</var>, a priority-120 flow that matches on
+         <code>ct.new &amp;&amp; ip &amp;&amp; ip4.dst == <var>VIP</var>
+         &amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>PORT
+-        </var></code> with an action of <code>ct_lb(<var>args</var>)</code>,
+-        where <var>args</var> contains comma separated IPv4 addresses (and
+-        optional port numbers) to load balance to.  If the router is configured
+-        to force SNAT any load-balanced packets, the above action will be
+-        replaced by <code>flags.force_snat_for_lb = 1;
++        </var></code> (<code>ip6.dst == <var>VIP</var></code> in the IPv6 case)
++        with an action of <code>ct_lb(<var>args</var>)</code>,
++        where <var>args</var> contains comma separated IPv4 or IPv6 addresses
++        (and optional port numbers) to load balance to.  If the router is
++        configured to force SNAT any load-balanced packets, the above action
++        will be replaced by <code>flags.force_snat_for_lb = 1;
+         ct_lb(<var>args</var>);</code>.
+       </li>
+ 
+       <li>
+         For all the configured load balancing rules for a router in
+         <code>OVN_Northbound</code> database that includes a L4 port
+-        <var>PORT</var> of protocol <var>P</var> and IPv4 address
++        <var>PORT</var> of protocol <var>P</var> and IPv4 or IPv6 address
+         <var>VIP</var>, a priority-120 flow that matches on
+         <code>ct.est &amp;&amp; ip &amp;&amp; ip4.dst == <var>VIP</var>
+         &amp;&amp; <var>P</var> &amp;&amp; <var>P</var>.dst == <var>PORT
+-        </var></code> with an action of <code>ct_dnat;</code>. If the router is
++        </var></code> (<code>ip6.dst == <var>VIP</var></code> in the IPv6 case)
++        with an action of <code>ct_dnat;</code>. If the router is
+         configured to force SNAT any load-balanced packets, the above action
+         will be replaced by <code>flags.force_snat_for_lb = 1; ct_dnat;</code>.
+       </li>
+@@ -1903,11 +1928,13 @@ icmp6 {
+         <code>OVN_Northbound</code> database that includes just an IP address
+         <var>VIP</var> to match on, a priority-110 flow that matches on
+         <code>ct.new &amp;&amp; ip &amp;&amp; ip4.dst ==
+-        <var>VIP</var></code> with an action of
++        <var>VIP</var></code> (<code>ip6.dst == <var>VIP</var></code> in the
++        IPv6 case) with an action of
+         <code>ct_lb(<var>args</var>)</code>, where <var>args</var> contains
+-        comma separated IPv4 addresses.  If the router is configured to force
+-        SNAT any load-balanced packets, the above action will be replaced by
+-        <code>flags.force_snat_for_lb = 1; ct_lb(<var>args</var>);</code>.
++        comma separated IPv4 or IPv6 addresses.  If the router is configured
++        to force SNAT any load-balanced packets, the above action will be
++        replaced by <code>flags.force_snat_for_lb = 1;
++        ct_lb(<var>args</var>);</code>.
+       </li>
+ 
+       <li>
+@@ -1915,7 +1942,8 @@ icmp6 {
+         <code>OVN_Northbound</code> database that includes just an IP address
+         <var>VIP</var> to match on, a priority-110 flow that matches on
+         <code>ct.est &amp;&amp; ip &amp;&amp; ip4.dst ==
+-        <var>VIP</var></code> with an action of <code>ct_dnat;</code>.
++        <var>VIP</var></code> (or <code>ip6.dst == <var>VIP</var></code>)
++        with an action of <code>ct_dnat;</code>.
+         If the router is configured to force SNAT any load-balanced
+         packets, the above action will be replaced by
+         <code>flags.force_snat_for_lb = 1; ct_dnat;</code>.
+@@ -1929,7 +1957,8 @@ icmp6 {
+         For each configuration in the OVN Northbound database, that asks
+         to change the destination IP address of a packet from <var>A</var> to
+         <var>B</var>, a priority-100 flow matches <code>ip &amp;&amp;
+-        ip4.dst == <var>A</var></code> with an action
++        ip4.dst == <var>A</var></code> or <code>ip &amp;&amp;
++        ip6.dst == <var>A</var></code> with an action
+         <code>flags.loopback = 1; ct_dnat(<var>B</var>);</code>.  If the
+         Gateway router is configured to force SNAT any DNATed packet,
+         the above action will be replaced by
+@@ -1966,7 +1995,8 @@ icmp6 {
+           <var>B</var>, a priority-100 flow matches <code>ip &amp;&amp;
+           ip4.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code>,
+           where <var>GW</var> is the logical router gateway port, with an
+-          action <code>ct_dnat(<var>B</var>);</code>.
++          action <code>ct_dnat(<var>B</var>);</code>.  The match will
++          include <code>ip6.dst == <var>B</var></code> in the IPv6 case.
+         </p>
+ 
+         <p>
+@@ -1979,9 +2009,10 @@ icmp6 {
+           For each configuration in the OVN Northbound database, that asks
+           to change the destination IP address of a packet from <var>A</var> to
+           <var>B</var>, a priority-50 flow matches <code>ip &amp;&amp;
+-          ip4.dst == <var>B</var></code> with an action
++          ip4.dst == <var>B</var></code> or <code>ip &amp;&amp;
++          ip6.dst == <var>B</var></code> with an action
+           <code>REGBIT_NAT_REDIRECT = 1; next;</code>.  This flow is for
+-          east/west traffic to a NAT destination IPv4 address.  By
++          east/west traffic to a NAT destination IPv4/IPv6 address.  By
+           setting the <code>REGBIT_NAT_REDIRECT</code> flag, in the
+           ingress table <code>Gateway Redirect</code> this will trigger a
+           redirect to the instance of the gateway port on the
+@@ -2136,8 +2167,8 @@ logical_ip{0,1} = <var>LIP{0,1}</var>;
+         <pre>
+ eth.dst = <var>MAC0</var>;
+ eth.src = <var>MAC1</var>;
+-reg0 = ip4.dst;
+-reg1 = <var>EIP1</var>;
++reg0 = ip4.dst; /* xxreg0 = ip6.dst; in the IPv6 case */
++reg1 = <var>EIP1</var>; /* xxreg1 in the IPv6 case */
+ outport = <code>redirect-chassis-port</code>;
+ <code>REGBIT_DISTRIBUTED_NAT = 1; next;</code>.
+         </pre>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 7cfeb60be..84b2a9ff1 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -66,6 +66,15 @@ struct northd_context {
+     struct ovsdb_idl_index *sbrec_ip_mcast_by_dp;
+ };
+ 
++/* An IPv4 or IPv6 address */
++struct v46_ip {
++    int family;
++    union {
++        ovs_be32 ipv4;
++        struct in6_addr ipv6;
++    };
++};
++
+ static const char *ovnnb_db;
+ static const char *ovnsb_db;
+ static const char *unixctl_path;
+@@ -2266,6 +2275,15 @@ get_nat_addresses(const struct ovn_port *op, size_t *n)
+                     break;
+                 }
+             }
++            if (!is_router_ip) {
++                for (size_t j = 0; j < op->lrp_networks.n_ipv6_addrs; j++) {
++                    if (!strcmp(nat->external_ip,
++                                op->lrp_networks.ipv6_addrs[j].addr_s)) {
++                        is_router_ip = true;
++                        break;
++                    }
++                }
++            }
+ 
+             if (!is_router_ip) {
+                 ds_put_format(&c_addresses, " %s", nat->external_ip);
+@@ -6013,9 +6031,28 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
+             continue;
+         }
+ 
++        /* Determine if we need to create IPv4 or IPv6 flows */
++        ovs_be32 ip;
++        struct in6_addr ipv6;
++        int family = AF_INET;
++        if (!ip_parse(nat->external_ip, &ip) || !ip) {
++            family = AF_INET6;
++            if (!ipv6_parse(nat->external_ip, &ipv6)) {
++                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
++                VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
++                             "for router %s", nat->external_ip, op->key);
++                /* We'll create IPv6 flows anyway, but the address
++                 * is probably bogus ... */
++            }
++        }
++
+         ds_put_format(&match, "inport == %s && "
+-                      "ip4.src == %s && ip4.dst == %s",
+-                       op->json_key, nat->logical_ip, nat->external_ip);
++                      "ip%s.src == %s && ip%s.dst == %s",
++                       op->json_key,
++                       family == AF_INET ? "4" : "6",
++                       nat->logical_ip,
++                       family == AF_INET ? "4" : "6",
++                       nat->external_ip);
+         ds_put_format(&actions, "outport = %s; eth.dst = %s; "
+                       REGBIT_DISTRIBUTED_NAT" = 1; "
+                       REGBIT_NAT_REDIRECT" = 0; next;",
+@@ -6033,17 +6070,38 @@ add_distributed_nat_routes(struct hmap *lflows, const struct ovn_port *op)
+                 !nat2->external_mac || !nat2->external_ip)
+                 continue;
+ 
++            family = AF_INET;
++            if (!ip_parse(nat2->external_ip, &ip) || !ip) {
++                family = AF_INET6;
++                if (!ipv6_parse(nat2->external_ip, &ipv6)) {
++                    static struct vlog_rate_limit rl =
++                        VLOG_RATE_LIMIT_INIT(5, 1);
++                    VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
++                                 "for router %s", nat2->external_ip, op->key);
++                    /* We'll create IPv6 flows anyway, but the address
++                     * is probably bogus ... */
++                }
++            }
++
+             ds_put_format(&match, "inport == %s && "
+-                          "ip4.src == %s && ip4.dst == %s",
+-                          op->json_key, nat->logical_ip, nat2->external_ip);
++                          "ip%s.src == %s && ip%s.dst == %s",
++                          op->json_key,
++                          family == AF_INET ? "4" : "6",
++                          nat->logical_ip,
++                          family == AF_INET ? "4" : "6",
++                          nat2->external_ip);
+             ds_put_format(&actions, "outport = %s; "
+                           "eth.src = %s; eth.dst = %s; "
+-                          "reg0 = ip4.dst; reg1 = %s; "
++                          "%sreg0 = ip%s.dst; %sreg1 = %s; "
+                           REGBIT_DISTRIBUTED_NAT" = 1; "
+                           REGBIT_NAT_REDIRECT" = 0; next;",
+                           op->od->l3dgw_port->json_key,
+                           op->od->l3dgw_port->lrp_networks.ea_s,
+-                          nat2->external_mac, nat->external_ip);
++                          nat2->external_mac,
++                          family == AF_INET ? "" : "xx",
++                          family == AF_INET ? "4" : "6",
++                          family == AF_INET ? "" : "xx",
++                          nat->external_ip);
+             ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_ROUTING, 400,
+                           ds_cstr(&match), ds_cstr(&actions));
+             ds_clear(&match);
+@@ -6280,7 +6338,8 @@ op_put_v6_networks(struct ds *ds, const struct ovn_port *op)
+ }
+ 
+ static const char *
+-get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip)
++get_force_snat_ip(struct ovn_datapath *od, const char *key_type,
++                  struct v46_ip *ip)
+ {
+     char *key = xasprintf("%s_force_snat_ip", key_type);
+     const char *ip_address = smap_get(&od->nbr->options, key);
+@@ -6288,19 +6347,27 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type, ovs_be32 *ip)
+ 
+     if (ip_address) {
+         ovs_be32 mask;
+-        char *error = ip_parse_masked(ip_address, ip, &mask);
++        ip->family = AF_INET;
++        char *error = ip_parse_masked(ip_address, &ip->ipv4, &mask);
+         if (error || mask != OVS_BE32_MAX) {
+-            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+-            VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
+-                         ip_address, UUID_ARGS(&od->key));
+             free(error);
+-            *ip = 0;
+-            return NULL;
++            struct in6_addr mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
++            ip->family = AF_INET6;
++            error = ipv6_parse_masked(ip_address, &ip->ipv6, &mask_v6);
++            if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) {
++                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
++                VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
++                             ip_address, UUID_ARGS(&od->key));
++                memset(ip, 0, sizeof *ip);
++                ip->family = AF_UNSPEC;
++                return NULL;
++            }
+         }
+         return ip_address;
+     }
+ 
+-    *ip = 0;
++    memset(ip, 0, sizeof *ip);
++    ip->family = AF_UNSPEC;
+     return NULL;
+ }
+ 
+@@ -6866,11 +6933,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         /* A gateway router can have 2 SNAT IP addresses to force DNATed and
+          * LBed traffic respectively to be SNATed.  In addition, there can be
+          * a number of SNAT rules in the NAT table. */
+-        ovs_be32 *snat_ips = xmalloc(sizeof *snat_ips *
+-                                     (op->od->nbr->n_nat + 2));
++        struct v46_ip *snat_ips = xmalloc(sizeof *snat_ips
++                                          * (op->od->nbr->n_nat + 2));
+         size_t n_snat_ips = 0;
+ 
+-        ovs_be32 snat_ip;
++        struct v46_ip snat_ip;
+         const char *dnat_force_snat_ip = get_force_snat_ip(op->od, "dnat",
+                                                            &snat_ip);
+         if (dnat_force_snat_ip) {
+@@ -6889,44 +6956,85 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             nat = op->od->nbr->nat[i];
+ 
+             ovs_be32 ip;
++            struct in6_addr ipv6;
++            bool is_v6 = false;
+             if (!ip_parse(nat->external_ip, &ip) || !ip) {
+-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+-                VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
+-                             "for router %s", nat->external_ip, op->key);
+-                continue;
++                if (!ipv6_parse(nat->external_ip, &ipv6)) {
++                    static struct vlog_rate_limit rl =
++                        VLOG_RATE_LIMIT_INIT(5, 1);
++                    VLOG_WARN_RL(&rl, "bad ip address %s in nat configuration "
++                                 "for router %s", nat->external_ip, op->key);
++                    continue;
++                }
++                is_v6 = true;
+             }
+ 
+             if (!strcmp(nat->type, "snat")) {
+-                snat_ips[n_snat_ips++] = ip;
++                if (is_v6) {
++                    snat_ips[n_snat_ips].family = AF_INET6;
++                    snat_ips[n_snat_ips++].ipv6 = ipv6;
++                } else {
++                    snat_ips[n_snat_ips].family = AF_INET;
++                    snat_ips[n_snat_ips++].ipv4 = ip;
++                }
+                 continue;
+             }
+ 
+-            /* ARP handling for external IP addresses.
++            /* ARP / ND handling for external IP addresses.
+              *
+              * DNAT IP addresses are external IP addresses that need ARP
+              * handling. */
++            char addr_s[INET6_ADDRSTRLEN + 1];
+             ds_clear(&match);
+-            ds_put_format(&match,
+-                          "inport == %s && arp.tpa == "IP_FMT" && arp.op == 1",
+-                          op->json_key, IP_ARGS(ip));
+-
+             ds_clear(&actions);
+-            ds_put_format(&actions,
+-                "eth.dst = eth.src; "
+-                "arp.op = 2; /* ARP reply */ "
+-                "arp.tha = arp.sha; ");
++            if (is_v6) {
++                /* For ND solicitations, we need to listen for both the
++                 * unicast IPv6 address and its all-nodes multicast address,
++                 * but always respond with the unicast IPv6 address. */
++                char sn_addr_s[INET6_ADDRSTRLEN + 1];
++                struct in6_addr sn_addr;
++                in6_addr_solicited_node(&sn_addr, &ipv6);
++                ipv6_string_mapped(sn_addr_s, &sn_addr);
++                ipv6_string_mapped(addr_s, &ipv6);
++
++                ds_put_format(&match, "inport == %s && "
++                        "nd_ns && ip6.dst == {%s, %s} && nd.target == %s",
++                        op->json_key, addr_s, sn_addr_s, addr_s);
+ 
++                ds_put_format(&actions,
++                    "eth.dst = eth.src; "
++                    "nd_na { ");
++            } else {
++                ds_put_format(&match,
++                              "inport == %s "
++                              "&& arp.tpa == "IP_FMT" && arp.op == 1",
++                              op->json_key, IP_ARGS(ip));
++
++                ds_put_format(&actions,
++                    "eth.dst = eth.src; "
++                    "arp.op = 2; /* ARP reply */ "
++                    "arp.tha = arp.sha; ");
++            }
+             if (op->od->l3dgw_port && op == op->od->l3dgw_port) {
+                 struct eth_addr mac;
+                 if (nat->external_mac &&
+                     eth_addr_from_string(nat->external_mac, &mac)
+                     && nat->logical_port) {
+                     /* distributed NAT case, use nat->external_mac */
+-                    ds_put_format(&actions,
+-                        "eth.src = "ETH_ADDR_FMT"; "
+-                        "arp.sha = "ETH_ADDR_FMT"; ",
+-                        ETH_ADDR_ARGS(mac),
+-                        ETH_ADDR_ARGS(mac));
++                    if (is_v6) {
++                        ds_put_format(&actions,
++                            "eth.src = "ETH_ADDR_FMT"; "
++                            "nd.tll = "ETH_ADDR_FMT"; ",
++                            ETH_ADDR_ARGS(mac),
++                            ETH_ADDR_ARGS(mac));
++
++                    } else {
++                        ds_put_format(&actions,
++                            "eth.src = "ETH_ADDR_FMT"; "
++                            "arp.sha = "ETH_ADDR_FMT"; ",
++                            ETH_ADDR_ARGS(mac),
++                            ETH_ADDR_ARGS(mac));
++                    }
+                     /* Traffic with eth.src = nat->external_mac should only be
+                      * sent from the chassis where nat->logical_port is
+                      * resident, so that upstream MAC learning points to the
+@@ -6935,11 +7043,20 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                     ds_put_format(&match, " && is_chassis_resident(\"%s\")",
+                                   nat->logical_port);
+                 } else {
+-                    ds_put_format(&actions,
+-                        "eth.src = %s; "
+-                        "arp.sha = %s; ",
+-                        op->lrp_networks.ea_s,
+-                        op->lrp_networks.ea_s);
++                    if (is_v6) {
++                        ds_put_format(&actions,
++                            "eth.src = %s; "
++                            "nd.tll = %s; ",
++                            op->lrp_networks.ea_s,
++                            op->lrp_networks.ea_s);
++
++                    } else {
++                        ds_put_format(&actions,
++                            "eth.src = %s; "
++                            "arp.sha = %s; ",
++                            op->lrp_networks.ea_s,
++                            op->lrp_networks.ea_s);
++                    }
+                     /* Traffic with eth.src = l3dgw_port->lrp_networks.ea_s
+                      * should only be sent from the "redirect-chassis", so that
+                      * upstream MAC learning points to the "redirect-chassis".
+@@ -6950,21 +7067,40 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                                       op->od->l3redirect_port->json_key);
+                     }
+                 }
++            } else {
++                if (is_v6) {
++                    ds_put_format(&actions,
++                        "eth.src = %s; "
++                        "nd.tll = %s; ",
++                        op->lrp_networks.ea_s,
++                        op->lrp_networks.ea_s);
++                } else {
++                    ds_put_format(&actions,
++                        "eth.src = %s; "
++                        "arp.sha = %s; ",
++                        op->lrp_networks.ea_s,
++                        op->lrp_networks.ea_s);
++                }
++            }
++            if (is_v6) {
++                ds_put_format(&actions,
++                    "ip6.src = %s; "
++                    "nd.target = %s; "
++                    "outport = %s; "
++                    "flags.loopback = 1; "
++                    "output; "
++                    "};",
++                    addr_s, addr_s, op->json_key);
+             } else {
+                 ds_put_format(&actions,
+-                    "eth.src = %s; "
+-                    "arp.sha = %s; ",
+-                    op->lrp_networks.ea_s,
+-                    op->lrp_networks.ea_s);
++                    "arp.tpa = arp.spa; "
++                    "arp.spa = "IP_FMT"; "
++                    "outport = %s; "
++                    "flags.loopback = 1; "
++                    "output;",
++                    IP_ARGS(ip),
++                    op->json_key);
+             }
+-            ds_put_format(&actions,
+-                "arp.tpa = arp.spa; "
+-                "arp.spa = "IP_FMT"; "
+-                "outport = %s; "
+-                "flags.loopback = 1; "
+-                "output;",
+-                IP_ARGS(ip),
+-                op->json_key);
+             ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                           ds_cstr(&match), ds_cstr(&actions));
+         }
+@@ -7021,7 +7157,36 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             bool snat_ip_is_router_ip = false;
+             for (int j = 0; j < n_snat_ips; j++) {
+                 /* Packets to SNAT IPs should not be dropped. */
+-                if (op->lrp_networks.ipv4_addrs[i].addr == snat_ips[j]) {
++                if (snat_ips[j].family == AF_INET
++                    && op->lrp_networks.ipv4_addrs[i].addr
++                       == snat_ips[j].ipv4) {
++                        snat_ip_is_router_ip = true;
++                        break;
++                }
++            }
++            if (snat_ip_is_router_ip) {
++                continue;
++            }
++            ds_put_format(&match, "%s, ",
++                          op->lrp_networks.ipv4_addrs[i].addr_s);
++            has_drop_ips = true;
++        }
++        if (has_drop_ips) {
++            ds_chomp(&match, ' ');
++            ds_chomp(&match, ',');
++            ds_put_cstr(&match, "} || ip6.dst == {");
++        } else {
++            ds_clear(&match);
++            ds_put_cstr(&match, "ip6.dst == {");
++        }
++
++        for (int i = 0; i < op->lrp_networks.n_ipv6_addrs; i++) {
++            bool snat_ip_is_router_ip = false;
++            for (int j = 0; j < n_snat_ips; j++) {
++                /* Packets to SNAT IPs should not be dropped. */
++                if (snat_ips[j].family == AF_INET6
++                    && !memcmp(&op->lrp_networks.ipv6_addrs[i].addr,
++                               &snat_ips[j].ipv6, sizeof snat_ips[j].ipv6)) {
+                     snat_ip_is_router_ip = true;
+                     break;
+                 }
+@@ -7030,9 +7195,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                 continue;
+             }
+             ds_put_format(&match, "%s, ",
+-                          op->lrp_networks.ipv4_addrs[i].addr_s);
++                          op->lrp_networks.ipv6_addrs[i].addr_s);
+             has_drop_ips = true;
+         }
++
+         ds_chomp(&match, ' ');
+         ds_chomp(&match, ',');
+         ds_put_cstr(&match, "}");
+@@ -7059,14 +7225,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+ 
+         if (op->lrp_networks.n_ipv6_addrs) {
+-            /* L3 admission control: drop packets that originate from an
+-             * IPv6 address owned by the router (priority 100). */
+-            ds_clear(&match);
+-            ds_put_cstr(&match, "ip6.src == ");
+-            op_put_v6_networks(&match, op);
+-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 100,
+-                          ds_cstr(&match), "drop;");
+-
+             /* ICMPv6 echo reply.  These flows reply to echo requests
+              * received for the router's IP address. */
+             ds_clear(&match);
+@@ -7083,13 +7241,6 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                         "next; ");
+             ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 90,
+                           ds_cstr(&match), ds_cstr(&actions));
+-
+-            /* Drop IPv6 traffic to this router. */
+-            ds_clear(&match);
+-            ds_put_cstr(&match, "ip6.dst == ");
+-            op_put_v6_networks(&match, op);
+-            ovn_lflow_add(lflows, op->od, S_ROUTER_IN_IP_INPUT, 60,
+-                          ds_cstr(&match), "drop;");
+         }
+ 
+         /* ND reply.  These flows reply to ND solicitations for the
+@@ -7231,11 +7382,11 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
+-        ovs_be32 snat_ip;
++        struct v46_ip snat_ip, lb_snat_ip;
+         const char *dnat_force_snat_ip = get_force_snat_ip(od, "dnat",
+                                                            &snat_ip);
+         const char *lb_force_snat_ip = get_force_snat_ip(od, "lb",
+-                                                         &snat_ip);
++                                                         &lb_snat_ip);
+ 
+         for (int i = 0; i < od->nbr->n_nat; i++) {
+             const struct nbrec_nat *nat;
+@@ -7243,21 +7394,38 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             nat = od->nbr->nat[i];
+ 
+             ovs_be32 ip, mask;
++            struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
++            bool is_v6 = false;
+ 
+             char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
+             if (error || mask != OVS_BE32_MAX) {
+-                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
+-                VLOG_WARN_RL(&rl, "bad external ip %s for nat",
+-                             nat->external_ip);
+                 free(error);
+-                continue;
++                error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6);
++                if (error || memcmp(&mask_v6, &v6_exact, sizeof(mask_v6))) {
++                    /* Invalid for both IPv4 and IPv6 */
++                    static struct vlog_rate_limit rl =
++                        VLOG_RATE_LIMIT_INIT(5, 1);
++                    VLOG_WARN_RL(&rl, "bad external ip %s for nat",
++                                 nat->external_ip);
++                    free(error);
++                    continue;
++                }
++                /* It was an invalid IPv4 address, but valid IPv6.
++                 * Treat the rest of the handling of this NAT rule
++                 * as IPv6. */
++                is_v6 = true;
+             }
+ 
+             /* Check the validity of nat->logical_ip. 'logical_ip' can
+              * be a subnet when the type is "snat". */
+-            error = ip_parse_masked(nat->logical_ip, &ip, &mask);
++            if (is_v6) {
++                error = ipv6_parse_masked(nat->external_ip, &ipv6, &mask_v6);
++            } else {
++                error = ip_parse_masked(nat->logical_ip, &ip, &mask);
++            }
+             if (!strcmp(nat->type, "snat")) {
+                 if (error) {
++                    /* Invalid for both IPv4 and IPv6 */
+                     static struct vlog_rate_limit rl =
+                         VLOG_RATE_LIMIT_INIT(5, 1);
+                     VLOG_WARN_RL(&rl, "bad ip network or ip %s for snat "
+@@ -7267,7 +7435,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                     continue;
+                 }
+             } else {
+-                if (error || mask != OVS_BE32_MAX) {
++                if (error || (!is_v6 && mask != OVS_BE32_MAX)
++                    || (is_v6 && memcmp(&mask_v6, &v6_exact,
++                                        sizeof mask_v6))) {
++                    /* Invalid for both IPv4 and IPv6 */
+                     static struct vlog_rate_limit rl =
+                         VLOG_RATE_LIMIT_INIT(5, 1);
+                     VLOG_WARN_RL(&rl, "bad ip %s for dnat in router "
+@@ -7308,7 +7479,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                 if (!od->l3dgw_port) {
+                     /* Gateway router. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.dst == %s",
++                    ds_put_format(&match, "ip && ip%s.dst == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->external_ip);
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
+                                   ds_cstr(&match), "ct_snat;");
+@@ -7317,8 +7489,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                     /* Traffic received on l3dgw_port is subject to NAT. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.dst == %s"
++                    ds_put_format(&match, "ip && ip%s.dst == %s"
+                                           " && inport == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->external_ip,
+                                   od->l3dgw_port->json_key);
+                     if (!distributed && od->l3redirect_port) {
+@@ -7334,7 +7507,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                      * redirected to the central instance of the l3dgw_port
+                      * for NAT processing. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.dst == %s",
++                    ds_put_format(&match, "ip && ip%s.dst == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->external_ip);
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 50,
+                                   ds_cstr(&match),
+@@ -7353,7 +7527,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                      * We need to set flags.loopback because the router can
+                      * send the packet back through the same interface. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.dst == %s",
++                    ds_put_format(&match, "ip && ip%s.dst == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->external_ip);
+                     ds_clear(&actions);
+                     if (dnat_force_snat_ip) {
+@@ -7372,8 +7547,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                     /* Traffic received on l3dgw_port is subject to NAT. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.dst == %s"
++                    ds_put_format(&match, "ip && ip%s.dst == %s"
+                                           " && inport == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->external_ip,
+                                   od->l3dgw_port->json_key);
+                     if (!distributed && od->l3redirect_port) {
+@@ -7392,7 +7568,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                      * redirected to the central instance of the l3dgw_port
+                      * for NAT processing. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.dst == %s",
++                    ds_put_format(&match, "ip && ip%s.dst == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->external_ip);
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 50,
+                                   ds_cstr(&match),
+@@ -7411,8 +7588,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             if (od->l3dgw_port && (!strcmp(nat->type, "dnat")
+                 || !strcmp(nat->type, "dnat_and_snat"))) {
+                 ds_clear(&match);
+-                ds_put_format(&match, "ip && ip4.src == %s"
++                ds_put_format(&match, "ip && ip%s.src == %s"
+                                       " && outport == %s",
++                              is_v6 ? "6" : "4",
+                               nat->logical_ip,
+                               od->l3dgw_port->json_key);
+                 if (!distributed && od->l3redirect_port) {
+@@ -7439,7 +7617,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                 if (!od->l3dgw_port) {
+                     /* Gateway router. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.src == %s",
++                    ds_put_format(&match, "ip && ip%s.src == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->logical_ip);
+                     ds_clear(&actions);
+                     ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
+@@ -7455,8 +7634,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                     /* Distributed router. */
+                     ds_clear(&match);
+-                    ds_put_format(&match, "ip && ip4.src == %s"
++                    ds_put_format(&match, "ip && ip%s.src == %s"
+                                           " && outport == %s",
++                                  is_v6 ? "6" : "4",
+                                   nat->logical_ip,
+                                   od->l3dgw_port->json_key);
+                     if (!distributed && od->l3redirect_port) {
+@@ -7505,7 +7685,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+              * can be applied in a distributed manner. */
+             if (distributed) {
+                 ds_clear(&match);
+-                ds_put_format(&match, "ip4.src == %s && outport == %s",
++                ds_put_format(&match, "ip%s.src == %s && outport == %s",
++                              is_v6 ? "6" : "4",
+                               nat->logical_ip,
+                               od->l3dgw_port->json_key);
+                 ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
+@@ -7532,9 +7713,10 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                         ds_clear(&match);
+                         ds_put_format(&match, "is_chassis_resident(\"%s\") && "
+-                                      "ip4.src == %s && ip4.dst == %s",
+-                                      nat->logical_port, nat2->external_ip,
+-                                      nat->external_ip);
++                                      "ip%s.src == %s && ip%s.dst == %s",
++                                      nat->logical_port,
++                                      is_v6 ? "6" : "4", nat2->external_ip,
++                                      is_v6 ? "6" : "4", nat->external_ip);
+                         ds_clear(&actions);
+                         ds_put_format(&actions,
+                                       "inport = outport; outport = \"\"; "
+@@ -7546,8 +7728,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+                         ds_clear(&match);
+                         ds_put_format(&match,
+-                                      "ip4.src == %s && ip4.dst == %s",
+-                                      nat2->external_ip, nat->external_ip);
++                                      "ip%s.src == %s && ip%s.dst == %s",
++                                      is_v6 ? "6" : "4", nat2->external_ip,
++                                      is_v6 ? "6" : "4", nat->external_ip);
+                         ovn_lflow_add(lflows, od, S_ROUTER_OUT_EGR_LOOP, 200,
+                                       ds_cstr(&match), "next;");
+                         ds_clear(&match);
+@@ -7555,7 +7738,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                 }
+ 
+                 ds_clear(&match);
+-                ds_put_format(&match, "ip4.dst == %s && outport == %s",
++                ds_put_format(&match, "ip%s.dst == %s && outport == %s",
++                              is_v6 ? "6" : "4",
+                               nat->external_ip,
+                               od->l3dgw_port->json_key);
+                 ds_clear(&actions);
+@@ -7579,7 +7763,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+              * gateway router (as set in options:dnat_force_snat_ip) is seen,
+              * UNSNAT it. */
+             ds_clear(&match);
+-            ds_put_format(&match, "ip && ip4.dst == %s", dnat_force_snat_ip);
++            ds_put_format(&match, "ip && ip%s.dst == %s",
++                          snat_ip.family == AF_INET ? "4" : "6",
++                          dnat_force_snat_ip);
+             ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 110,
+                           ds_cstr(&match), "ct_snat;");
+ 
+@@ -7598,7 +7784,9 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+              * gateway router (as set in options:lb_force_snat_ip) is seen,
+              * UNSNAT it. */
+             ds_clear(&match);
+-            ds_put_format(&match, "ip && ip4.dst == %s", lb_force_snat_ip);
++            ds_put_format(&match, "ip && ip%s.dst == %s",
++                          lb_snat_ip.family == AF_INET ? "4" : "6",
++                          lb_force_snat_ip);
+             ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
+                           ds_cstr(&match), "ct_snat;");
+ 
+@@ -7700,7 +7888,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                     if (addr_family == AF_INET) {
+                         ds_put_format(&match, "ip && ip4.dst == %s",
+                                       ip_address);
+-                    } else {
++                    } else if (addr_family == AF_INET6) {
+                         ds_put_format(&match, "ip && ip6.dst == %s",
+                                       ip_address);
+                     }
+@@ -7720,7 +7908,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                 if (addr_family == AF_INET) {
+                     ds_put_format(&match, "ip && ip4.dst == %s",
+                                 ip_address);
+-                } else {
++                } else if (addr_family == AF_INET6) {
+                     ds_put_format(&match, "ip && ip6.dst == %s",
+                                 ip_address);
+                 }
+-- 
+2.23.0
+
diff --git a/SOURCES/0004-northd-add-empty_lb-controller_event-for-logical-rou.patch b/SOURCES/0004-northd-add-empty_lb-controller_event-for-logical-rou.patch
new file mode 100644
index 0000000..f54c103
--- /dev/null
+++ b/SOURCES/0004-northd-add-empty_lb-controller_event-for-logical-rou.patch
@@ -0,0 +1,195 @@
+From ef13dd074f46a57440c636cba618cc2bb623858a Mon Sep 17 00:00:00 2001
+Message-Id: <ef13dd074f46a57440c636cba618cc2bb623858a.1568637911.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Tue, 10 Sep 2019 19:00:58 +0200
+Subject: [PATCH ovn] northd: add empty_lb controller_event for logical router
+
+Add empty load balancer controller_event support to logical router
+pipeline. Update northd documentation even for logical switch pipeline
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ northd/ovn-northd.8.xml | 10 ++++++++
+ northd/ovn-northd.c     | 24 ++++++++++++------
+ tests/ovn.at            | 56 +++++++++++++++++++++++++++++++++++------
+ 3 files changed, 76 insertions(+), 14 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1748,6 +1748,16 @@ icmp6 {
+ 
+     <ul>
+       <li>
++        If controller_event has been enabled for all the configured load
++        balancing rules for a Gateway router or Router with gateway port
++        in <code>OVN_Northbound</code> database that does not have configured
++        backends, a priority-130 flow is added to trigger ovn-controller events
++        whenever the chassis receives a packet for that particular VIP.
++        If <code>event-elb</code> meter has been previously created, it will be
++        associated to the empty_lb logical flow
++      </li>
++
++      <li>
+         For all the configured load balancing rules for a Gateway router or
+         Router with gateway port in <code>OVN_Northbound</code> database that
+         includes a L4 port <var>PORT</var> of protocol <var>P</var> and IPv4
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -5998,9 +5998,17 @@ get_force_snat_ip(struct ovn_datapath *o
+ static void
+ add_router_lb_flow(struct hmap *lflows, struct ovn_datapath *od,
+                    struct ds *match, struct ds *actions, int priority,
+-                   const char *lb_force_snat_ip, char *backend_ips,
+-                   bool is_udp, int addr_family)
++                   const char *lb_force_snat_ip, struct smap_node *lb_info,
++                   bool is_udp, int addr_family, char *ip_addr,
++                   uint16_t l4_port, struct nbrec_load_balancer *lb,
++                   struct shash *meter_groups)
+ {
++    char *backend_ips = lb_info->value;
++
++    build_empty_lb_event_flow(od, lflows, lb_info, ip_addr, lb,
++                              l4_port, addr_family, S_ROUTER_IN_DNAT,
++                              meter_groups);
++
+     /* A match and actions for new connections. */
+     char *new_match = xasprintf("ct.new && %s", ds_cstr(match));
+     if (lb_force_snat_ip) {
+@@ -6158,7 +6166,7 @@ copy_ra_to_sb(struct ovn_port *op, const
+ 
+ static void
+ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+-                    struct hmap *lflows)
++                    struct hmap *lflows, struct shash *meter_groups)
+ {
+     /* This flow table structure is documented in ovn-northd(8), so please
+      * update ovn-northd.8.xml if you change anything. */
+@@ -7371,7 +7379,6 @@ build_lrouter_flows(struct hmap *datapat
+                     ds_put_format(&match, "ip && ip6.dst == %s",
+                                 ip_address);
+                 }
+-                free(ip_address);
+ 
+                 int prio = 110;
+                 bool is_udp = lb->protocol && !strcmp(lb->protocol, "udp") ?
+@@ -7392,8 +7399,11 @@ build_lrouter_flows(struct hmap *datapat
+                                   od->l3redirect_port->json_key);
+                 }
+                 add_router_lb_flow(lflows, od, &match, &actions, prio,
+-                                   lb_force_snat_ip, node->value, is_udp,
+-                                   addr_family);
++                                   lb_force_snat_ip, node, is_udp,
++                                   addr_family, ip_address, port, lb,
++                                   meter_groups);
++
++                free(ip_address);
+             }
+         }
+         sset_destroy(&all_ips);
+@@ -8099,7 +8109,7 @@ build_lflows(struct northd_context *ctx,
+ 
+     build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups,
+                         igmp_groups, meter_groups);
+-    build_lrouter_flows(datapaths, ports, &lflows);
++    build_lrouter_flows(datapaths, ports, &lflows, meter_groups);
+ 
+     /* Push changes to the Logical_Flow table to database. */
+     const struct sbrec_logical_flow *sbflow, *next_sbflow;
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14693,9 +14693,22 @@ ovn_start
+ # Create hypervisors hv[12].
+ # Add vif1[12] to hv1, vif2[12] to hv2
+ # Add all of the vifs to a single logical switch sw0.
++# Create logical router lr0
+ 
+ net_add n1
+-ovn-nbctl ls-add sw0
++
++ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
++for i in 0 1; do
++    idx=$((i+1))
++    ovn-nbctl ls-add sw$i
++    ovn-nbctl lrp-add lr0 lrp$i 00:00:00:00:ff:0$idx 192.168.$idx.254/24
++    ovn-nbctl \
++        -- lsp-add sw$i lrp$i-attachment \
++        -- set Logical_Switch_Port lrp$i-attachment type=router \
++                         options:router-port=lrp$i \
++                         addresses='"00:00:00:00:ff:'0$idx'"'
++done
++
+ for i in 1 2; do
+     sim_add hv$i
+     as hv$i
+@@ -14715,10 +14728,24 @@ for i in 1 2; do
+     done
+ done
+ 
++as hv1
++ovn-nbctl lsp-add sw1 sw1-p0 \
++    -- lsp-set-addresses sw1-p0 "00:00:00:00:00:33 192.168.2.11"
++ovs-vsctl -- add-port br-int vif33 -- \
++        set interface vif33 \
++        external-ids:iface-id=sw1-p0 \
++        options:tx_pcap=hv$i/vif33-tx.pcap \
++        options:rxq_pcap=hv$i/vif33-rx.pcap \
++        ofport-request=33
++
+ ovn-nbctl --wait=hv set NB_Global . options:controller_event=true
+ ovn-nbctl lb-add lb0 192.168.1.100:80 ""
+ ovn-nbctl ls-lb-add sw0 lb0
+-uuid_lb=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0)
++uuid_lb0=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0)
++
++ovn-nbctl lb-add lb1 192.168.2.100:80 ""
++ovn-nbctl lr-lb-add lr0 lb1
++uuid_lb1=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb1)
+ ovn-nbctl --wait=hv meter-add event-elb drop 100 pktps 10
+ 
+ OVN_POPULATE_ARP
+@@ -14726,10 +14753,10 @@ ovn-nbctl --timeout=3 --wait=hv sync
+ ovn-sbctl lflow-list
+ as hv1 ovs-ofctl dump-flows br-int
+ 
+-packet="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 &&
+-       ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.1.100 &&
+-       tcp && tcp.src==10000 && tcp.dst==80"
+-as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
++packet0="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 &&
++         ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.1.100 &&
++         tcp && tcp.src==10000 && tcp.dst==80"
++as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet0"
+ 
+ ovn-sbctl list controller_event
+ uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}')
+@@ -14743,12 +14770,27 @@ AT_CHECK([ovn-sbctl get controller_event
+ tcp
+ ])
+ AT_CHECK_UNQUOTED([ovn-sbctl get controller_event $uuid event_info:load_balancer], [0], [dnl
+-"$uuid_lb"
++"$uuid_lb0"
+ ])
+ AT_CHECK([ovn-sbctl get controller_event $uuid seq_num], [0], [dnl
+ 1
+ ])
+ 
++ovn-sbctl destroy controller_event $uuid
++packet1="inport==\"sw1-p0\" && eth.src==00:00:00:00:00:33 && eth.dst==00:00:00:00:ff:02 &&
++         ip4 && ip.ttl==64 && ip4.src==192.168.2.11 && ip4.dst==192.168.2.100 &&
++         tcp && tcp.src==10000 && tcp.dst==80"
++
++as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet1"
++ovn-sbctl list controller_event
++uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}')
++AT_CHECK([ovn-sbctl get controller_event $uuid event_type], [0], [dnl
++empty_lb_backends
++])
++AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl
++"192.168.2.100:80"
++])
++
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+ 
diff --git a/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch b/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch
new file mode 100644
index 0000000..6722a76
--- /dev/null
+++ b/SOURCES/0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch
@@ -0,0 +1,185 @@
+From 92f6a2f668708c677a8b10b0ac861bfd712f6a20 Mon Sep 17 00:00:00 2001
+Message-Id: <92f6a2f668708c677a8b10b0ac861bfd712f6a20.1590585469.git.lorenzo.bianconi@redhat.com>
+In-Reply-To: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
+References: <d9ed450713eda62af1bec5009694b2d206c9f435.1590585469.git.lorenzo.bianconi@redhat.com>
+From: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Date: Mon, 25 May 2020 18:31:27 +0200
+Subject: [PATCH ovn 3/3] northd: manage ARP request locally for FIP traffic
+
+Modify 100-priority logical flows in Gateway Redirect table of
+logical router ingress pipeline (table 15) in order to manage ARP
+request locally for FIP traffic. In particular set reg1 and eth.src
+to NAT external ip and NAT external mac respectively and do not
+distribute ARP traffic using FIP
+
+Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+---
+ ovn/northd/ovn-northd.8.xml | 10 +++++++---
+ ovn/northd/ovn-northd.c     | 23 ++++++++++++++++-------
+ tests/ovn.at            | 28 +++++++++++++++++++++++++---
+ tests/system-ovn.at     | 30 ++++++++++++++++++++++++++++++
+ 4 files changed, 78 insertions(+), 13 deletions(-)
+
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -2632,9 +2632,13 @@ icmp4 {
+         For each NAT rule in the OVN Northbound database that can
+         be handled in a distributed manner, a priority-100 logical
+         flow with match <code>ip4.src == <var>B</var> &amp;&amp;
+-        outport == <var>GW</var></code>, where <var>GW</var> is
+-        the logical router distributed gateway port, with actions
+-        <code>next;</code>.
++        outport == <var>GW</var></code> &amp;&amp;
++        is_chassis_resident(<var>P</var>), where <var>GW</var> is
++        the logical router distributed gateway port and <var>P</var>
++        is the NAT logical port. IP traffic matching the above rule
++        will be managed locally setting <code>reg1</code> to <var>C</var>
++        and <code>eth.src</code> to <var>D</var>, where <var>C</var> is NAT
++        external ip and <var>D</var> is NAT external mac.
+       </li>
+ 
+       <li>
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -8306,15 +8306,24 @@ build_lrouter_flows(struct hmap *datapat
+             /* Ingress Gateway Redirect Table: For NAT on a distributed
+              * router, add flows that are specific to a NAT rule.  These
+              * flows indicate the presence of an applicable NAT rule that
+-             * can be applied in a distributed manner. */
++             * can be applied in a distributed manner.
++             * In particulr reg1 and eth.src are set to NAT external IP and
++             * NAT external mac so the ARP request generated in the following
++             * stage is sent out with proper IP/MAC src addresses
++             */
+             if (distributed) {
+                 ds_clear(&match);
+-                ds_put_format(&match, "ip%s.src == %s && outport == %s",
+-                              is_v6 ? "6" : "4",
+-                              nat->logical_ip,
+-                              od->l3dgw_port->json_key);
++                ds_clear(&actions);
++                ds_put_format(&match,
++                              "ip%s.src == %s && outport == %s && "
++                              "is_chassis_resident(\"%s\")",
++                              is_v6 ? "6" : "4", nat->logical_ip,
++                              od->l3dgw_port->json_key, nat->logical_port);
++                ds_put_format(&actions, "eth.src = %s; %sreg1 = %s; next;",
++                              nat->external_mac, is_v6 ? "xx" : "",
++                              nat->external_ip);
+                 ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
+-                              ds_cstr(&match), "next;");
++                              ds_cstr(&match), ds_cstr(&actions));
+             }
+ 
+             /* Egress Loopback table: For NAT on a distributed router.
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14019,9 +14019,14 @@ ovs-vsctl -- add-port br-int hv2-vif1 --
+     set interface hv2-vif1 external-ids:iface-id=sw1-p0 \
+     options:tx_pcap=hv2/vif1-tx.pcap \
+     options:rxq_pcap=hv2/vif1-rx.pcap \
+-    ofport-request=1
++    ofport-request=2
++ovs-vsctl -- add-port br-int hv2-vif2 -- \
++    set interface hv2-vif2 external-ids:iface-id=sw0-p1 \
++    options:tx_pcap=hv2/vif2-tx.pcap \
++    options:rxq_pcap=hv2/vif2-rx.pcap \
++    ofport-request=3
+ 
+-ovn-nbctl create Logical_Router name=lr0 options:chassis=hv1
++ovn-nbctl create Logical_Router name=lr0
+ ovn-nbctl ls-add sw0
+ ovn-nbctl ls-add sw1
+ 
+@@ -14030,13 +14035,16 @@ ovn-nbctl lsp-add sw0 rp-sw0 -- set Logi
+     type=router options:router-port=sw0 \
+     -- lsp-set-addresses rp-sw0 router
+ 
+-ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64
++ovn-nbctl lrp-add lr0 sw1 00:00:02:01:02:03 172.16.1.1/24 2002:0:0:0:0:0:0:1/64 \
++    -- set Logical_Router_Port sw1 options:redirect-chassis="hv2"
+ ovn-nbctl lsp-add sw1 rp-sw1 -- set Logical_Switch_Port rp-sw1 \
+     type=router options:router-port=sw1 \
+     -- lsp-set-addresses rp-sw1 router
+ 
+ ovn-nbctl lsp-add sw0 sw0-p0 \
+     -- lsp-set-addresses sw0-p0 "f0:00:00:01:02:03 192.168.1.2 2001::2"
++ovn-nbctl lsp-add sw0 sw0-p1 \
++    -- lsp-set-addresses sw0-p1 "f0:00:00:11:02:03 192.168.1.3 2001::3"
+ 
+ ovn-nbctl lsp-add sw1 sw1-p0 \
+     -- lsp-set-addresses sw1-p0 unknown
+@@ -14082,6 +14090,20 @@ send_na 2 1 $dst_mac $router_mac1 $dst_i
+ 
+ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
+ 
++# Create FIP on sw0-p0, add a route on logical router pipeline and
++# ARP request for a unkwon destination is sent using FIP MAC/IP
++ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.16.1.2 192.168.1.3 sw0-p1 f0:00:00:01:02:04
++ovn-nbctl lr-route-add lr0 172.16.2.0/24 172.16.1.11
++
++dst_ip=$(ip_to_hex 172 16 2 10)
++fip_ip=$(ip_to_hex 172 16 1 2)
++src_ip=$(ip_to_hex 192 168 1 3)
++gw_router=$(ip_to_hex 172 16 1 11)
++send_icmp_packet 2 2 f00000110203 $router_mac0 $src_ip $dst_ip 0000 $data
++echo $(get_arp_req f00000010204 $fip_ip $gw_router) >> expected
++
++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected])
++
+ OVN_CLEANUP([hv1],[hv2])
+ AT_CLEANUP
+ 
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -1824,6 +1824,19 @@ ADD_VETH(alice1, alice1, br-int, "172.16
+ ovn-nbctl lsp-add alice alice1 \
+ -- lsp-set-addresses alice1 "f0:00:00:01:02:05 172.16.1.2"
+ 
++# Add external network
++ADD_NAMESPACES(ext-net)
++AT_CHECK([ip link add alice-ext netns alice1 type veth peer name ext-veth netns ext-net])
++NS_CHECK_EXEC([ext-net], [ip link set dev ext-veth up], [0], [])
++NS_CHECK_EXEC([ext-net], [ip addr add 10.0.0.1/24 dev ext-veth], [0], [])
++NS_CHECK_EXEC([ext-net], [ip route add default via 10.0.0.2], [0], [])
++
++NS_CHECK_EXEC([alice1], [ip link set dev alice-ext up], [0], [])
++NS_CHECK_EXEC([alice1], [ip addr add 10.0.0.2/24 dev alice-ext], [0], [])
++NS_CHECK_EXEC([alice1], [sysctl -w net.ipv4.conf.all.forwarding=1],[0], [dnl
++net.ipv4.conf.all.forwarding = 1
++])
++
+ # Add DNAT rules
+ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.3 192.168.1.2 foo1 00:00:02:02:03:04])
+ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat 172.16.1.4 192.168.1.3 foo2 00:00:02:02:03:05])
+@@ -1831,6 +1844,9 @@ AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_a
+ # Add a SNAT rule
+ AT_CHECK([ovn-nbctl lr-nat-add R1 snat 172.16.1.1 192.168.0.0/16])
+ 
++# Add default route to ext-net
++AT_CHECK([ovn-nbctl lr-route-add R1 10.0.0.0/24 172.16.1.2])
++
+ ovn-nbctl --wait=hv sync
+ OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=172.16.1.1)'])
+ 
+@@ -1874,6 +1890,20 @@ sed -e 's/zone=[[0-9]]*/zone=<cleared>/'
+ icmp,orig=(src=192.168.2.2,dst=172.16.1.2,id=<cleared>,type=8,code=0),reply=(src=172.16.1.2,dst=172.16.1.1,id=<cleared>,type=0,code=0),zone=<cleared>
+ ])
+ 
++# Try to ping external network
++NS_CHECK_EXEC([ext-net], [tcpdump -n -c 3 -i ext-veth dst 172.16.1.3 and icmp > ext-net.pcap &])
++sleep 1
++AT_CHECK([ovn-nbctl lr-nat-del R1 snat])
++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 10.0.0.1 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++OVS_WAIT_UNTIL([
++    total_pkts=$(cat ext-net.pcap | wc -l)
++    test "${total_pkts}" = "3"
++])
++
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+ 
+ as ovn-sb
diff --git a/SOURCES/0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch b/SOURCES/0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch
new file mode 100644
index 0000000..f445979
--- /dev/null
+++ b/SOURCES/0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch
@@ -0,0 +1,1264 @@
+From 85136b292d698a76c5317ec10622bcc015165770 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Wed, 4 Dec 2019 17:28:28 +0100
+Subject: [PATCH ovn 4/4] ovn-controller: Fix use of dangling pointers in I-P
+ runtime_data.
+
+The incremental processing engine might stop a run before the
+en_runtime_data node is processed. In such cases the ed_runtime_data
+fields might contain pointers to already deleted SB records. For
+example, if a port binding corresponding to a patch port is removed from
+the SB database and the incremental processing engine aborts before the
+en_runtime_data node is processed then the corresponding local_datapath
+hashtable entry in ed_runtime_data is stale and will store a pointer to
+the already freed sbrec_port_binding record.
+
+This will cause invalid memory accesses in various places (e.g.,
+pinctrl_run() -> prepare_ipv6_ras()).
+
+To fix the issue we introduce the engine_get_data() API which must be
+called in order to safely access internal node data. If the node is in
+state EN_STALE or EN_ABORTED, engine_get_data() returns NULL as the
+references might be stale.
+
+This commit also adds an "is_valid()" method to engine nodes to allow
+users to override the default behavior of determining if data is valid in a
+node (e.g., for the ct-zones node the data is always safe to access).
+
+Also, all interactions with node data outside inc-proc-eng.c are now
+performed through APIs and never by directly accessing the node->data
+field. This makes it easier to ensure that we don't access invalid
+(stale) data.
+
+CC: Han Zhou <hzhou@ovn.org>
+Fixes: ca278d98a4f5 ("ovn-controller: Initial use of incremental engine - quiet mode.")
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Signed-off-by: Han Zhou <hzhou@ovn.org>
+
+(cherry picked from upstream commit 94cbc59dc0f1cb56e56d1551956efe5824561864)
+
+Change-Id: Icdb3d38e4b43e36c4c1924509e2461707cdb57e6
+---
+ ovn/controller/ovn-controller.c | 457 +++++++++++++++++++++-------------------
+ ovn/lib/inc-proc-eng.c          |  59 +++++-
+ ovn/lib/inc-proc-eng.h          | 115 +++++++---
+ 3 files changed, 370 insertions(+), 261 deletions(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index 20e5ce4..cb23bc8 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -702,26 +702,25 @@ struct ed_type_ofctrl_is_connected {
+     bool connected;
+ };
+ 
+-static void
+-en_ofctrl_is_connected_init(struct engine_node *node)
++static void *
++en_ofctrl_is_connected_init(struct engine_node *node OVS_UNUSED,
++                            struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_ofctrl_is_connected *data =
+-        (struct ed_type_ofctrl_is_connected *)node->data;
+-    data->connected = false;
++    struct ed_type_ofctrl_is_connected *data = xzalloc(sizeof *data);
++    return data;
+ }
+ 
+ static void
+-en_ofctrl_is_connected_cleanup(struct engine_node *node OVS_UNUSED)
++en_ofctrl_is_connected_cleanup(void *data OVS_UNUSED)
+ {
+ }
+ 
+ static void
+-en_ofctrl_is_connected_run(struct engine_node *node)
++en_ofctrl_is_connected_run(struct engine_node *node, void *data)
+ {
+-    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;
++    struct ed_type_ofctrl_is_connected *of_data = data;
++    if (of_data->connected != ofctrl_is_connected()) {
++        of_data->connected = !of_data->connected;
+         engine_set_node_state(node, EN_UPDATED);
+         return;
+     }
+@@ -736,21 +735,24 @@ struct ed_type_addr_sets {
+     struct sset updated;
+ };
+ 
+-static void
+-en_addr_sets_init(struct engine_node *node)
++static void *
++en_addr_sets_init(struct engine_node *node OVS_UNUSED,
++                  struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
++    struct ed_type_addr_sets *as = xzalloc(sizeof *as);
++
+     shash_init(&as->addr_sets);
+     as->change_tracked = false;
+     sset_init(&as->new);
+     sset_init(&as->deleted);
+     sset_init(&as->updated);
++    return as;
+ }
+ 
+ static void
+-en_addr_sets_cleanup(struct engine_node *node)
++en_addr_sets_cleanup(void *data)
+ {
+-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
++    struct ed_type_addr_sets *as = data;
+     expr_const_sets_destroy(&as->addr_sets);
+     shash_destroy(&as->addr_sets);
+     sset_destroy(&as->new);
+@@ -759,9 +761,9 @@ en_addr_sets_cleanup(struct engine_node *node)
+ }
+ 
+ static void
+-en_addr_sets_run(struct engine_node *node)
++en_addr_sets_run(struct engine_node *node, void *data)
+ {
+-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
++    struct ed_type_addr_sets *as = data;
+ 
+     sset_clear(&as->new);
+     sset_clear(&as->deleted);
+@@ -779,9 +781,9 @@ en_addr_sets_run(struct engine_node *node)
+ }
+ 
+ static bool
+-addr_sets_sb_address_set_handler(struct engine_node *node)
++addr_sets_sb_address_set_handler(struct engine_node *node, void *data)
+ {
+-    struct ed_type_addr_sets *as = (struct ed_type_addr_sets *)node->data;
++    struct ed_type_addr_sets *as = data;
+ 
+     sset_clear(&as->new);
+     sset_clear(&as->deleted);
+@@ -813,21 +815,24 @@ struct ed_type_port_groups{
+     struct sset updated;
+ };
+ 
+-static void
+-en_port_groups_init(struct engine_node *node)
++static void *
++en_port_groups_init(struct engine_node *node OVS_UNUSED,
++                    struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
++    struct ed_type_port_groups *pg = xzalloc(sizeof *pg);
++
+     shash_init(&pg->port_groups);
+     pg->change_tracked = false;
+     sset_init(&pg->new);
+     sset_init(&pg->deleted);
+     sset_init(&pg->updated);
++    return pg;
+ }
+ 
+ static void
+-en_port_groups_cleanup(struct engine_node *node)
++en_port_groups_cleanup(void *data)
+ {
+-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
++    struct ed_type_port_groups *pg = data;
+     expr_const_sets_destroy(&pg->port_groups);
+     shash_destroy(&pg->port_groups);
+     sset_destroy(&pg->new);
+@@ -836,9 +841,9 @@ en_port_groups_cleanup(struct engine_node *node)
+ }
+ 
+ static void
+-en_port_groups_run(struct engine_node *node)
++en_port_groups_run(struct engine_node *node, void *data)
+ {
+-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
++    struct ed_type_port_groups *pg = data;
+ 
+     sset_clear(&pg->new);
+     sset_clear(&pg->deleted);
+@@ -856,9 +861,9 @@ en_port_groups_run(struct engine_node *node)
+ }
+ 
+ static bool
+-port_groups_sb_port_group_handler(struct engine_node *node)
++port_groups_sb_port_group_handler(struct engine_node *node, void *data)
+ {
+-    struct ed_type_port_groups *pg = (struct ed_type_port_groups *)node->data;
++    struct ed_type_port_groups *pg = data;
+ 
+     sset_clear(&pg->new);
+     sset_clear(&pg->deleted);
+@@ -899,46 +904,45 @@ struct ed_type_runtime_data {
+     struct sset active_tunnels;
+ };
+ 
+-static void
+-en_runtime_data_init(struct engine_node *node)
++static void *
++en_runtime_data_init(struct engine_node *node OVS_UNUSED,
++                     struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_runtime_data *data =
+-        (struct ed_type_runtime_data *)node->data;
++    struct ed_type_runtime_data *data = xzalloc(sizeof *data);
+ 
+     hmap_init(&data->local_datapaths);
+     sset_init(&data->local_lports);
+     sset_init(&data->local_lport_ids);
+     sset_init(&data->active_tunnels);
++    return data;
+ }
+ 
+ static void
+-en_runtime_data_cleanup(struct engine_node *node)
++en_runtime_data_cleanup(void *data)
+ {
+-    struct ed_type_runtime_data *data =
+-        (struct ed_type_runtime_data *)node->data;
++    struct ed_type_runtime_data *rt_data = data;
+ 
+-    sset_destroy(&data->local_lports);
+-    sset_destroy(&data->local_lport_ids);
+-    sset_destroy(&data->active_tunnels);
++    sset_destroy(&rt_data->local_lports);
++    sset_destroy(&rt_data->local_lport_ids);
++    sset_destroy(&rt_data->active_tunnels);
+     struct local_datapath *cur_node, *next_node;
+     HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
+-                        &data->local_datapaths) {
++                        &rt_data->local_datapaths) {
+         free(cur_node->peer_ports);
+-        hmap_remove(&data->local_datapaths, &cur_node->hmap_node);
++        hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node);
+         free(cur_node);
+     }
+-    hmap_destroy(&data->local_datapaths);
++    hmap_destroy(&rt_data->local_datapaths);
+ }
+ 
+ static void
+-en_runtime_data_run(struct engine_node *node)
++en_runtime_data_run(struct engine_node *node, void *data)
+ {
+-    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;
++    struct ed_type_runtime_data *rt_data = 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;
+ 
+     static bool first_run = true;
+     if (first_run) {
+@@ -981,8 +985,7 @@ en_runtime_data_run(struct engine_node *node)
+     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;
++        engine_get_input_data("ofctrl_is_connected", node);
+     if (ed_ofctrl_is_connected->connected) {
+         /* Calculate the active tunnels only if have an an active
+          * OpenFlow connection to br-int.
+@@ -1036,12 +1039,11 @@ en_runtime_data_run(struct engine_node *node)
+ }
+ 
+ static bool
+-runtime_data_sb_port_binding_handler(struct engine_node *node)
++runtime_data_sb_port_binding_handler(struct engine_node *node, void *data)
+ {
+-    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 ed_type_runtime_data *rt_data = data;
++    struct sset *local_lports = &rt_data->local_lports;
++    struct sset *active_tunnels = &rt_data->active_tunnels;
+ 
+     struct ovsrec_open_vswitch_table *ovs_table =
+         (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
+@@ -1080,10 +1082,10 @@ struct ed_type_ct_zones {
+     struct simap current;
+ };
+ 
+-static void
+-en_ct_zones_init(struct engine_node *node)
++static void *
++en_ct_zones_init(struct engine_node *node, struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_ct_zones *data = node->data;
++    struct ed_type_ct_zones *data = xzalloc(sizeof *data);
+     struct ovsrec_open_vswitch_table *ovs_table =
+         (struct ovsrec_open_vswitch_table *)EN_OVSDB_GET(
+             engine_get_input("OVS_open_vswitch", node));
+@@ -1097,56 +1099,63 @@ en_ct_zones_init(struct engine_node *node)
+     memset(data->bitmap, 0, sizeof data->bitmap);
+     bitmap_set1(data->bitmap, 0); /* Zone 0 is reserved. */
+     restore_ct_zones(bridge_table, ovs_table, &data->current, data->bitmap);
++    return data;
+ }
+ 
+ static void
+-en_ct_zones_cleanup(struct engine_node *node)
++en_ct_zones_cleanup(void *data)
+ {
+-    struct ed_type_ct_zones *data = node->data;
++    struct ed_type_ct_zones *ct_zones_data = data;
+ 
+-    simap_destroy(&data->current);
+-    shash_destroy(&data->pending);
++    simap_destroy(&ct_zones_data->current);
++    shash_destroy(&ct_zones_data->pending);
+ }
+ 
+ static void
+-en_ct_zones_run(struct engine_node *node)
++en_ct_zones_run(struct engine_node *node, void *data)
+ {
+-    struct ed_type_ct_zones *data = node->data;
++    struct ed_type_ct_zones *ct_zones_data = data;
+     struct ed_type_runtime_data *rt_data =
+-        (struct ed_type_runtime_data *)engine_get_input(
+-            "runtime_data", node)->data;
++        engine_get_input_data("runtime_data", node);
+ 
+     update_ct_zones(&rt_data->local_lports, &rt_data->local_datapaths,
+-                    &data->current, data->bitmap, &data->pending);
++                    &ct_zones_data->current, ct_zones_data->bitmap,
++                    &ct_zones_data->pending);
+ 
+     engine_set_node_state(node, EN_UPDATED);
+ }
+ 
++/* The data in the ct_zones node is always valid (i.e., no stale pointers). */
++static bool
++en_ct_zones_is_valid(struct engine_node *node OVS_UNUSED)
++{
++    return true;
++}
++
+ struct ed_type_mff_ovn_geneve {
+     enum mf_field_id mff_ovn_geneve;
+ };
+ 
+-static void
+-en_mff_ovn_geneve_init(struct engine_node *node)
++static void *
++en_mff_ovn_geneve_init(struct engine_node *node OVS_UNUSED,
++                       struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_mff_ovn_geneve *data =
+-        (struct ed_type_mff_ovn_geneve *)node->data;
+-    data->mff_ovn_geneve = 0;
++    struct ed_type_mff_ovn_geneve *data = xzalloc(sizeof *data);
++    return data;
+ }
+ 
+ static void
+-en_mff_ovn_geneve_cleanup(struct engine_node *node OVS_UNUSED)
++en_mff_ovn_geneve_cleanup(void *data OVS_UNUSED)
+ {
+ }
+ 
+ static void
+-en_mff_ovn_geneve_run(struct engine_node *node)
++en_mff_ovn_geneve_run(struct engine_node *node, void *data)
+ {
+-    struct ed_type_mff_ovn_geneve *data =
+-        (struct ed_type_mff_ovn_geneve *)node->data;
++    struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve = 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;
++    if (ed_mff_ovn_geneve->mff_ovn_geneve != mff_ovn_geneve) {
++        ed_mff_ovn_geneve->mff_ovn_geneve = mff_ovn_geneve;
+         engine_set_node_state(node, EN_UPDATED);
+         return;
+     }
+@@ -1166,48 +1175,46 @@ struct ed_type_flow_output {
+     struct lflow_resource_ref lflow_resource_ref;
+ };
+ 
+-static void
+-en_flow_output_init(struct engine_node *node)
++static void *
++en_flow_output_init(struct engine_node *node OVS_UNUSED,
++                    struct engine_arg *arg OVS_UNUSED)
+ {
+-    struct ed_type_flow_output *data =
+-        (struct ed_type_flow_output *)node->data;
++    struct ed_type_flow_output *data = xzalloc(sizeof *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);
++    return data;
+ }
+ 
+ static void
+-en_flow_output_cleanup(struct engine_node *node)
++en_flow_output_cleanup(void *data)
+ {
+-    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);
++    struct ed_type_flow_output *flow_output_data = data;
++    ovn_desired_flow_table_destroy(&flow_output_data->flow_table);
++    ovn_extend_table_destroy(&flow_output_data->group_table);
++    ovn_extend_table_destroy(&flow_output_data->meter_table);
++    lflow_resource_destroy(&flow_output_data->lflow_resource_ref);
+ }
+ 
+ static void
+-en_flow_output_run(struct engine_node *node)
++en_flow_output_run(struct engine_node *node, void *data)
+ {
+     struct ed_type_runtime_data *rt_data =
+-        (struct ed_type_runtime_data *)engine_get_input(
+-            "runtime_data", node)->data;
++        engine_get_input_data("runtime_data", node);
+     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 ed_type_ct_zones *ct_zones_data =
+-        (struct ed_type_ct_zones *)engine_get_input(
+-            "ct_zones", node)->data;
++        engine_get_input_data("ct_zones", node);
+     struct simap *ct_zones = &ct_zones_data->current;
+ 
+     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+-        (struct ed_type_mff_ovn_geneve *)engine_get_input(
+-            "mff_ovn_geneve", node)->data;
++        engine_get_input_data("mff_ovn_geneve", node);
+     enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
+ 
+     struct ovsrec_open_vswitch_table *ovs_table =
+@@ -1224,12 +1231,11 @@ en_flow_output_run(struct engine_node *node)
+                 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;
++        engine_get_input_data("addr_sets", node);
+     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;
++        engine_get_input_data("port_groups", node);
+     struct shash *port_groups = &pg_data->port_groups;
+ 
+     const struct sbrec_chassis *chassis = NULL;
+@@ -1239,8 +1245,7 @@ en_flow_output_run(struct engine_node *node)
+ 
+     ovs_assert(br_int && chassis);
+ 
+-    struct ed_type_flow_output *fo =
+-        (struct ed_type_flow_output *)node->data;
++    struct ed_type_flow_output *fo = 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;
+@@ -1320,21 +1325,19 @@ en_flow_output_run(struct engine_node *node)
+ }
+ 
+ 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;
++flow_output_sb_logical_flow_handler(struct engine_node *node, void *data)
++{
++    struct ed_type_runtime_data *rt_data =
++        engine_get_input_data("runtime_data", node);
++    struct hmap *local_datapaths = &rt_data->local_datapaths;
++    struct sset *local_lport_ids = &rt_data->local_lport_ids;
++    struct sset *active_tunnels = &rt_data->active_tunnels;
+     struct ed_type_addr_sets *as_data =
+-        (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
++        engine_get_input_data("addr_sets", node);
+     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;
++        engine_get_input_data("port_groups", node);
+     struct shash *port_groups = &pg_data->port_groups;
+ 
+     struct ovsrec_open_vswitch_table *ovs_table =
+@@ -1358,8 +1361,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node)
+ 
+     ovs_assert(br_int && chassis);
+ 
+-    struct ed_type_flow_output *fo =
+-        (struct ed_type_flow_output *)node->data;
++    struct ed_type_flow_output *fo = 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;
+@@ -1403,7 +1405,7 @@ flow_output_sb_logical_flow_handler(struct engine_node *node)
+ }
+ 
+ static bool
+-flow_output_sb_mac_binding_handler(struct engine_node *node)
++flow_output_sb_mac_binding_handler(struct engine_node *node, void *data)
+ {
+     struct ovsdb_idl_index *sbrec_port_binding_by_name =
+         engine_ovsdb_node_get_index(
+@@ -1414,8 +1416,7 @@ flow_output_sb_mac_binding_handler(struct engine_node *node)
+         (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 ed_type_flow_output *fo = data;
+     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
+ 
+     lflow_handle_changed_neighbors(sbrec_port_binding_by_name,
+@@ -1426,22 +1427,19 @@ flow_output_sb_mac_binding_handler(struct engine_node *node)
+ }
+ 
+ static bool
+-flow_output_sb_port_binding_handler(struct engine_node *node)
++flow_output_sb_port_binding_handler(struct engine_node *node, void *data)
+ {
+-    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 ed_type_runtime_data *rt_data =
++        engine_get_input_data("runtime_data", node);
++    struct hmap *local_datapaths = &rt_data->local_datapaths;
++    struct sset *active_tunnels = &rt_data->active_tunnels;
+ 
+     struct ed_type_ct_zones *ct_zones_data =
+-        (struct ed_type_ct_zones *)engine_get_input(
+-            "ct_zones", node)->data;
++        engine_get_input_data("ct_zones", node);
+     struct simap *ct_zones = &ct_zones_data->current;
+ 
+     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+-        (struct ed_type_mff_ovn_geneve *)engine_get_input(
+-            "mff_ovn_geneve", node)->data;
++        engine_get_input_data("mff_ovn_geneve", node);
+     enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
+ 
+     struct ovsrec_open_vswitch_table *ovs_table =
+@@ -1463,8 +1461,7 @@ flow_output_sb_port_binding_handler(struct engine_node *node)
+     }
+     ovs_assert(br_int && chassis);
+ 
+-    struct ed_type_flow_output *fo =
+-        (struct ed_type_flow_output *)node->data;
++    struct ed_type_flow_output *fo = data;
+     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
+ 
+     struct ovsdb_idl_index *sbrec_port_binding_by_name =
+@@ -1534,21 +1531,18 @@ flow_output_sb_port_binding_handler(struct engine_node *node)
+ }
+ 
+ static bool
+-flow_output_sb_multicast_group_handler(struct engine_node *node)
++flow_output_sb_multicast_group_handler(struct engine_node *node, void *data)
+ {
+-    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 ed_type_runtime_data *rt_data =
++        engine_get_input_data("runtime_data", node);
++    struct hmap *local_datapaths = &rt_data->local_datapaths;
+ 
+     struct ed_type_ct_zones *ct_zones_data =
+-        (struct ed_type_ct_zones *)engine_get_input(
+-            "ct_zones", node)->data;
++        engine_get_input_data("ct_zones", node);
+     struct simap *ct_zones = &ct_zones_data->current;
+ 
+     struct ed_type_mff_ovn_geneve *ed_mff_ovn_geneve =
+-        (struct ed_type_mff_ovn_geneve *)engine_get_input(
+-            "mff_ovn_geneve", node)->data;
++        engine_get_input_data("mff_ovn_geneve", node);
+     enum mf_field_id mff_ovn_geneve = ed_mff_ovn_geneve->mff_ovn_geneve;
+ 
+     struct ovsrec_open_vswitch_table *ovs_table =
+@@ -1570,8 +1564,7 @@ flow_output_sb_multicast_group_handler(struct engine_node *node)
+     }
+     ovs_assert(br_int && chassis);
+ 
+-    struct ed_type_flow_output *fo =
+-        (struct ed_type_flow_output *)node->data;
++    struct ed_type_flow_output *fo = data;
+     struct ovn_desired_flow_table *flow_table = &fo->flow_table;
+ 
+     struct sbrec_multicast_group_table *multicast_group_table =
+@@ -1588,23 +1581,21 @@ flow_output_sb_multicast_group_handler(struct engine_node *node)
+ }
+ 
+ static bool
+-_flow_output_resource_ref_handler(struct engine_node *node,
+-                                 enum ref_type ref_type)
++_flow_output_resource_ref_handler(struct engine_node *node, void *data,
++                                  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_runtime_data *rt_data =
++        engine_get_input_data("runtime_data", node);
++    struct hmap *local_datapaths = &rt_data->local_datapaths;
++    struct sset *local_lport_ids = &rt_data->local_lport_ids;
++    struct sset *active_tunnels = &rt_data->active_tunnels;
+ 
+     struct ed_type_addr_sets *as_data =
+-        (struct ed_type_addr_sets *)engine_get_input("addr_sets", node)->data;
++        engine_get_input_data("addr_sets", node);
+     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;
++        engine_get_input_data("port_groups", node);
+     struct shash *port_groups = &pg_data->port_groups;
+ 
+     struct ovsrec_open_vswitch_table *ovs_table =
+@@ -1627,8 +1618,7 @@ _flow_output_resource_ref_handler(struct engine_node *node,
+ 
+     ovs_assert(br_int && chassis);
+ 
+-    struct ed_type_flow_output *fo =
+-        (struct ed_type_flow_output *)node->data;
++    struct ed_type_flow_output *fo = 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;
+@@ -1735,15 +1725,15 @@ _flow_output_resource_ref_handler(struct engine_node *node,
+ }
+ 
+ static bool
+-flow_output_addr_sets_handler(struct engine_node *node)
++flow_output_addr_sets_handler(struct engine_node *node, void *data)
+ {
+-    return _flow_output_resource_ref_handler(node, REF_TYPE_ADDRSET);
++    return _flow_output_resource_ref_handler(node, data, REF_TYPE_ADDRSET);
+ }
+ 
+ static bool
+-flow_output_port_groups_handler(struct engine_node *node)
++flow_output_port_groups_handler(struct engine_node *node, void *data)
+ {
+-    return _flow_output_resource_ref_handler(node, REF_TYPE_PORTGROUP);
++    return _flow_output_resource_ref_handler(node, data, REF_TYPE_PORTGROUP);
+ }
+ 
+ struct ovn_controller_exit_args {
+@@ -1851,15 +1841,7 @@ main(int argc, char *argv[])
+     stopwatch_create(CONTROLLER_LOOP_STOPWATCH_NAME, SW_MS);
+ 
+     /* Define inc-proc-engine nodes. */
+-    struct ed_type_ct_zones ed_ct_zones;
+-    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(ct_zones, "ct_zones");
++    ENGINE_NODE_CUSTOM_DATA(ct_zones, "ct_zones");
+     ENGINE_NODE(runtime_data, "runtime_data");
+     ENGINE_NODE(mff_ovn_geneve, "mff_ovn_geneve");
+     ENGINE_NODE(ofctrl_is_connected, "ofctrl_is_connected");
+@@ -1875,18 +1857,6 @@ main(int argc, char *argv[])
+     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,
+@@ -1935,20 +1905,45 @@ main(int argc, char *argv[])
+     engine_add_input(&en_runtime_data, &en_sb_port_binding,
+                      runtime_data_sb_port_binding_handler);
+ 
+-    engine_init(&en_flow_output);
++    struct engine_arg engine_arg = {
++        .sb_idl = ovnsb_idl_loop.idl,
++        .ovs_idl = ovs_idl_loop.idl,
++    };
++    engine_init(&en_flow_output, &engine_arg);
+ 
+-    ofctrl_init(&ed_flow_output.group_table,
+-                &ed_flow_output.meter_table,
++    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);
++
++    struct ed_type_flow_output *flow_output_data =
++        engine_get_internal_data(&en_flow_output);
++    struct ed_type_ct_zones *ct_zones_data =
++        engine_get_internal_data(&en_ct_zones);
++    struct ed_type_runtime_data *runtime_data = NULL;
++
++    ofctrl_init(&flow_output_data->group_table,
++                &flow_output_data->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);
++                             group_table_list,
++                             &flow_output_data->group_table);
+ 
+     unixctl_command_register("meter-table-list", "", 0, 0,
+-                             meter_table_list, &ed_flow_output.meter_table);
++                             meter_table_list,
++                             &flow_output_data->meter_table);
+ 
+     unixctl_command_register("ct-zone-list", "", 0, 0,
+-                             ct_zone_list, &ed_ct_zones.current);
++                             ct_zone_list,
++                             &ct_zones_data->current);
+ 
+     struct pending_pkt pending_pkt = { .conn = NULL };
+     unixctl_command_register("inject-pkt", "MICROFLOW", 1, 1, inject_pkt,
+@@ -2024,7 +2019,10 @@ main(int argc, char *argv[])
+             }
+ 
+             if (br_int) {
+-                ofctrl_run(br_int, &ed_ct_zones.pending);
++                ct_zones_data = engine_get_data(&en_ct_zones);
++                if (ct_zones_data) {
++                    ofctrl_run(br_int, &ct_zones_data->pending);
++                }
+ 
+                 if (chassis) {
+                     patch_run(ovs_idl_txn,
+@@ -2064,41 +2062,50 @@ main(int argc, char *argv[])
+                     }
+                     stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME,
+                                    time_msec());
++                    ct_zones_data = engine_get_data(&en_ct_zones);
+                     if (ovs_idl_txn) {
+-                        commit_ct_zones(br_int, &ed_ct_zones.pending);
++                        if (ct_zones_data) {
++                            commit_ct_zones(br_int, &ct_zones_data->pending);
++                        }
+                         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_ct_zones.pending,
+-                               sbrec_meter_table_get(ovnsb_idl_loop.idl),
+-                               get_nb_cfg(sbrec_sb_global_table_get(
+-                                              ovnsb_idl_loop.idl)),
+-                               engine_node_changed(&en_flow_output));
+-                    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),
+-                                sbrec_service_monitor_table_get(
+-                                    ovnsb_idl_loop.idl),
+-                                br_int, chassis,
+-                                &ed_runtime_data.local_datapaths,
+-                                &ed_runtime_data.active_tunnels);
+ 
+-                    if (engine_node_changed(&en_runtime_data)) {
+-                        update_sb_monitors(ovnsb_idl_loop.idl, chassis,
+-                                           &ed_runtime_data.local_lports,
+-                                           &ed_runtime_data.local_datapaths);
++                    flow_output_data = engine_get_data(&en_flow_output);
++                    if (flow_output_data && ct_zones_data) {
++                        ofctrl_put(&flow_output_data->flow_table,
++                                   &ct_zones_data->pending,
++                                   sbrec_meter_table_get(ovnsb_idl_loop.idl),
++                                   get_nb_cfg(sbrec_sb_global_table_get(
++                                                   ovnsb_idl_loop.idl)),
++                                   engine_node_changed(&en_flow_output));
++                    }
++                    runtime_data = engine_get_data(&en_runtime_data);
++                    if (runtime_data) {
++                        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),
++                                    sbrec_service_monitor_table_get(
++                                        ovnsb_idl_loop.idl),
++                                    br_int, chassis,
++                                    &runtime_data->local_datapaths,
++                                    &runtime_data->active_tunnels);
++                        if (engine_node_changed(&en_runtime_data)) {
++                            update_sb_monitors(ovnsb_idl_loop.idl, chassis,
++                                               &runtime_data->local_lports,
++                                               &runtime_data->local_datapaths);
++                        }
+                     }
+                 }
+ 
+@@ -2133,9 +2140,13 @@ main(int argc, char *argv[])
+ 
+ 
+             if (pending_pkt.conn) {
+-                if (br_int && chassis) {
++                struct ed_type_addr_sets *as_data =
++                    engine_get_data(&en_addr_sets);
++                struct ed_type_port_groups *pg_data =
++                    engine_get_data(&en_port_groups);
++                if (br_int && chassis && as_data && pg_data) {
+                     char *error = ofctrl_inject_pkt(br_int, pending_pkt.flow_s,
+-                        &ed_addr_sets.addr_sets, &ed_port_groups.port_groups);
++                        &as_data->addr_sets, &pg_data->port_groups);
+                     if (error) {
+                         unixctl_command_reply_error(pending_pkt.conn, error);
+                         free(error);
+@@ -2173,12 +2184,16 @@ main(int argc, char *argv[])
+         }
+ 
+         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_ct_zones.pending) {
+-                struct ct_zone_pending_entry *ctzpe = iter->data;
+-                if (ctzpe->state == CT_ZONE_DB_SENT) {
+-                    shash_delete(&ed_ct_zones.pending, iter);
+-                    free(ctzpe);
++            ct_zones_data = engine_get_data(&en_ct_zones);
++            if (ct_zones_data) {
++                struct shash_node *iter, *iter_next;
++                SHASH_FOR_EACH_SAFE (iter, iter_next,
++                                     &ct_zones_data->pending) {
++                    struct ct_zone_pending_entry *ctzpe = iter->data;
++                    if (ctzpe->state == CT_ZONE_DB_SENT) {
++                        shash_delete(&ct_zones_data->pending, iter);
++                        free(ctzpe);
++                    }
+                 }
+             }
+         }
+diff --git a/ovn/lib/inc-proc-eng.c b/ovn/lib/inc-proc-eng.c
+index 59b5cac..9b1479a 100644
+--- a/ovn/lib/inc-proc-eng.c
++++ b/ovn/lib/inc-proc-eng.c
+@@ -103,13 +103,16 @@ engine_get_nodes(struct engine_node *node, size_t *n_count)
+ }
+ 
+ void
+-engine_init(struct engine_node *node)
++engine_init(struct engine_node *node, struct engine_arg *arg)
+ {
+     engine_nodes = engine_get_nodes(node, &engine_n_nodes);
+ 
+     for (size_t i = 0; i < engine_n_nodes; i++) {
+         if (engine_nodes[i]->init) {
+-            engine_nodes[i]->init(engine_nodes[i]);
++            engine_nodes[i]->data =
++                engine_nodes[i]->init(engine_nodes[i], arg);
++        } else {
++            engine_nodes[i]->data = NULL;
+         }
+     }
+ }
+@@ -119,8 +122,9 @@ engine_cleanup(void)
+ {
+     for (size_t i = 0; i < engine_n_nodes; i++) {
+         if (engine_nodes[i]->cleanup) {
+-            engine_nodes[i]->cleanup(engine_nodes[i]);
++            engine_nodes[i]->cleanup(engine_nodes[i]->data);
+         }
++        free(engine_nodes[i]->data);
+     }
+     free(engine_nodes);
+     engine_nodes = NULL;
+@@ -140,9 +144,16 @@ engine_get_input(const char *input_name, struct engine_node *node)
+     return NULL;
+ }
+ 
++void *
++engine_get_input_data(const char *input_name, struct engine_node *node)
++{
++    struct engine_node *input_node = engine_get_input(input_name, node);
++    return engine_get_data(input_node);
++}
++
+ void
+ engine_add_input(struct engine_node *node, struct engine_node *input,
+-                 bool (*change_handler)(struct engine_node *))
++                 bool (*change_handler)(struct engine_node *, void *))
+ {
+     ovs_assert(node->n_inputs < ENGINE_MAX_INPUT);
+     node->inputs[node->n_inputs].node = input;
+@@ -153,7 +164,7 @@ engine_add_input(struct engine_node *node, struct engine_node *input,
+ struct ovsdb_idl_index *
+ engine_ovsdb_node_get_index(struct engine_node *node, const char *name)
+ {
+-    struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data;
++    struct ed_type_ovsdb_table *ed = node->data;
+     for (size_t i = 0; i < ed->n_indexes; i++) {
+         if (!strcmp(ed->indexes[i].name, name)) {
+             return ed->indexes[i].index;
+@@ -167,7 +178,7 @@ void
+ engine_ovsdb_node_add_index(struct engine_node *node, const char *name,
+                             struct ovsdb_idl_index *index)
+ {
+-    struct ed_type_ovsdb_table *ed = (struct ed_type_ovsdb_table *)node->data;
++    struct ed_type_ovsdb_table *ed = node->data;
+     ovs_assert(ed->n_indexes < ENGINE_MAX_OVSDB_INDEX);
+ 
+     ed->indexes[ed->n_indexes].name = name;
+@@ -192,6 +203,19 @@ engine_set_node_state_at(struct engine_node *node,
+     node->state = state;
+ }
+ 
++static bool
++engine_node_valid(struct engine_node *node)
++{
++    if (node->state == EN_UPDATED || node->state == EN_VALID) {
++        return true;
++    }
++
++    if (node->is_valid) {
++        return node->is_valid(node);
++    }
++    return false;
++}
++
+ bool
+ engine_node_changed(struct engine_node *node)
+ {
+@@ -215,6 +239,21 @@ engine_aborted(void)
+     return engine_run_aborted;
+ }
+ 
++void *
++engine_get_data(struct engine_node *node)
++{
++    if (engine_node_valid(node)) {
++        return node->data;
++    }
++    return NULL;
++}
++
++void *
++engine_get_internal_data(struct engine_node *node)
++{
++    return node->data;
++}
++
+ void
+ engine_init_run(void)
+ {
+@@ -240,7 +279,7 @@ engine_recompute(struct engine_node *node, bool forced, bool allowed)
+     }
+ 
+     /* Run the node handler which might change state. */
+-    node->run(node);
++    node->run(node, node->data);
+ }
+ 
+ /* Return true if the node could be computed, false otherwise. */
+@@ -256,7 +295,7 @@ engine_compute(struct engine_node *node, bool recompute_allowed)
+             /* If the input change can't be handled incrementally, run
+              * the node handler.
+              */
+-            if (!node->inputs[i].change_handler(node)) {
++            if (!node->inputs[i].change_handler(node, node->data)) {
+                 VLOG_DBG("node: %s, can't handle change for input %s, "
+                          "fall back to recompute",
+                          node->name, node->inputs[i].node->name);
+@@ -273,7 +312,7 @@ engine_run_node(struct engine_node *node, bool recompute_allowed)
+ {
+     if (!node->n_inputs) {
+         /* Run the node handler which might change state. */
+-        node->run(node);
++        node->run(node, node->data);
+         return;
+     }
+ 
+@@ -345,7 +384,7 @@ engine_need_run(void)
+             continue;
+         }
+ 
+-        engine_nodes[i]->run(engine_nodes[i]);
++        engine_nodes[i]->run(engine_nodes[i], engine_nodes[i]->data);
+         VLOG_DBG("input node: %s, state: %s", engine_nodes[i]->name,
+                  engine_node_state_name[engine_nodes[i]->state]);
+         if (engine_nodes[i]->state == EN_UPDATED) {
+diff --git a/ovn/lib/inc-proc-eng.h b/ovn/lib/inc-proc-eng.h
+index 2f3ff62..9cc99a3 100644
+--- a/ovn/lib/inc-proc-eng.h
++++ b/ovn/lib/inc-proc-eng.h
+@@ -68,6 +68,12 @@ struct engine_context {
+     struct ovsdb_idl_txn *ovnsb_idl_txn;
+ };
+ 
++/* Arguments to be passed to the engine at engine_init(). */
++struct engine_arg {
++    struct ovsdb_idl *sb_idl;
++    struct ovsdb_idl *ovs_idl;
++};
++
+ struct engine_node;
+ 
+ struct engine_node_input {
+@@ -79,7 +85,7 @@ struct engine_node_input {
+      *  - true: if change can be handled
+      *  - false: if change cannot be handled (indicating full recompute needed)
+      */
+-    bool (*change_handler)(struct engine_node *node);
++    bool (*change_handler)(struct engine_node *node, void *data);
+ };
+ 
+ enum engine_node_state {
+@@ -106,30 +112,42 @@ struct engine_node {
+     /* Inputs of this node. */
+     struct engine_node_input inputs[ENGINE_MAX_INPUT];
+ 
+-    /* Data of this node. It is vague and interpreted by the related functions.
+-     * The content of the data should be changed only by the change_handlers
+-     * and run() function of the current node. Users should ensure that the
+-     * data is read-only in change-handlers of the nodes that depends on this
+-     * node. */
++    /* A pointer to node internal data. The data is safely accessible to
++     * users through the engine_get_data() API. For special cases, when the
++     * data is known to be valid (e.g., at init time), users can also call
++     * engine_get_internal_data().
++     */
+     void *data;
+ 
+     /* State of the node after the last engine run. */
+     enum engine_node_state state;
+ 
+-    /* Method to initialize data. It may be NULL. */
+-    void (*init)(struct engine_node *);
++    /* Method to allocate and initialize node data. It may be NULL.
++     * The user supplied argument 'arg' is passed from the call to
++     * engine_init().
++     */
++    void *(*init)(struct engine_node *node, struct engine_arg *arg);
+ 
+     /* Method to clean up data. It may be NULL. */
+-    void (*cleanup)(struct engine_node *);
++    void (*cleanup)(void *data);
+ 
+     /* Fully processes all inputs of this node and regenerates the data
+-     * of this node */
+-    void (*run)(struct engine_node *);
++     * of this node. The pointer to the node's data is passed as argument.
++     */
++    void (*run)(struct engine_node *node, void *data);
++
++    /* Method to validate if the 'internal_data' is valid. This allows users
++     * to customize when 'data' can be used (e.g., even if the node
++     * hasn't been refreshed in the last iteration, if 'data'
++     * doesn't store pointers to DB records it's still safe to use).
++     */
++    bool (*is_valid)(struct engine_node *);
+ };
+ 
+ /* Initialize the data for the engine nodes. It calls each node's
+- * init() method if not NULL. It should be called before the main loop. */
+-void engine_init(struct engine_node *node);
++ * init() method if not NULL passing the user supplied 'arg'.
++ * It should be called before the main loop. */
++void engine_init(struct engine_node *node, struct engine_arg *arg);
+ 
+ /* Initialize the engine nodes for a new run. It should be called in the
+  * main processing loop before every potential engine_run().
+@@ -155,12 +173,15 @@ bool engine_need_run(void);
+ struct engine_node * engine_get_input(const char *input_name,
+                                       struct engine_node *);
+ 
++/* Get the data from the input node with <name> for <node> */
++void *engine_get_input_data(const char *input_name, struct engine_node *);
++
+ /* Add an input (dependency) for <node>, with corresponding change_handler,
+  * which can be NULL. If the change_handler is NULL, the engine will not
+  * be able to process the change incrementally, and will fall back to call
+  * the run method to recompute. */
+ void engine_add_input(struct engine_node *node, struct engine_node *input,
+-                      bool (*change_handler)(struct engine_node *));
++                      bool (*change_handler)(struct engine_node *, void *));
+ 
+ /* Force the engine to recompute everything if set to true. It is used
+  * in circumstances when we are not sure there is change or not, or
+@@ -185,6 +206,25 @@ bool engine_has_run(void);
+ /* Returns true if during the last engine run we had to abort processing. */
+ bool engine_aborted(void);
+ 
++/* Return a pointer to node data accessible for users outside the processing
++ * engine. If the node data is not valid (e.g., last engine_run() failed or
++ * didn't happen), the node's is_valid() method is used to determine if the
++ * data can be safely accessed. If it's not the case, the function returns
++ * NULL.
++ * The content of the data should be changed only by the change_handlers
++ * and run() function of the current node. Users should ensure that the
++ * data is read-only in change-handlers of the nodes that depends on this
++ * node.
++ */
++void *engine_get_data(struct engine_node *node);
++
++/* Return a pointer to node data *without* performing any sanity checks on
++ * the state of the node. This may be used only in specific cases when data
++ * is guaranteed to be valid, e.g., immediately after initialization and
++ * before the first engine_run().
++ */
++void *engine_get_internal_data(struct engine_node *node);
++
+ /* Set the state of the node and log changes. */
+ #define engine_set_node_state(node, state) \
+     engine_set_node_state_at(node, state, OVS_SOURCE_LOCATOR)
+@@ -201,30 +241,42 @@ struct ed_type_ovsdb_table {
+ };
+ 
+ #define EN_OVSDB_GET(NODE) \
+-    (((struct ed_type_ovsdb_table *)NODE->data)->table)
++    (((struct ed_type_ovsdb_table *)(NODE)->data)->table)
+ 
+ struct ovsdb_idl_index * engine_ovsdb_node_get_index(struct engine_node *,
+                                                      const char *name);
+ 
++/* Adds an OVSDB IDL index to the node. This should be called only after
++ * engine_init() as the index is stored in the node data.
++ */
+ void engine_ovsdb_node_add_index(struct engine_node *, const char *name,
+                                  struct ovsdb_idl_index *);
+ 
+ /* Macro to define an engine node. */
+-#define ENGINE_NODE(NAME, NAME_STR) \
++#define ENGINE_NODE_DEF(NAME, NAME_STR) \
+     struct engine_node en_##NAME = { \
+         .name = NAME_STR, \
+-        .data = &ed_##NAME, \
++        .data = NULL, \
+         .state = EN_STALE, \
+         .init = en_##NAME##_init, \
+         .run = en_##NAME##_run, \
+         .cleanup = en_##NAME##_cleanup, \
++        .is_valid = en_##NAME##_is_valid, \
+     };
+ 
++#define ENGINE_NODE_CUSTOM_DATA(NAME, NAME_STR) \
++    ENGINE_NODE_DEF(NAME, NAME_STR)
++
++#define ENGINE_NODE(NAME, NAME_STR) \
++    static bool (*en_##NAME##_is_valid)(struct engine_node *node) = NULL; \
++    ENGINE_NODE_DEF(NAME, NAME_STR)
++
+ /* Macro to define member functions of an engine node which represents
+  * a table of OVSDB */
+ #define ENGINE_FUNC_OVSDB(DB_NAME, TBL_NAME) \
+ static void \
+-en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \
++en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node, \
++                                void *data OVS_UNUSED) \
+ { \
+     const struct DB_NAME##rec_##TBL_NAME##_table *table = \
+         EN_OVSDB_GET(node); \
+@@ -234,10 +286,18 @@ en_##DB_NAME##_##TBL_NAME##_run(struct engine_node *node) \
+     } \
+     engine_set_node_state(node, EN_VALID); \
+ } \
+-static void (*en_##DB_NAME##_##TBL_NAME##_init)(struct engine_node *node) \
+-            = NULL; \
+-static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \
+-            = NULL;
++static void *en_##DB_NAME##_##TBL_NAME##_init( \
++    struct engine_node *node OVS_UNUSED, \
++    struct engine_arg *arg) \
++{ \
++    struct ovsdb_idl *idl = arg->DB_NAME##_idl; \
++    struct ed_type_ovsdb_table *data = xzalloc(sizeof *data); \
++    data->table = DB_NAME##rec_##TBL_NAME##_table_get(idl); \
++    return data; \
++} \
++static void en_##DB_NAME##_##TBL_NAME##_cleanup(void *data OVS_UNUSED) \
++{ \
++}
+ 
+ /* Macro to define member functions of an engine node which represents
+  * a table of OVN SB DB */
+@@ -250,21 +310,16 @@ static void (*en_##DB_NAME##_##TBL_NAME##_cleanup)(struct engine_node *node) \
+     ENGINE_FUNC_OVSDB(ovs, TBL_NAME)
+ 
+ /* Macro to define an engine node which represents a table of OVSDB */
+-#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR, IDL) \
+-    struct ed_type_ovsdb_table ed_##DB_NAME##_##TBL_NAME; \
+-    memset(&ed_##DB_NAME##_##TBL_NAME, 0, sizeof ed_##DB_NAME##_##TBL_NAME); \
+-    ovs_assert(IDL); \
+-    ed_##DB_NAME##_##TBL_NAME.table = \
+-        DB_NAME##rec_##TBL_NAME##_table_get(IDL); \
++#define ENGINE_NODE_OVSDB(DB_NAME, DB_NAME_STR, TBL_NAME, TBL_NAME_STR) \
+     ENGINE_NODE(DB_NAME##_##TBL_NAME, DB_NAME_STR"_"TBL_NAME_STR)
+ 
+ /* Macro to define an engine node which represents a table of OVN SB DB */
+ #define ENGINE_NODE_SB(TBL_NAME, TBL_NAME_STR) \
+-    ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR, ovnsb_idl_loop.idl);
++    ENGINE_NODE_OVSDB(sb, "SB", TBL_NAME, TBL_NAME_STR);
+ 
+ /* Macro to define an engine node which represents a table of open_vswitch
+  * DB */
+ #define ENGINE_NODE_OVS(TBL_NAME, TBL_NAME_STR) \
+-    ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR, ovs_idl_loop.idl);
++    ENGINE_NODE_OVSDB(ovs, "OVS", TBL_NAME, TBL_NAME_STR);
+ 
+ #endif /* ovn/lib/inc-proc-eng.h */
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch b/SOURCES/0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch
new file mode 100644
index 0000000..7058cb2
--- /dev/null
+++ b/SOURCES/0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch
@@ -0,0 +1,148 @@
+From 7a4814d4a50a78a9434df7f55ca0491f4736c658 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Wed, 22 Jan 2020 19:02:18 +0530
+Subject: [PATCH 4/4] ovn-controller: Remove ports from struct local_datapaths.
+
+struct local_datapaths stores the array of port bindings for each datapath.
+These ports are used only in the pinctrl module to check if a mac binding
+has been learnt for the buffered packets.
+
+MAC bindings are always learnt in the router pipeline and so
+logical_port column of MAC_Binding table will always refer to a
+logical router port. run_buffered_binding() of pinctrl module can use
+the peer ports stored in the struct local_datapaths instead.
+This would save many calls to mac_binding_lookup().
+
+This patch doesn't store the array of port bindings for each local
+datapath as it is not required at all.
+
+Earlier, the peer ports were stored only for patch port bindings. But we can have
+peer ports even for l3gateway port bindings. This patch now considers l3gateway
+ports also for storing the peer ports in struct local_datapaths.
+
+Acked-by: Han Zhou <hzhou@ovn.org>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+
+(cherry-picked from upstream master commit 6c7d9f1ff6a50f695bfd13be3912f4e82c5045f5)
+
+Change-Id: I30e984df51c1d8f81b74b863aaf37778fffc6b96
+---
+ ovn/controller/binding.c        | 26 +++++++++++++-------------
+ ovn/controller/ovn-controller.c |  2 --
+ ovn/controller/ovn-controller.h |  4 ----
+ ovn/controller/pinctrl.c        | 11 +++++++++--
+ 4 files changed, 22 insertions(+), 21 deletions(-)
+
+diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
+index 1380a3e6e..d94e43893 100644
+--- a/ovn/controller/binding.c
++++ b/ovn/controller/binding.c
+@@ -146,7 +146,7 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+     const struct sbrec_port_binding *pb;
+     SBREC_PORT_BINDING_FOR_EACH_EQUAL (pb, target,
+                                        sbrec_port_binding_by_datapath) {
+-        if (!strcmp(pb->type, "patch")) {
++        if (!strcmp(pb->type, "patch") || !strcmp(pb->type, "l3gateway")) {
+             const char *peer_name = smap_get(&pb->options, "peer");
+             if (peer_name) {
+                 const struct sbrec_port_binding *peer;
+@@ -155,11 +155,18 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                                             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);
++                    if (!strcmp(pb->type, "patch")) {
++                        /* Add the datapath to local datapath only for patch
++                         * ports. For l3gateway ports, since gateway router
++                         * resides on one chassis, we don't need to add.
++                         * Otherwise, all other chassis might create patch
++                         * ports between br-int and the provider bridge. */
++                        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++;
+                     if (ld->n_peer_ports > ld->n_allocated_peer_ports) {
+                         ld->peer_ports =
+@@ -172,13 +179,6 @@ add_local_datapath__(struct ovsdb_idl_index *sbrec_datapath_binding_by_key,
+                 }
+             }
+         }
+-
+-        ld->n_ports++;
+-        if (ld->n_ports > ld->n_allocated_ports) {
+-            ld->ports = x2nrealloc(ld->ports, &ld->n_allocated_ports,
+-                                   sizeof *ld->ports);
+-        }
+-        ld->ports[ld->n_ports - 1] = pb;
+     }
+     sbrec_port_binding_index_destroy_row(target);
+ }
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index 8d7ea89c4..b80d6c0dc 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -929,7 +929,6 @@ en_runtime_data_cleanup(void *data)
+     HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node,
+                         &rt_data->local_datapaths) {
+         free(cur_node->peer_ports);
+-        free(cur_node->ports);
+         hmap_remove(&rt_data->local_datapaths, &cur_node->hmap_node);
+         free(cur_node);
+     }
+@@ -953,7 +952,6 @@ en_runtime_data_run(struct engine_node *node, void *data)
+         struct local_datapath *cur_node, *next_node;
+         HMAP_FOR_EACH_SAFE (cur_node, next_node, hmap_node, local_datapaths) {
+             free(cur_node->peer_ports);
+-            free(cur_node->ports);
+             hmap_remove(local_datapaths, &cur_node->hmap_node);
+             free(cur_node);
+         }
+diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h
+index 09ef4b632..8bff57c56 100644
+--- a/ovn/controller/ovn-controller.h
++++ b/ovn/controller/ovn-controller.h
+@@ -60,10 +60,6 @@ struct local_datapath {
+      * hypervisor. */
+     bool has_local_l3gateway;
+ 
+-    const struct sbrec_port_binding **ports;
+-    size_t n_ports;
+-    size_t n_allocated_ports;
+-
+     struct {
+         const struct sbrec_port_binding *local;
+         const struct sbrec_port_binding *remote;
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index 5636fc1e0..800bde7e7 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -2973,10 +2973,17 @@ run_buffered_binding(struct ovsdb_idl_index *sbrec_mac_binding_by_lport_ip,
+     bool notify = false;
+ 
+     HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
++        /* MAC_Binding.logical_port will always belong to a
++         * a router datapath. Hence we can skip logical switch
++         * datapaths.
++         * */
++        if (datapath_is_switch(ld->datapath)) {
++            continue;
++        }
+ 
+-        for (size_t i = 0; i < ld->n_ports; i++) {
++        for (size_t i = 0; i < ld->n_peer_ports; i++) {
+ 
+-            const struct sbrec_port_binding *pb = ld->ports[i];
++            const struct sbrec_port_binding *pb = ld->peer_ports[i].local;
+             struct buffered_packets *cur_qp, *next_qp;
+             HMAP_FOR_EACH_SAFE (cur_qp, next_qp, hmap_node,
+                                 &buffered_packets_map) {
+-- 
+2.26.2
+
diff --git a/SOURCES/0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch b/SOURCES/0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch
new file mode 100644
index 0000000..b5f63e2
--- /dev/null
+++ b/SOURCES/0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch
@@ -0,0 +1,652 @@
+From 193b2aefdb41d55209193af1f5e8c8dacb861c84 Mon Sep 17 00:00:00 2001
+From: Dumitru Ceara <dceara@redhat.com>
+Date: Tue, 24 Sep 2019 10:02:49 +0200
+Subject: [PATCH 4/4] ovn-northd: Add static IP multicast flood configuration
+
+Add the following new configuration options to the
+Logical_Switch_Port:options column in the OVN Northbound database:
+
+- mcast_flood: if set to 'true' all incoming IP multicast traffic
+  (except IP multicast reports) entering the switch will also be
+  flooded on the logical switch port.
+- mcast_flood_reports: if set to 'true' all incoming IP multicast
+  entering the switch will also be flooded on the logical switch
+  port. A clone of the packets is also processed by ovn-controller
+  for snooping.
+
+Add the following new configuration option to the
+Logical_Router_Port:options column in the OVN Northbound database:
+
+- mcast_flood: if set to 'true' all incoming IP multicast traffic
+  (including IP multicast reports) entering the router will be also
+  flooded on the logical router port.
+
+Due to the fact that in the router pipeline multicast reports are
+not treated in a special way there's no need for an explicit
+'mcast_flood_reports' option for router ports.
+
+Signed-off-by: Dumitru Ceara <dceara@redhat.com>
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Mark Michelson <mmichels@redhat.com>
+
+(cherry-picked from ovn commit 79308138891ae04a02a07068501696ef78157912)
+---
+ ovn/lib/mcast-group-index.h |   2 +
+ ovn/northd/ovn-northd.8.xml |  30 ++++-
+ ovn/northd/ovn-northd.c     | 212 +++++++++++++++++++++++++++++++-----
+ ovn/ovn-nb.xml              |  34 ++++++
+ tests/ovn.at                |  81 +++++++++++++-
+ 5 files changed, 325 insertions(+), 34 deletions(-)
+
+diff --git a/ovn/lib/mcast-group-index.h b/ovn/lib/mcast-group-index.h
+index 6249cac99..930963b1b 100644
+--- a/ovn/lib/mcast-group-index.h
++++ b/ovn/lib/mcast-group-index.h
+@@ -28,6 +28,8 @@ enum ovn_mcast_tunnel_keys {
+     OVN_MCAST_FLOOD_TUNNEL_KEY = OVN_MIN_MULTICAST,
+     OVN_MCAST_UNKNOWN_TUNNEL_KEY,
+     OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY,
++    OVN_MCAST_MROUTER_STATIC_TUNNEL_KEY,
++    OVN_MCAST_STATIC_TUNNEL_KEY,
+     OVN_MIN_IP_MULTICAST,
+     OVN_MAX_IP_MULTICAST = OVN_MAX_MULTICAST,
+ };
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index ec9020d2a..b9ae28e14 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -954,7 +954,11 @@ output;
+       <li>
+         A priority-100 flow that punts all IGMP packets to
+         <code>ovn-controller</code> if IGMP snooping is enabled on the
+-        logical switch.
++        logical switch. The flow also forwards the IGMP packets to the
++        <code>MC_MROUTER_STATIC</code> multicast group, which
++        <code>ovn-northd</code> populates with all the logical ports that
++        have <ref column="options" table="Logical_Switch_Port"/>
++        <code>:mcast_flood_reports='true'</code>.
+       </li>
+ 
+       <li>
+@@ -976,10 +980,15 @@ output;
+ 
+       <li>
+         A priority-80 flow that forwards all unregistered IP multicast traffic
+-        to the <code>MC_MROUTER_FLOOD</code> multicast group, if any.
+-        Otherwise the flow drops all unregistered IP multicast packets.  This
+-        flow is added only if <ref column="other_config"
+-        table="Logical_Switch"/>:mcast_flood_unregistered='false'.
++        to the <code>MC_STATIC</code> multicast group, which
++        <code>ovn-northd</code> populates with all the logical ports that
++        have <ref column="options" table="Logical_Switch_Port"/>
++        <code>:mcast_flood='true'</code>. The flow also forwards
++        unregistered IP multicast traffic to the <code>MC_MROUTER_FLOOD</code>
++        multicast group, which <code>ovn-northd</code> populates with all the
++        logical ports connected to logical routers that have
++        <ref column="options" table="Logical_Router"/>
++        <code>:mcast_relay='true'</code>.
+       </li>
+ 
+       <li>
+@@ -2092,6 +2101,17 @@ output;
+         </p>
+       </li>
+ 
++      <li>
++        <p>
++          Priority-450 flow that matches unregistered IP multicast traffic
++          and sets <code>outport</code> to the <code>MC_STATIC</code>
++          multicast group, which <code>ovn-northd</code> populates with the
++          logical ports that have
++          <ref column="options" table="Logical_Router_Port"/>
++          <code>:mcast_flood='true'</code>.
++        </p>
++      </li>
++
+       <li>
+         <p>
+           For distributed logical routers where one of the logical router
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 2f6826f17..250529eb7 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -459,7 +459,12 @@ struct mcast_switch_info {
+                                  * should be flooded to the mrouter. Only
+                                  * applicable if flood_unregistered == false.
+                                  */
+-
++    bool flood_reports;         /* True if the switch has at least one port
++                                 * configured to flood reports.
++                                 */
++    bool flood_static;          /* True if the switch has at least one port
++                                 * configured to flood traffic.
++                                 */
+     int64_t table_size;         /* Max number of IP multicast groups. */
+     int64_t idle_timeout;       /* Timeout after which an idle group is
+                                  * flushed.
+@@ -477,7 +482,10 @@ struct mcast_switch_info {
+ };
+ 
+ struct mcast_router_info {
+-    bool relay; /* True if the router should relay IP multicast. */
++    bool relay;        /* True if the router should relay IP multicast. */
++    bool flood_static; /* True if the router has at least one port configured
++                        * to flood traffic.
++                        */
+ };
+ 
+ struct mcast_info {
+@@ -492,6 +500,34 @@ struct mcast_info {
+     };
+ };
+ 
++struct mcast_port_info {
++    bool flood;         /* True if the port should flood IP multicast traffic
++                         * regardless if it's registered or not. */
++    bool flood_reports; /* True if the port should flood IP multicast reports
++                         * (e.g., IGMP join/leave). */
++};
++
++static void
++init_mcast_port_info(struct mcast_port_info *mcast_info,
++                     const struct nbrec_logical_switch_port *nbsp,
++                     const struct nbrec_logical_router_port *nbrp)
++{
++    if (nbsp) {
++        mcast_info->flood =
++            smap_get_bool(&nbsp->options, "mcast_flood", false);
++        mcast_info->flood_reports =
++            smap_get_bool(&nbsp->options, "mcast_flood_reports",
++                          false);
++    } else if (nbrp) {
++        /* We don't process multicast reports in any special way on logical
++         * routers so just treat them as regular multicast traffic.
++         */
++        mcast_info->flood =
++            smap_get_bool(&nbrp->options, "mcast_flood", false);
++        mcast_info->flood_reports = mcast_info->flood;
++    }
++}
++
+ static uint32_t
+ ovn_mcast_group_allocate_key(struct mcast_info *mcast_info)
+ {
+@@ -1033,7 +1069,7 @@ build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
+         ovn_datapath_destroy(datapaths, od);
+     }
+ }
+-
++
+ struct ovn_port {
+     struct hmap_node key_node;  /* Index on 'key'. */
+     char *key;                  /* nbs->name, nbr->name, sb->logical_port. */
+@@ -1055,6 +1091,9 @@ struct ovn_port {
+ 
+     struct lport_addresses lrp_networks;
+ 
++    /* Logical port multicast data. */
++    struct mcast_port_info mcast_info;
++
+     bool derived; /* Indicates whether this is an additional port
+                    * derived from nbsp or nbrp. */
+ 
+@@ -1071,6 +1110,23 @@ struct ovn_port {
+     struct ovs_list list;       /* In list of similar records. */
+ };
+ 
++static void
++ovn_port_set_sb(struct ovn_port *op,
++                const struct sbrec_port_binding *sb)
++{
++    op->sb = sb;
++}
++
++static void
++ovn_port_set_nb(struct ovn_port *op,
++                const struct nbrec_logical_switch_port *nbsp,
++                const struct nbrec_logical_router_port *nbrp)
++{
++    op->nbsp = nbsp;
++    op->nbrp = nbrp;
++    init_mcast_port_info(&op->mcast_info, op->nbsp, op->nbrp);
++}
++
+ static struct ovn_port *
+ ovn_port_create(struct hmap *ports, const char *key,
+                 const struct nbrec_logical_switch_port *nbsp,
+@@ -1084,9 +1140,8 @@ ovn_port_create(struct hmap *ports, const char *key,
+     op->json_key = ds_steal_cstr(&json_key);
+ 
+     op->key = xstrdup(key);
+-    op->sb = sb;
+-    op->nbsp = nbsp;
+-    op->nbrp = nbrp;
++    ovn_port_set_sb(op, sb);
++    ovn_port_set_nb(op, nbsp, nbrp);
+     op->derived = false;
+     hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
+     return op;
+@@ -1882,7 +1937,7 @@ join_logical_ports(struct northd_context *ctx,
+                                      nbsp->name);
+                         continue;
+                     }
+-                    op->nbsp = nbsp;
++                    ovn_port_set_nb(op, nbsp, NULL);
+                     ovs_list_remove(&op->list);
+ 
+                     uint32_t queue_id = smap_get_int(&op->sb->options,
+@@ -1973,7 +2028,7 @@ join_logical_ports(struct northd_context *ctx,
+                                      nbrp->name);
+                         continue;
+                     }
+-                    op->nbrp = nbrp;
++                    ovn_port_set_nb(op, NULL, nbrp);
+                     ovs_list_remove(&op->list);
+                     ovs_list_push_back(both, &op->list);
+ 
+@@ -2018,7 +2073,7 @@ join_logical_ports(struct northd_context *ctx,
+                     struct ovn_port *crp = ovn_port_find(ports, redirect_name);
+                     if (crp) {
+                         crp->derived = true;
+-                        crp->nbrp = nbrp;
++                        ovn_port_set_nb(crp, NULL, nbrp);
+                         ovs_list_remove(&crp->list);
+                         ovs_list_push_back(both, &crp->list);
+                     } else {
+@@ -2896,7 +2951,7 @@ build_ports(struct northd_context *ctx,
+             continue;
+         }
+ 
+-        op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
++        ovn_port_set_sb(op, sbrec_port_binding_insert(ctx->ovnsb_txn));
+         ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, op,
+                               &chassis_qdisc_queues,
+                               &active_ha_chassis_grps);
+@@ -2938,6 +2993,14 @@ static const struct multicast_group mc_flood =
+ static const struct multicast_group mc_mrouter_flood =
+     { MC_MROUTER_FLOOD, OVN_MCAST_MROUTER_FLOOD_TUNNEL_KEY };
+ 
++#define MC_MROUTER_STATIC "_MC_mrouter_static"
++static const struct multicast_group mc_mrouter_static =
++    { MC_MROUTER_STATIC, OVN_MCAST_MROUTER_STATIC_TUNNEL_KEY };
++
++#define MC_STATIC "_MC_static"
++static const struct multicast_group mc_static =
++    { MC_STATIC, OVN_MCAST_STATIC_TUNNEL_KEY };
++
+ #define MC_UNKNOWN "_MC_unknown"
+ static const struct multicast_group mc_unknown =
+     { MC_UNKNOWN, OVN_MCAST_UNKNOWN_TUNNEL_KEY };
+@@ -3151,7 +3214,23 @@ ovn_igmp_group_get_ports(const struct sbrec_igmp_group *sb_igmp_group,
+ 
+      *n_ports = 0;
+      for (size_t i = 0; i < sb_igmp_group->n_ports; i++) {
+-        ports[(*n_ports)] =
++        struct ovn_port *port =
++            ovn_port_find(ovn_ports, sb_igmp_group->ports[i]->logical_port);
++
++        /* If this is already a flood port skip it for the group. */
++        if (port->mcast_info.flood) {
++            continue;
++        }
++
++        /* If this is already a port of a router on which relay is enabled,
++         * skip it for the group. Traffic is flooded there anyway.
++         */
++        if (port->peer && port->peer->od &&
++                port->peer->od->mcast_info.rtr.relay) {
++            continue;
++        }
++
++        ports[(*n_ports)] = port;
+             ovn_port_find(ovn_ports, sb_igmp_group->ports[i]->logical_port);
+         if (ports[(*n_ports)]) {
+             (*n_ports)++;
+@@ -5531,9 +5610,18 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
+ 
+         if (mcast_sw_info->enabled) {
++            ds_clear(&actions);
++            if (mcast_sw_info->flood_reports) {
++                ds_put_cstr(&actions,
++                            "clone { "
++                                "outport = \""MC_MROUTER_STATIC"\"; "
++                                "output; "
++                            "};");
++            }
++            ds_put_cstr(&actions, "igmp;");
+             /* Punt IGMP traffic to controller. */
+             ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 100,
+-                          "ip4 && ip.proto == 2", "igmp;");
++                          "ip4 && ip.proto == 2", ds_cstr(&actions));
+ 
+             /* Flood all IP multicast traffic destined to 224.0.0.X to all
+              * ports - RFC 4541, section 2.1.2, item 2.
+@@ -5542,17 +5630,30 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+                           "ip4 && ip4.dst == 224.0.0.0/24",
+                           "outport = \""MC_FLOOD"\"; output;");
+ 
+-            /* Drop unregistered IP multicast if not allowed. */
++            /* Forward uregistered IP multicast to routers with relay enabled
++             * and to any ports configured to flood IP multicast traffic.
++             * If configured to flood unregistered traffic this will be
++             * handled by the L2 multicast flow.
++             */
+             if (!mcast_sw_info->flood_unregistered) {
+-                /* Forward unregistered IP multicast to mrouter (if any). */
++                ds_clear(&actions);
++
+                 if (mcast_sw_info->flood_relay) {
+-                    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
+-                                  "ip4 && ip4.mcast",
+-                                  "outport = \""MC_MROUTER_FLOOD"\"; output;");
++                    ds_put_cstr(&actions,
++                                "clone { "
++                                    "outport = \""MC_MROUTER_FLOOD"\"; "
++                                    "output; "
++                                "}; ");
++                }
++
++                if (mcast_sw_info->flood_static) {
++                    ds_put_cstr(&actions, "outport =\""MC_STATIC"\"; output;");
+                 } else {
+-                    ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
+-                                  "ip4 && ip4.mcast", "drop;");
++                    ds_put_cstr(&actions, "drop;");
+                 }
++
++                ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 80,
++                              "ip4 && ip4.mcast", ds_cstr(&actions));
+             }
+         }
+ 
+@@ -5582,11 +5683,20 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+ 
+         ds_put_format(&match, "eth.mcast && ip4 && ip4.dst == %s ",
+                       igmp_group->mcgroup.name);
++
+         /* Also flood traffic to all multicast routers with relay enabled. */
+         if (mcast_sw_info->flood_relay) {
+             ds_put_cstr(&actions,
+                         "clone { "
+-                            "outport = \""MC_MROUTER_FLOOD "\"; output; "
++                            "outport = \""MC_MROUTER_FLOOD "\"; "
++                            "output; "
++                        "};");
++        }
++        if (mcast_sw_info->flood_static) {
++            ds_put_cstr(&actions,
++                        "clone { "
++                            "outport =\""MC_STATIC"\"; "
++                            "output; "
+                         "};");
+         }
+         ds_put_format(&actions, "outport = \"%s\"; output; ",
+@@ -7783,6 +7893,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+         if (!od->nbr || !od->mcast_info.rtr.relay) {
+             continue;
+         }
++
+         struct ovn_igmp_group *igmp_group;
+ 
+         LIST_FOR_EACH (igmp_group, list_node, &od->mcast_info.groups) {
+@@ -7790,11 +7901,35 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             ds_clear(&actions);
+             ds_put_format(&match, "ip4 && ip4.dst == %s ",
+                           igmp_group->mcgroup.name);
++            if (od->mcast_info.rtr.flood_static) {
++                ds_put_cstr(&actions,
++                            "clone { "
++                                "outport = \""MC_STATIC"\"; "
++                                "ip.ttl--; "
++                                "next; "
++                            "};");
++            }
+             ds_put_format(&actions, "outport = \"%s\"; ip.ttl--; next;",
+                           igmp_group->mcgroup.name);
+             ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 500,
+                           ds_cstr(&match), ds_cstr(&actions));
+         }
++
++        /* If needed, flood unregistered multicast on statically configured
++         * ports.
++         */
++        if (od->mcast_info.rtr.flood_static) {
++            ds_clear(&match);
++            ds_clear(&actions);
++            ds_put_format(&match, "ip4.mcast");
++            ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, 450,
++                          "ip4.mcast",
++                          "clone { "
++                                "outport = \""MC_STATIC"\"; "
++                                "ip.ttl--; "
++                                "next; "
++                          "};");
++        }
+     }
+ 
+     /* Logical router ingress table 8: Policy.
+@@ -8943,11 +9078,15 @@ build_mcast_groups(struct northd_context *ctx,
+     hmap_init(igmp_groups);
+ 
+     HMAP_FOR_EACH (op, key_node, ports) {
+-        if (!op->nbsp) {
+-            continue;
+-        }
+-
+-        if (lsp_is_enabled(op->nbsp)) {
++        if (op->nbrp && lrport_is_enabled(op->nbrp)) {
++            /* If this port is configured to always flood multicast traffic
++             * add it to the MC_STATIC group.
++             */
++            if (op->mcast_info.flood) {
++                ovn_multicast_add(mcast_groups, &mc_static, op);
++                op->od->mcast_info.rtr.flood_static = true;
++            }
++        } else if (op->nbsp && lsp_is_enabled(op->nbsp)) {
+             ovn_multicast_add(mcast_groups, &mc_flood, op);
+ 
+             /* If this port is connected to a multicast router then add it
+@@ -8957,6 +9096,22 @@ build_mcast_groups(struct northd_context *ctx,
+                     op->peer->od && op->peer->od->mcast_info.rtr.relay) {
+                 ovn_multicast_add(mcast_groups, &mc_mrouter_flood, op);
+             }
++
++            /* If this port is configured to always flood multicast reports
++             * add it to the MC_MROUTER_STATIC group.
++             */
++            if (op->mcast_info.flood_reports) {
++                ovn_multicast_add(mcast_groups, &mc_mrouter_static, op);
++                op->od->mcast_info.sw.flood_reports = true;
++            }
++
++            /* If this port is configured to always flood multicast traffic
++             * add it to the MC_STATIC group.
++             */
++            if (op->mcast_info.flood) {
++                ovn_multicast_add(mcast_groups, &mc_static, op);
++                op->od->mcast_info.sw.flood_static = true;
++            }
+         }
+     }
+ 
+@@ -9017,8 +9172,13 @@ build_mcast_groups(struct northd_context *ctx,
+         for (size_t i = 0; i < od->n_router_ports; i++) {
+             struct ovn_port *router_port = od->router_ports[i]->peer;
+ 
++            /* If the router the port connects to doesn't have multicast
++             * relay enabled or if it was already configured to flood
++             * multicast traffic then skip it.
++             */
+             if (!router_port || !router_port->od ||
+-                    !router_port->od->mcast_info.rtr.relay) {
++                    !router_port->od->mcast_info.rtr.relay ||
++                    router_port->mcast_info.flood) {
+                 continue;
+             }
+ 
+diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
+index c2472a04a..e0051db25 100644
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -671,6 +671,26 @@
+         </column>
+       </group>
+ 
++      <group title="IP Multicast Snooping Options">
++        <p>
++          These options apply when the port is part of a logical switch
++          which has <ref table="Logical_Switch" column="other_config"/>
++          :mcast_snoop set to <code>true</code>.
++        </p>
++
++        <column name="options" key="mcast_flood"
++                type='{"type": "boolean"}'>
++          If set to <code>true</code>, multicast packets (except reports) are
++          unconditionally forwarded to the specific port.
++        </column>
++
++        <column name="options" key="mcast_flood_reports"
++                type='{"type": "boolean"}'>
++          If set to <code>true</code>, multicast reports are unconditionally
++          forwarded to the specific port.
++        </column>
++      </group>
++
+     </group>
+ 
+     <group title="Containers">
+@@ -1962,6 +1982,20 @@
+           issues.
+         </p>
+       </column>
++
++      <column name="options" key="mcast_flood"
++              type='{"type": "boolean"}'>
++        <p>
++          If set to <code>true</code>, multicast traffic (including reports)
++          are unconditionally forwarded to the specific port.
++        </p>
++
++        <p>
++          This option applies when the port is part of a logical router which
++          has <ref table="Logical_Router" column="options"/>:mcast_relay set
++          to <code>true</code>.
++        </p>
++      </column>
+     </group>
+ 
+     <group title="Attachment">
+diff --git a/tests/ovn.at b/tests/ovn.at
+index d52c97541..410a56fe5 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -16258,7 +16258,6 @@ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
+ 
+ # Flush IGMP groups.
+ ovn-sbctl ip-multicast-flush sw1
+-ovn-nbctl --wait=hv -t 3 sync
+ OVS_WAIT_UNTIL([
+     total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
+     test "${total_entries}" = "0"
+@@ -16310,12 +16309,12 @@ send_ip_multicast_pkt hv2-vif4 hv2 \
+ # Sleep a bit to make sure no traffic is received and then check.
+ sleep 1
+ OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_empty])
+-OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
+-OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
+ 
+ # Enable IGMP relay on rtr
+@@ -16386,6 +16385,82 @@ OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
+ OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
+ 
++# Flush IGMP groups.
++ovn-sbctl ip-multicast-flush sw1
++ovn-sbctl ip-multicast-flush sw2
++ovn-sbctl ip-multicast-flush sw3
++OVS_WAIT_UNTIL([
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    test "${total_entries}" = "0"
++])
++
++as hv1 reset_pcap_file hv1-vif1 hv1/vif1
++as hv1 reset_pcap_file hv1-vif2 hv1/vif2
++as hv1 reset_pcap_file hv1-vif3 hv1/vif3
++as hv1 reset_pcap_file hv1-vif4 hv1/vif4
++as hv2 reset_pcap_file hv2-vif1 hv2/vif1
++as hv2 reset_pcap_file hv2-vif2 hv2/vif2
++as hv2 reset_pcap_file hv2-vif3 hv2/vif3
++as hv2 reset_pcap_file hv2-vif4 hv2/vif4
++
++truncate -s 0 expected_empty
++truncate -s 0 expected_switched
++truncate -s 0 expected_routed
++truncate -s 0 expected_reports
++
++# Enable mcast_flood on sw1-p11
++ovn-nbctl set Logical_Switch_Port sw1-p11 options:mcast_flood='true'
++
++# Enable mcast_flood_reports on sw1-p21
++ovn-nbctl set Logical_Switch_Port sw1-p21 options:mcast_flood_reports='true'
++# Enable mcast_flood on rtr-sw2
++ovn-nbctl set Logical_Router_Port rtr-sw2 options:mcast_flood='true'
++# Enable mcast_flood on sw2-p1
++ovn-nbctl set Logical_Switch_Port sw2-p1 options:mcast_flood='true'
++
++ovn-nbctl --wait=hv sync
++
++# Inject IGMP Join for 239.0.1.68 on sw1-p12.
++send_igmp_v3_report hv1-vif2 hv1 \
++    000000000001 $(ip_to_hex 10 0 0 1) f9f8 \
++    $(ip_to_hex 239 0 1 68) 04 e9b9 \
++    expected_reports
++
++# Check that the IGMP Group is learned.
++OVS_WAIT_UNTIL([
++    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" | wc -l`
++    test "${total_entries}" = "1"
++])
++
++# Send traffic from sw1-p21
++send_ip_multicast_pkt hv2-vif1 hv2 \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000
++store_ip_multicast_pkt \
++    000000000001 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 20 ca70 11 \
++    e518e518000a3b3a0000 expected_switched
++store_ip_multicast_pkt \
++    000000000200 01005e000144 \
++    $(ip_to_hex 10 0 0 42) $(ip_to_hex 239 0 1 68) 1e 1f cb70 11 \
++    e518e518000a3b3a0000 expected_routed
++
++# Sleep a bit to make sure no duplicate traffic is received
++sleep 1
++
++# Check that traffic is switched to sw1-p11 and sw1-p12
++# Check that IGMP join is flooded on sw1-p21
++# Check that traffic is routed by rtr to rtr-sw2 and then switched to sw2-p1
++OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected_switched])
++OVN_CHECK_PACKETS([hv1/vif2-tx.pcap], [expected_switched])
++OVN_CHECK_PACKETS([hv1/vif3-tx.pcap], [expected_routed])
++OVN_CHECK_PACKETS([hv1/vif4-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [expected_reports])
++OVN_CHECK_PACKETS([hv2/vif2-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
++OVN_CHECK_PACKETS([hv2/vif4-tx.pcap], [expected_empty])
++
+ OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+ 
+-- 
+2.21.0
+
diff --git a/SOURCES/0004-tests-Updated-expected-log-message.patch b/SOURCES/0004-tests-Updated-expected-log-message.patch
new file mode 100644
index 0000000..d29e0fb
--- /dev/null
+++ b/SOURCES/0004-tests-Updated-expected-log-message.patch
@@ -0,0 +1,52 @@
+From fe8a82dfeb6b965472aa66996c85a0a0383ddec1 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Sat, 7 Dec 2019 23:08:56 -0500
+Subject: [PATCH ovn 4/4] tests: Updated expected log message
+
+A previous commit added more detail to this log message.  Fix the test
+to reflect the new text.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Dumitru Ceara <dceara@redhat.com>
+
+(cherry picked from upstream commit b3e0e3b12426d47a153506c92fa578fcc2ccf7c1)
+---
+ tests/ovn.at | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/tests/ovn.at b/tests/ovn.at
+index e2565f2..90830dc 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -13674,22 +13674,22 @@ ovn-nbctl list logical_switch_port
+ # Now try to add duplicate addresses on a new port. These should all fail
+ ovn-nbctl --wait=sb lsp-add sw1 sw1-p5
+ AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.1"], [1], [],
+-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.1
++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '10.0.0.1' found on logical switch port 'sw1-p1'
+ ])
+ AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 10.0.0.2"], [1], [],
+-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 10.0.0.2
++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '10.0.0.2' found on logical switch port 'sw1-p1'
+ ])
+ AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::1"], [1], [],
+-[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::1
++[ovn-nbctl: Error on switch sw1: duplicate IPv6 address 'aef0::1' found on logical switch port 'sw1-p1'
+ ])
+ AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 aef0::2"], [1], [],
+-[ovn-nbctl: Error on switch sw1: duplicate IPv6 address aef0::2
++[ovn-nbctl: Error on switch sw1: duplicate IPv6 address 'aef0::2' found on logical switch port 'sw1-p1'
+ ])
+ AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.2"], [1], [],
+-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.2
++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '192.168.0.2' found on logical switch port 'sw1-p2'
+ ])
+ AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:04 192.168.0.3"], [1], [],
+-[ovn-nbctl: Error on switch sw1: duplicate IPv4 address 192.168.0.3
++[ovn-nbctl: Error on switch sw1: duplicate IPv4 address '192.168.0.3' found on logical switch port 'sw1-p3'
+ ])
+ 
+ # Now try re-setting sw1-p1. This should succeed
+-- 
+1.8.3.1
+
diff --git a/SOURCES/0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch b/SOURCES/0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch
new file mode 100644
index 0000000..2b70413
--- /dev/null
+++ b/SOURCES/0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch
@@ -0,0 +1,477 @@
+From 6915baff6dabb2605abd055436f6e8ce7a5b5d19 Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Tue, 10 Sep 2019 21:22:44 +0000
+Subject: [PATCH 05/12] Replace chassis mac with router port mac on destination
+ chassis
+
+During E-W routing for vlan backed networks, we replace router port
+mac with chassis mac, when packet leaves the source hypervisor.
+
+As a result, the destination VM (on remote hypervisor) will see
+chassis mac as source mac in received packet.
+
+Although, functionality wise this does not cause any issue,
+however chassis mac being see as source on VM, will
+lead to following:
+a. INCONSISTENT SOURCE MAC:
+   If the destination VM moves to same hypervisor as sender,
+   then it will see router port mac as source mac. Whereas, on
+   a remote hypervisor, source mac will be the sender chassis mac.
+
+   This will cause inconsistency in packet headers for the same
+   flow and could be confusing for someone looking at packet
+   captures inside the vm.
+
+b. SYSTEM MAC BEING EXPOSED TO VM:
+   Chassis mac is a CMS provided mac, i.e it is an infrastructure
+   mac. It is not a good practice to expose such values to VM,
+   which should not be seeing them in first place.
+
+In order to replace chassis mac with router port mac, we will
+do following.
+
+a. Create conjunction for each chassis mac and router port vlan
+   id combination. For example, for a 2 node chassis setup, where
+   we have a logical router, connected to 4 logical switches with
+   vlan ids: 2000, 1000, 0 and 24, the conjunction flow will look
+   like following:
+
+   cookie=0x0, duration=9094.608s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_src=aa:bb:cc:dd:ee:22 actions=conjunction(100,1/2)
+   cookie=0x0, duration=9094.608s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_src=aa:bb:cc:dd:ff:ee actions=conjunction(100,1/2)
+
+   cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_vlan=2000 actions=conjunction(100,2/2)
+   cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_vlan=1000 actions=conjunction(100,2/2)
+   cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,vlan_tci=0x0000/0x1fff actions=conjunction(100,2/2)
+   cookie=0x0, duration=9094.552s, table=0, n_packets=0, n_bytes=0, idle_age=9094, priority=180,dl_vlan=24 actions=conjunction(100,2/2)
+
+b. Using this conjunction as match, we can identify if packet entering destination
+   hypervisor was routed at the source or not. This will be done in table=0 (Physical to logical)
+   at priority=180.
+   For example:
+   cookie=0x0, duration=9795.957s, table=0, n_packets=1391, n_bytes=141882, idle_age=8396, priority=180,conj_id=100,in_port=146,dl_vlan=1000 actions=.........,mod_dl_src:00:00:01:01:02:03,...
+
+c. We use conjunction, as it will ensure that we do not end up having lot of flows
+   as we scale up. If we do not use conjunction, then we will have
+   N (number of chassis macs) X M (number of router vlans) number of ovs flows.
+   Conjunction converts it to N + M.
+   Consider a setup, with 500 Chassis and 500 routed vlans.
+   Without conjunction we will need 25000 (500 * 500) flows,
+   whereas with conjunction that number comes down to 1000 (500 + 500).
+
+Change-Id: Idae7f716703c9fe94bd742ad4bd412a9cabcb550
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/controller/chassis.c        |   2 +-
+ ovn/controller/chassis.h        |   3 +
+ ovn/controller/ovn-controller.c |   5 +
+ ovn/controller/physical.c       | 222 +++++++++++++++++++++++++++++++-
+ ovn/controller/physical.h       |   1 +
+ ovn/ovn-architecture.7.xml      |  10 +-
+ tests/ovn.at                    |  14 +-
+ 7 files changed, 244 insertions(+), 13 deletions(-)
+
+diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
+index 4cd340124..c53bb5385 100644
+--- a/ovn/controller/chassis.c
++++ b/ovn/controller/chassis.c
+@@ -144,7 +144,7 @@ get_bridge_mappings(const struct smap *ext_ids)
+     return smap_get_def(ext_ids, "ovn-bridge-mappings", "");
+ }
+ 
+-static const char *
++const char *
+ get_chassis_mac_mappings(const struct smap *ext_ids)
+ {
+     return smap_get_def(ext_ids, "ovn-chassis-mac-mappings", "");
+diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h
+index 16a131a3b..bff2ca4c5 100644
+--- a/ovn/controller/chassis.h
++++ b/ovn/controller/chassis.h
+@@ -27,6 +27,7 @@ struct sbrec_chassis;
+ struct sbrec_chassis_table;
+ struct sset;
+ struct eth_addr;
++struct smap;
+ 
+ void chassis_register_ovs_idl(struct ovsdb_idl *);
+ const struct sbrec_chassis *chassis_run(
+@@ -43,4 +44,6 @@ bool chassis_get_mac(const struct sbrec_chassis *chassis,
+                      struct eth_addr *chassis_mac);
+ const char *chassis_get_id(void);
+ 
++const char * get_chassis_mac_mappings(const struct smap *ext_ids);
++
+ #endif /* ovn/chassis.h */
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index e86410198..cb3d63cbb 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -1270,9 +1270,14 @@ en_flow_output_run(struct engine_node *node)
+         (struct sbrec_port_binding_table *)EN_OVSDB_GET(
+             engine_get_input("SB_port_binding", node));
+ 
++    struct sbrec_chassis_table *chassis_table =
++        (struct sbrec_chassis_table *)EN_OVSDB_GET(
++            engine_get_input("SB_chassis", node));
++
+     physical_run(sbrec_port_binding_by_name,
+                  multicast_group_table,
+                  port_binding_table,
++                 chassis_table,
+                  mff_ovn_geneve,
+                  br_int, chassis, ct_zones,
+                  local_datapaths, local_lports,
+diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
+index bf1d0d716..9d553da9a 100644
+--- a/ovn/controller/physical.c
++++ b/ovn/controller/physical.c
+@@ -47,9 +47,23 @@
+ 
+ VLOG_DEFINE_THIS_MODULE(physical);
+ 
++/* 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 void
++load_logical_ingress_metadata(const struct sbrec_port_binding *binding,
++                              const struct zone_ids *zone_ids,
++                              struct ofpbuf *ofpacts_p);
++
+ /* UUID to identify OF flows not associated with ovsdb rows. */
+ static struct uuid *hc_uuid = NULL;
+ 
++#define CHASSIS_MAC_TO_ROUTER_MAC_CONJID        100
++
+ void
+ physical_register_ovs_idl(struct ovsdb_idl *ovs_idl)
+ {
+@@ -199,12 +213,6 @@ get_localnet_port(const struct hmap *local_datapaths, int64_t 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,
+@@ -385,6 +393,200 @@ put_remote_port_redirect_overlay(const struct
+                     match, ofpacts_p, &binding->header_.uuid);
+ }
+ 
++
++static struct hmap remote_chassis_macs =
++    HMAP_INITIALIZER(&remote_chassis_macs);
++
++/* Maps from a physical network name to the chassis macs of remote chassis. */
++struct remote_chassis_mac {
++    struct hmap_node hmap_node;
++    char *chassis_mac;
++    char *chassis_id;
++};
++
++static void
++populate_remote_chassis_macs(const struct sbrec_chassis *my_chassis,
++                             const struct sbrec_chassis_table *chassis_table)
++{
++    const struct sbrec_chassis *chassis;
++    SBREC_CHASSIS_TABLE_FOR_EACH (chassis, chassis_table) {
++
++        /* We want only remote chassis macs. */
++        if (!strcmp(my_chassis->name, chassis->name)) {
++            continue;
++        }
++
++        const char *tokens
++            = get_chassis_mac_mappings(&chassis->external_ids);
++
++        if (!strlen(tokens)) {
++            continue;
++        }
++
++        char *save_ptr = NULL;
++        char *token;
++        char *tokstr = xstrdup(tokens);
++
++        /* Format for a chassis mac configuration is:
++         * ovn-chassis-mac-mappings="bridge-name1:MAC1,bridge-name2:MAC2"
++         */
++        for (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);
++            struct remote_chassis_mac *remote_chassis_mac = NULL;
++            remote_chassis_mac = xmalloc(sizeof *remote_chassis_mac);
++            hmap_insert(&remote_chassis_macs, &remote_chassis_mac->hmap_node,
++                        hash_string(chassis_mac_bridge, 0));
++            remote_chassis_mac->chassis_mac = xstrdup(chassis_mac_str);
++            remote_chassis_mac->chassis_id = xstrdup(chassis->name);
++        }
++        free(tokstr);
++    }
++}
++
++static void
++free_remote_chassis_macs(void)
++{
++    struct remote_chassis_mac *mac, *next_mac;
++
++    HMAP_FOR_EACH_SAFE (mac, next_mac, hmap_node, &remote_chassis_macs) {
++        hmap_remove(&remote_chassis_macs, &mac->hmap_node);
++        free(mac->chassis_mac);
++        free(mac->chassis_id);
++        free(mac);
++    }
++}
++
++static void
++put_chassis_mac_conj_id_flow(const struct sbrec_chassis_table *chassis_table,
++                             const struct sbrec_chassis *chassis,
++                             struct ofpbuf *ofpacts_p,
++                             struct ovn_desired_flow_table *flow_table)
++{
++    struct match match;
++    struct remote_chassis_mac *mac;
++
++    populate_remote_chassis_macs(chassis, chassis_table);
++
++    HMAP_FOR_EACH (mac, hmap_node, &remote_chassis_macs) {
++        struct eth_addr chassis_mac;
++        char *err_str = NULL;
++        struct ofpact_conjunction *conj;
++
++        if ((err_str = str_to_mac(mac->chassis_mac, &chassis_mac))) {
++            free(err_str);
++            free_remote_chassis_macs();
++            return;
++        }
++
++        ofpbuf_clear(ofpacts_p);
++        match_init_catchall(&match);
++
++
++        match_set_dl_src(&match, chassis_mac);
++
++        conj = ofpact_put_CONJUNCTION(ofpacts_p);
++        conj->id = CHASSIS_MAC_TO_ROUTER_MAC_CONJID;
++        conj->n_clauses = 2;
++        conj->clause = 0;
++        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 180, 0,
++                        &match, ofpacts_p, hc_uuid);
++    }
++
++    free_remote_chassis_macs();
++}
++
++static void
++put_replace_chassis_mac_flows(const struct simap *ct_zones,
++                              const struct
++                              sbrec_port_binding *localnet_port,
++                              const struct hmap *local_datapaths,
++                              struct ofpbuf *ofpacts_p,
++                              ofp_port_t ofport,
++                              struct ovn_desired_flow_table *flow_table)
++{
++    /* Packets arriving on localnet port, could have been routed on
++     * source chassis and hence will have a chassis mac.
++     * conj_match will match source mac with chassis macs conjunction
++     * and replace it with corresponding router port mac.
++     */
++    struct local_datapath *ld = get_local_datapath(local_datapaths,
++                                                   localnet_port->datapath->
++                                                   tunnel_key);
++    ovs_assert(ld);
++
++    int tag = localnet_port->tag ? *localnet_port->tag : 0;
++    struct zone_ids zone_ids = get_zone_ids(localnet_port, ct_zones);
++
++    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;
++        char *err_str = NULL;
++        struct match match;
++        struct ofpact_mac *replace_mac;
++
++        ovs_assert(rport_binding->n_mac == 1);
++        if ((err_str = str_to_mac(rport_binding->mac[0], &router_port_mac))) {
++            /* 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;
++        }
++        ofpbuf_clear(ofpacts_p);
++        match_init_catchall(&match);
++
++        /* Add flow, which will match on conjunction id and will
++         * replace source with router port mac */
++
++        /* Match on ingress port, vlan_id and conjunction id */
++        match_set_in_port(&match, ofport);
++        match_set_conj_id(&match, CHASSIS_MAC_TO_ROUTER_MAC_CONJID);
++
++        if (tag) {
++            match_set_dl_vlan(&match, htons(tag), 0);
++        } else {
++            match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
++        }
++
++        /* Actions */
++
++        if (tag) {
++            ofpact_put_STRIP_VLAN(ofpacts_p);
++        }
++        load_logical_ingress_metadata(localnet_port, &zone_ids, ofpacts_p);
++        replace_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
++        replace_mac->mac = router_port_mac;
++
++        /* Resubmit to first logical ingress pipeline table. */
++        put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
++        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG,
++                        180, 0, &match, ofpacts_p, hc_uuid);
++
++        /* Provide second search criteria, i.e localnet port's
++         * vlan ID for conjunction flow */
++        struct ofpact_conjunction *conj;
++        ofpbuf_clear(ofpacts_p);
++        match_init_catchall(&match);
++
++        if (tag) {
++            match_set_dl_vlan(&match, htons(tag), 0);
++        } else {
++            match_set_dl_tci_masked(&match, 0, htons(VLAN_CFI));
++        }
++
++        conj = ofpact_put_CONJUNCTION(ofpacts_p);
++        conj->id = CHASSIS_MAC_TO_ROUTER_MAC_CONJID;
++        conj->n_clauses = 2;
++        conj->clause = 1;
++        ofctrl_add_flow(flow_table, OFTABLE_PHY_TO_LOG, 180, 0, &match,
++                        ofpacts_p, hc_uuid);
++    }
++}
++
+ static void
+ put_replace_router_port_mac_flows(struct ovsdb_idl_index
+                                   *sbrec_port_binding_by_name,
+@@ -931,6 +1133,11 @@ consider_port_binding(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+                             &binding->header_.uuid);
+         }
+ 
++        if (!strcmp(binding->type, "localnet")) {
++            put_replace_chassis_mac_flows(ct_zones, binding, local_datapaths,
++                                          ofpacts_p, ofport, flow_table);
++        }
++
+         /* Table 65, Priority 100.
+          * =======================
+          *
+@@ -1224,6 +1431,7 @@ 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,
++             const struct sbrec_chassis_table *chassis_table,
+              enum mf_field_id mff_ovn_geneve,
+              const struct ovsrec_bridge *br_int,
+              const struct sbrec_chassis *chassis,
+@@ -1369,6 +1577,8 @@ physical_run(struct ovsdb_idl_index *sbrec_port_binding_by_name,
+     struct ofpbuf ofpacts;
+     ofpbuf_init(&ofpacts, 0);
+ 
++    put_chassis_mac_conj_id_flow(chassis_table, chassis, &ofpacts, flow_table);
++
+     /* 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;
+diff --git a/ovn/controller/physical.h b/ovn/controller/physical.h
+index c5544e8de..5934ab13e 100644
+--- a/ovn/controller/physical.h
++++ b/ovn/controller/physical.h
+@@ -46,6 +46,7 @@ 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 *,
++                  const struct sbrec_chassis_table *chassis_table,
+                   enum mf_field_id mff_ovn_geneve,
+                   const struct ovsrec_bridge *br_int,
+                   const struct sbrec_chassis *chassis,
+diff --git a/ovn/ovn-architecture.7.xml b/ovn/ovn-architecture.7.xml
+index fa163c023..c43f16db7 100644
+--- a/ovn/ovn-architecture.7.xml
++++ b/ovn/ovn-architecture.7.xml
+@@ -1463,7 +1463,7 @@
+         (MAC,VLAN) tuple will seen by physical network from other chassis as
+         well, which could cause these issues:
+       </p>
+-      
++
+       <ul>
+         <li>
+           Continuous MAC moves in top-of-rack switch (ToR).
+@@ -1479,9 +1479,11 @@
+ 
+     <li>
+       The destination chassis receives the packet via the localnet port and
+-      sends it to the integration bridge. The packet enters the
+-      ingress pipeline and then egress pipeline of the destination localnet
+-      logical switch and finally gets delivered to the destination VM port.
++      sends it to the integration bridge. Before entering the integration
++      bridge the source mac of the packet will be replaced with
++      router port mac again. The packet enters the ingress pipeline and
++      then egress pipeline of the destination localnet logical switch and
++      finally gets delivered to the destination VM port.
+     </li>
+   </ol>
+ 
+diff --git a/tests/ovn.at b/tests/ovn.at
+index fe5d7f130..951a55226 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -14409,6 +14409,14 @@ hv_to_chassis_mac () {
+     esac
+ }
+ 
++lrp_to_lrp_mac () {
++     case $1 in dnl (
++        router-to-ls[[1]]) echo 000001010203 ;; dnl (
++        router-to-ls[[2]]) echo 000001010205 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
+ ip_to_hex() {
+        printf "%02x%02x%02x%02x" "$@"
+ }
+@@ -14476,7 +14484,9 @@ test_ip() {
+             # (and checksum).
+             outport_num=`vif_to_num $outport`
+             out_lrp=`vif_to_lrp $outport`
+-            echo f000000000${outport_num}aabbccddee${hv_num}${hv_num}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
++            lrp_mac=`lrp_to_lrp_mac $out_lrp`
++            echo f000000000${outport_num}${lrp_mac}08004500001c00000000"3f1101"00${src_ip}${dst_ip}0035111100080000
++
+         fi >> $outport.expected
+     done
+ }
+@@ -16519,7 +16529,7 @@ test_ip() {
+                 out_lrp=`vif_to_lrp $outport`
+                 # For North-South, packet will come via gateway chassis, i.e hv3
+                 if test $inport = vif-north; then
+-                    echo f00000000011aabbccddee3308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected
++                    echo f0000000001100000101020308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected
+                 fi
+                 if test $outport = vif-north; then
+                     echo f0f00000001100000101020708004500001c000000003e110200${src_ip}${dst_ip}0035111100080000 >> $outport.expected
+-- 
+2.23.0
+
diff --git a/SOURCES/0005-system-ovn-Add-IPv6-NAT-test-cases.patch b/SOURCES/0005-system-ovn-Add-IPv6-NAT-test-cases.patch
new file mode 100644
index 0000000..82baf4b
--- /dev/null
+++ b/SOURCES/0005-system-ovn-Add-IPv6-NAT-test-cases.patch
@@ -0,0 +1,918 @@
+From fcf41d43c6286f6794a690541f7ac657b216f46f Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 25 Oct 2019 11:58:32 -0400
+Subject: [PATCH 5/5] system-ovn: Add IPv6 NAT test cases
+
+These tests failed prior to the changes leading up to this one.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ tests/system-ovn.at | 862 +++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 860 insertions(+), 2 deletions(-)
+
+diff --git a/tests/system-ovn.at b/tests/system-ovn.at
+index f88ad31e4..b3f90aae2 100644
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -176,6 +176,186 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+ /connection dropped.*/d"])
+ AT_CLEANUP
+ 
++AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, SNAT and DNAT - IPv6])
++AT_KEYWORDS([ovnnat])
++
++CHECK_CONNTRACK()
++CHECK_CONNTRACK_NAT()
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++# Logical network:
++# Two LRs - R1 and R2 that are connected to each other via LS "join"
++# in fd00::/64 network. R1 has switchess foo (fd11::/64) and
++# bar (fd12::/64) connected to it. R2 has alice (fd21::/64) connected
++# to it.  R2 is a gateway router on which we add NAT rules.
++#
++#    foo -- R1 -- join - R2 -- alice
++#           |
++#    bar ----
++
++ovn-nbctl create Logical_Router name=R1
++ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
++
++ovn-nbctl ls-add foo
++ovn-nbctl ls-add bar
++ovn-nbctl ls-add alice
++ovn-nbctl ls-add join
++
++# Connect foo to R1
++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64
++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
++    type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
++
++# Connect bar to R1
++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64
++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
++    type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
++
++# Connect alice to R2
++ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 fd21::1/64
++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
++    type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
++
++# Connect R1 to join
++ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 fd00::1/64
++ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
++    type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
++
++# Connect R2 to join
++ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 fd00::2/64
++ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
++    type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
++
++# Static routes.
++ovn-nbctl lr-route-add R1 fd21::/64 fd00::2
++ovn-nbctl lr-route-add R2 fd11::/64 fd00::1
++ovn-nbctl lr-route-add R2 fd12::/64 fd00::1
++
++# Logical port 'foo1' in switch 'foo'.
++ADD_NAMESPACES(foo1)
++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \
++         "fd11::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo1 \
++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2"
++
++# Logical port 'alice1' in switch 'alice'.
++ADD_NAMESPACES(alice1)
++ADD_VETH(alice1, alice1, br-int, "fd21::2/64", "f0:00:00:01:02:04", \
++         "fd21::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd21::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add alice alice1 \
++-- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd21::2"
++
++# Logical port 'bar1' in switch 'bar'.
++ADD_NAMESPACES(bar1)
++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:05", \
++         "fd12::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add bar bar1 \
++-- lsp-set-addresses bar1 "f0:00:00:01:02:05 fd12::2"
++
++# Add a DNAT rule.
++ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip=\"fd11::2\" \
++    external_ip=\"fd30::2\" -- add logical_router R2 nat @nat
++
++# Add a SNAT rule
++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=\"fd12::2\" \
++    external_ip=\"fd30::1\" -- add logical_router R2 nat @nat
++
++# wait for ovn-controller to catch up.
++ovn-nbctl --wait=hv sync
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd30::1)'])
++
++# 'alice1' should be able to ping 'foo1' directly.
++NS_CHECK_EXEC([alice1], [ping -6 -v -q -c 3 -i 0.3 -w 2 fd11::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# North-South DNAT: 'alice1' should also be able to ping 'foo1' via fd30::2
++NS_CHECK_EXEC([alice1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Check conntrack entries.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd21::2) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd21::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd21::2,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic
++# from fd30::1
++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd21::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that SNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd12::2,dst=fd21::2,id=<cleared>,type=128,code=0),reply=(src=fd21::2,dst=fd30::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# Add static routes to handle east-west NAT.
++ovn-nbctl lr-route-add R1 fd30::/64 fd00::2
++
++# wait for ovn-controller to catch up.
++ovn-nbctl --wait=hv sync
++
++# Flush conntrack entries for easier output parsing of next test.
++AT_CHECK([ovs-appctl dpctl/flush-conntrack])
++
++# East-west DNAT and SNAT: 'bar1' pings fd30::2. 'foo1' receives it.
++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# As we have a static route that sends all packets with destination
++# fd30::2 to R2, it hits the DNAT rule and converts fd30::2 to fd11::2
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::2) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd12::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd12::2,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# As we have a SNAT rule that converts fd12::2 to fd30::1, the source is
++# SNATted and 'foo1' receives it.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd12::2,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd30::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++AT_CLEANUP
++
+ AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, easy SNAT])
+ AT_KEYWORDS([ovnnat])
+ 
+@@ -286,6 +466,118 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+ /connection dropped.*/d"])
+ AT_CLEANUP
+ 
++AT_SETUP([ovn -- 2 LRs connected via LS, gateway router, easy SNAT - IPv6])
++AT_KEYWORDS([ovnnat])
++
++CHECK_CONNTRACK()
++CHECK_CONNTRACK_NAT()
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++# Logical network:
++# Two LRs - R1 and R2 that are connected to each other via LS "join"
++# in fd20::/64 network. R1 has switchess foo (fd10::/64) connected
++# to it.  R2 has alice (fd30::/64) connected to it.
++# R2 is a gateway router on which we add NAT rules.
++#
++#    foo -- R1 -- join - R2 -- alice
++
++ovn-nbctl lr-add R1
++ovn-nbctl lr-add R2 -- set Logical_Router R2 options:chassis=hv1
++
++ovn-nbctl ls-add foo
++ovn-nbctl ls-add alice
++ovn-nbctl ls-add join
++
++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd10::1/64
++ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 fd30::1/64
++ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 fd20::1/64
++ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 fd20::2/64
++
++# Connect foo to R1
++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
++    type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
++
++# Connect alice to R2
++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
++    type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
++
++# Connect R1 to join
++ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
++    type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
++
++# Connect R2 to join
++ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
++    type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
++
++# Static routes.
++ovn-nbctl lr-route-add R1 fd30::/64 fd20::2
++ovn-nbctl lr-route-add R2 fd10::/64 fd20::1
++
++# Logical port 'foo1' in switch 'foo'.
++ADD_NAMESPACES(foo1)
++ADD_VETH(foo1, foo1, br-int, "fd10::2/64", "f0:00:00:01:02:03", \
++         "fd10::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd10::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo1 \
++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd10::2"
++
++# Logical port 'alice1' in switch 'alice'.
++ADD_NAMESPACES(alice1)
++ADD_VETH(alice1, alice1, br-int, "fd30::2/64", "f0:00:00:01:02:04", \
++         "fd30::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd30::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add alice alice1 \
++-- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd30::2"
++
++# Add a SNAT rule
++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip=\"fd10::2\" \
++    external_ip=\"fd30::1\" -- add logical_router R2 nat @nat
++
++ovn-nbctl --wait=hv sync
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd30::1)'])
++
++# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic
++# from fd30::1
++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd30::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that SNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd10::2,dst=fd30::2,id=<cleared>,type=128,code=0),reply=(src=fd30::2,dst=fd30::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++AT_CLEANUP
++
+ AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT])
+ AT_KEYWORDS([ovnnat])
+ 
+@@ -485,9 +777,237 @@ NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 172.16.1.3 | FORMAT_PING], \
+ ])
+ 
+ # We verify that SNAT indeed happened via 'dump-conntrack' command.
+-AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=<cleared>,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=<cleared>,type=0,code=0),zone=<cleared>
++])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++AT_CLEANUP
++
++AT_SETUP([ovn -- multiple gateway routers, SNAT and DNAT - IPv6])
++AT_KEYWORDS([ovnnat])
++
++CHECK_CONNTRACK()
++CHECK_CONNTRACK_NAT()
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++# Logical network:
++# Three LRs - R1, R2 and R3 that are connected to each other via LS "join"
++# in fd20::/64 network. R1 has switchess foo (fd11::/64) and
++# bar (fd12::/64) connected to it. R2 has alice (fd30::/64) connected
++# to it.  R3 has bob (fd30::/64) connected to it. Note how both alice and
++# bob have the same subnet behind it.  We are trying to simulate external
++# network via those 2 switches. In real world the switch ports of these
++# switches will have addresses set as "unknown" to make them learning switches.
++# Or those switches will be "localnet" ones.
++#
++#    foo -- R1 -- join - R2 -- alice
++#           |          |
++#    bar ----          - R3 --- bob
++
++ovn-nbctl create Logical_Router name=R1
++ovn-nbctl create Logical_Router name=R2 options:chassis=hv1
++ovn-nbctl create Logical_Router name=R3 options:chassis=hv1
++
++ovn-nbctl ls-add foo
++ovn-nbctl ls-add bar
++ovn-nbctl ls-add alice
++ovn-nbctl ls-add bob
++ovn-nbctl ls-add join
++
++# Connect foo to R1
++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64
++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
++    type=router options:router-port=foo addresses=\"00:00:01:01:02:03\"
++
++# Connect bar to R1
++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64
++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
++    type=router options:router-port=bar addresses=\"00:00:01:01:02:04\"
++
++# Connect alice to R2
++ovn-nbctl lrp-add R2 alice 00:00:02:01:02:03 fd30::1/64
++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
++    type=router options:router-port=alice addresses=\"00:00:02:01:02:03\"
++
++# Connect bob to R3
++ovn-nbctl lrp-add R3 bob 00:00:03:01:02:03 fd30::2/64
++ovn-nbctl lsp-add bob rp-bob -- set Logical_Switch_Port rp-bob \
++    type=router options:router-port=bob addresses=\"00:00:03:01:02:03\"
++
++# Connect R1 to join
++ovn-nbctl lrp-add R1 R1_join 00:00:04:01:02:03 fd20::1/64
++ovn-nbctl lsp-add join r1-join -- set Logical_Switch_Port r1-join \
++    type=router options:router-port=R1_join addresses='"00:00:04:01:02:03"'
++
++# Connect R2 to join
++ovn-nbctl lrp-add R2 R2_join 00:00:04:01:02:04 fd20::2/64
++ovn-nbctl lsp-add join r2-join -- set Logical_Switch_Port r2-join \
++    type=router options:router-port=R2_join addresses='"00:00:04:01:02:04"'
++
++# Connect R3 to join
++ovn-nbctl lrp-add R3 R3_join 00:00:04:01:02:05 fd20::3/64
++ovn-nbctl lsp-add join r3-join -- set Logical_Switch_Port r3-join \
++    type=router options:router-port=R3_join addresses='"00:00:04:01:02:05"'
++
++# Install static routes with source ip address as the policy for routing.
++# We want traffic from 'foo' to go via R2 and traffic of 'bar' to go via R3.
++ovn-nbctl --policy="src-ip" lr-route-add R1 fd11::/64 fd20::2
++ovn-nbctl --policy="src-ip" lr-route-add R1 fd12::/64 fd20::3
++
++# Static routes.
++ovn-nbctl lr-route-add R2 fd11::/64 fd20::1
++ovn-nbctl lr-route-add R2 fd12::/64 fd20::1
++ovn-nbctl lr-route-add R3 fd11::/64 fd20::1
++ovn-nbctl lr-route-add R3 fd12::/64 fd20::1
++
++# For gateway routers R2 and R3, set a force SNAT rule.
++ovn-nbctl set logical_router R2 options:dnat_force_snat_ip=fd20::2
++ovn-nbctl set logical_router R3 options:dnat_force_snat_ip=fd20::3
++
++# Logical port 'foo1' in switch 'foo'.
++ADD_NAMESPACES(foo1)
++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \
++         "fd11::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo1 \
++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2"
++
++# Logical port 'alice1' in switch 'alice'.
++ADD_NAMESPACES(alice1)
++ADD_VETH(alice1, alice1, br-int, "fd30::3/64", "f0:00:00:01:02:04", \
++         "fd30::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd30::3 | grep tentative)" = ""])
++ovn-nbctl lsp-add alice alice1 \
++-- lsp-set-addresses alice1 "f0:00:00:01:02:04 fd30::3"
++
++# Logical port 'bar1' in switch 'bar'.
++ADD_NAMESPACES(bar1)
++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:05", \
++         "fd12::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add bar bar1 \
++-- lsp-set-addresses bar1 "f0:00:00:01:02:05 fd12::2"
++
++# Logical port 'bob1' in switch 'bob'.
++ADD_NAMESPACES(bob1)
++ADD_VETH(bob1, bob1, br-int, "fd30::4/64", "f0:00:00:01:02:06", \
++         "fd30::2")
++OVS_WAIT_UNTIL([test "$(ip netns exec bob1 ip a | grep fd30::4 | grep tentative)" = ""])
++ovn-nbctl lsp-add bob bob1 \
++-- lsp-set-addresses bob1 "f0:00:00:01:02:06 fd30::4"
++
++# External IPs -- 30.0.0.N --> fd40::N (from IPv4 version of test case)
++
++# Router R2
++# Add a DNAT rule.
++ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \
++    external_ip='"fd40::2"' -- add logical_router R2 nat @nat
++
++# Add a SNAT rule
++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd11::2"' \
++    external_ip='"fd40::1"' -- add logical_router R2 nat @nat
++
++# Router R3
++# Add a DNAT rule.
++ovn-nbctl -- --id=@nat create nat type="dnat" logical_ip='"fd11::2"' \
++    external_ip='"fd40::3"' -- add logical_router R3 nat @nat
++
++# Add a SNAT rule
++ovn-nbctl -- --id=@nat create nat type="snat" logical_ip='"fd12::2"' \
++    external_ip='"fd40::4"' -- add logical_router R3 nat @nat
++
++# wait for ovn-controller to catch up.
++ovn-nbctl --wait=hv sync
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd40::4)'])
++
++# North-South DNAT: 'alice1' should be able to ping 'foo1' via fd30::2
++NS_CHECK_EXEC([alice1], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Check conntrack entries.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::3) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd30::3,dst=fd40::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd30::3,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# But foo1 should receive traffic from fd20::2
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::2) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd30::3,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd20::2,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# North-South DNAT: 'bob1' should be able to ping 'foo1' via fd40::3
++NS_CHECK_EXEC([bob1], [ping -6 -q -c 3 -i 0.3 -w 2 fd40::3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Check conntrack entries.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd30::4) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd30::4,dst=fd40::3,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd30::4,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# But foo1 should receive traffic from fd20::3
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd30::4,dst=fd11::2,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd20::3,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# South-North SNAT: 'bar1' pings 'bob1'. But 'bob1' receives traffic
++# from fd40::4
++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that SNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::4) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd12::2,dst=fd30::4,id=<cleared>,type=128,code=0),reply=(src=fd30::4,dst=fd40::4,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# South-North SNAT: 'foo1' pings 'alice1'. But 'alice1' receives traffic
++# from fd40::1
++NS_CHECK_EXEC([foo1], [ping -6 -q -c 3 -i 0.3 -w 2 fd30::3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that SNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd40::1) | \
+ sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
+-icmp,orig=(src=192.168.1.2,dst=172.16.1.3,id=<cleared>,type=8,code=0),reply=(src=172.16.1.3,dst=30.0.0.1,id=<cleared>,type=0,code=0),zone=<cleared>
++icmpv6,orig=(src=fd11::2,dst=fd30::3,id=<cleared>,type=128,code=0),reply=(src=fd30::3,dst=fd40::1,id=<cleared>,type=129,code=0),zone=<cleared>
+ ])
+ 
+ OVS_APP_EXIT_AND_WAIT([ovn-controller])
+@@ -1370,6 +1890,162 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+ /connection dropped.*/d"])
+ AT_CLEANUP
+ 
++AT_SETUP([ovn -- DNAT and SNAT on distributed router - N/S - IPv6])
++AT_KEYWORDS([ovnnat])
++
++CHECK_CONNTRACK()
++CHECK_CONNTRACK_NAT()
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++# Logical network:
++# One LR R1 with switches foo (fd11::/64), bar (fd12::/64),
++# and alice (fd20::/64) connected to it.  The port between R1 and
++# alice is the router gateway port where the R1 NAT rules are applied.
++#
++#    foo -- R1 -- alice
++#           |
++#    bar ----
++
++ovn-nbctl lr-add R1
++
++ovn-nbctl ls-add foo
++ovn-nbctl ls-add bar
++ovn-nbctl ls-add alice
++
++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64
++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64
++ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 fd20::1/64 \
++    -- set Logical_Router_Port alice options:redirect-chassis=hv1
++
++# Connect foo to R1
++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
++    type=router options:router-port=foo \
++    -- lsp-set-addresses rp-foo router
++
++# Connect bar to R1
++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
++    type=router options:router-port=bar \
++    -- lsp-set-addresses rp-bar router
++
++# Connect alice to R1
++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
++    type=router options:router-port=alice \
++    -- lsp-set-addresses rp-alice router
++
++# Logical port 'foo1' in switch 'foo'.
++ADD_NAMESPACES(foo1)
++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \
++         "fd11::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo1 \
++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2"
++
++# Logical port 'foo2' in switch 'foo'.
++ADD_NAMESPACES(foo2)
++ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \
++         "fd11::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo2 ip a | grep fd11::3 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo2 \
++-- lsp-set-addresses foo2 "f0:00:00:01:02:06 fd11::3"
++
++# Logical port 'bar1' in switch 'bar'.
++ADD_NAMESPACES(bar1)
++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \
++         "fd12::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add bar bar1 \
++-- lsp-set-addresses bar1 "f0:00:00:01:02:04 fd12::2"
++
++# Logical port 'alice1' in switch 'alice'.
++ADD_NAMESPACES(alice1)
++ADD_VETH(alice1, alice1, br-int, "fd20::2/64", "f0:00:00:01:02:05", \
++         "fd20::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd20::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add alice alice1 \
++-- lsp-set-addresses alice1 "f0:00:00:01:02:05 fd20::2"
++
++ovn-nbctl --wait=hv sync
++
++# Add DNAT rules
++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02:03:04])
++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd11::3 foo2 00:00:02:02:03:05])
++
++# Add a SNAT rule
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64])
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd12::/64])
++
++ovn-nbctl --wait=hv sync
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)'])
++
++# North-South DNAT: 'alice1' pings 'foo1' using fd20::3
++NS_CHECK_EXEC([alice1], [ping -6 -q -c 3 -i 0.3 -w 2 fd20::3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that DNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::3) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd20::2,dst=fd20::3,id=<cleared>,type=128,code=0),reply=(src=fd11::2,dst=fd20::2,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++# South-North SNAT: 'foo2' pings 'alice1'. But 'alice1' receives traffic
++# from 172.16.1.4
++NS_CHECK_EXEC([foo2], [ping -6 -q -c 3 -i 0.3 -w 2 fd20::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that SNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd11::3,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++AT_CHECK([ovs-appctl dpctl/flush-conntrack])
++
++# South-North SNAT: 'bar1' pings 'alice1'. But 'alice1' receives traffic
++# from fd20::1
++NS_CHECK_EXEC([bar1], [ping -6 -q -c 3 -i 0.3 -w 2 fd20::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that SNAT indeed happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd12::2,dst=fd20::2,id=<cleared>,type=128,code=0),reply=(src=fd20::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++AT_CLEANUP
++
+ AT_SETUP([ovn -- DNAT and SNAT on distributed router - E/W])
+ AT_KEYWORDS([ovnnat])
+ 
+@@ -1547,6 +2223,188 @@ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+ /connection dropped.*/d"])
+ AT_CLEANUP
+ 
++AT_SETUP([ovn -- DNAT and SNAT on distributed router - E/W - IPv6])
++AT_KEYWORDS([ovnnat])
++
++CHECK_CONNTRACK()
++CHECK_CONNTRACK_NAT()
++ovn_start
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++# Logical network:
++# One LR R1 with switches foo (fd11::/64), bar (fd12::/64),
++# and alice (fd20::/64) connected to it.  The port between R1 and
++# alice is the router gateway port where the R1 NAT rules are applied.
++#
++#    foo -- R1 -- alice
++#           |
++#    bar ----
++
++ovn-nbctl lr-add R1
++
++ovn-nbctl ls-add foo
++ovn-nbctl ls-add bar
++ovn-nbctl ls-add alice
++
++ovn-nbctl lrp-add R1 foo 00:00:01:01:02:03 fd11::1/64
++ovn-nbctl lrp-add R1 bar 00:00:01:01:02:04 fd12::1/64
++ovn-nbctl lrp-add R1 alice 00:00:02:01:02:03 fd20::1/64 \
++    -- set Logical_Router_Port alice options:redirect-chassis=hv1
++
++# Connect foo to R1
++ovn-nbctl lsp-add foo rp-foo -- set Logical_Switch_Port rp-foo \
++    type=router options:router-port=foo \
++    -- lsp-set-addresses rp-foo router
++
++# Connect bar to R1
++ovn-nbctl lsp-add bar rp-bar -- set Logical_Switch_Port rp-bar \
++    type=router options:router-port=bar \
++    -- lsp-set-addresses rp-bar router
++
++# Connect alice to R1
++ovn-nbctl lsp-add alice rp-alice -- set Logical_Switch_Port rp-alice \
++    type=router options:router-port=alice \
++    -- lsp-set-addresses rp-alice router
++
++# Logical port 'foo1' in switch 'foo'.
++ADD_NAMESPACES(foo1)
++ADD_VETH(foo1, foo1, br-int, "fd11::2/64", "f0:00:00:01:02:03", \
++         "fd11::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo1 ip a | grep fd11::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo1 \
++-- lsp-set-addresses foo1 "f0:00:00:01:02:03 fd11::2"
++
++# Logical port 'foo2' in switch 'foo'.
++ADD_NAMESPACES(foo2)
++ADD_VETH(foo2, foo2, br-int, "fd11::3/64", "f0:00:00:01:02:06", \
++         "fd11::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec foo2 ip a | grep fd11::3 | grep tentative)" = ""])
++ovn-nbctl lsp-add foo foo2 \
++-- lsp-set-addresses foo2 "f0:00:00:01:02:06 fd11::3"
++
++# Logical port 'bar1' in switch 'bar'.
++ADD_NAMESPACES(bar1)
++ADD_VETH(bar1, bar1, br-int, "fd12::2/64", "f0:00:00:01:02:04", \
++         "fd12::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec bar1 ip a | grep fd12::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add bar bar1 \
++-- lsp-set-addresses bar1 "f0:00:00:01:02:04 fd12::2"
++
++# Logical port 'alice1' in switch 'alice'.
++ADD_NAMESPACES(alice1)
++ADD_VETH(alice1, alice1, br-int, "fd20::2/64", "f0:00:00:01:02:05", \
++         "fd20::1")
++OVS_WAIT_UNTIL([test "$(ip netns exec alice1 ip a | grep fd20::2 | grep tentative)" = ""])
++ovn-nbctl lsp-add alice alice1 \
++-- lsp-set-addresses alice1 "f0:00:00:01:02:05 fd20::2"
++
++# Add DNAT rules
++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::3 fd11::2 foo1 00:00:02:02:03:04])
++AT_CHECK([ovn-nbctl lr-nat-add R1 dnat_and_snat fd20::4 fd12::2 bar1 00:00:02:02:03:05])
++
++# Add a SNAT rule
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd11::/64])
++AT_CHECK([ovn-nbctl lr-nat-add R1 snat fd20::1 fd12::/64])
++
++ovn-nbctl --wait=hv sync
++OVS_WAIT_UNTIL([ovs-ofctl dump-flows br-int | grep 'nat(src=fd20::1)'])
++
++echo "------ hv dump ------"
++ovs-ofctl show br-int
++ovs-ofctl dump-flows br-int
++echo "---------------------"
++
++# East-West No NAT: 'foo1' pings 'bar1' using fd12::2.
++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that no NAT happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd12::2) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | wc -l], [0], [0
++])
++
++# East-West No NAT: 'foo2' pings 'bar1' using fd12::2.
++NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd12::2 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that no NAT happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd12::2) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | wc -l], [0], [0
++])
++
++# East-West No NAT: 'bar1' pings 'foo2' using fd11::3.
++NS_CHECK_EXEC([bar1], [ping -q -c 3 -i 0.3 -w 2 fd11::3 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# We verify that no NAT happened via 'dump-conntrack' command.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd12::2) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/' | wc -l], [0], [0
++])
++
++# East-West NAT: 'foo1' pings 'bar1' using fd20::4.
++NS_CHECK_EXEC([foo1], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Check conntrack entries.  First SNAT of 'foo1' address happens.
++# Then DNAT of 'bar1' address happens (listed first below).
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::4) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd11::2,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
++icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++AT_CHECK([ovs-appctl dpctl/flush-conntrack])
++
++# East-West NAT: 'foo2' pings 'bar1' using fd20::4.
++NS_CHECK_EXEC([foo2], [ping -q -c 3 -i 0.3 -w 2 fd20::4 | FORMAT_PING], \
++[0], [dnl
++3 packets transmitted, 3 received, 0% packet loss, time 0ms
++])
++
++# Check conntrack entries.  First SNAT of 'foo2' address happens.
++# Then DNAT of 'bar1' address happens (listed first below).
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd20::1) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++icmpv6,orig=(src=fd11::3,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd20::4,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
++icmpv6,orig=(src=fd20::1,dst=fd20::4,id=<cleared>,type=128,code=0),reply=(src=fd12::2,dst=fd20::1,id=<cleared>,type=129,code=0),zone=<cleared>
++])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++AT_CLEANUP
++
+ AT_SETUP([ovn -- 2 LSs IGMP])
+ AT_KEYWORDS([ovnigmp])
+ 
+-- 
+2.23.0
+
diff --git a/SOURCES/0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch b/SOURCES/0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch
new file mode 100644
index 0000000..b31b73d
--- /dev/null
+++ b/SOURCES/0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch
@@ -0,0 +1,232 @@
+From d8573c28c74a4e706d1b16dc84ac28a74fe624f0 Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Fri, 1 Nov 2019 01:27:36 +0000
+Subject: [PATCH 06/12] OVN: ADD nbctl cli to mark a dnat_and_snat rule as
+ stateless
+
+Adding ovn-nbctl to mark a dnat_and_snat rule as stateless.
+This configuration will be added to "options" column of NAT table.
+
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/ovn-nb.ovsschema          |  6 ++++--
+ ovn/ovn-nb.xml                |  5 +++++
+ ovn/utilities/ovn-nbctl.8.xml | 12 +++++++++++-
+ ovn/utilities/ovn-nbctl.c     | 30 +++++++++++++++++++++++++++-
+ tests/ovn-nbctl.at            | 37 +++++++++++++++++++++++++++++++++++
+ 5 files changed, 86 insertions(+), 4 deletions(-)
+
+diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
+index 2c87cbba7..084305b24 100644
+--- a/ovn/ovn-nb.ovsschema
++++ b/ovn/ovn-nb.ovsschema
+@@ -1,7 +1,7 @@
+ {
+     "name": "OVN_Northbound",
+-    "version": "5.16.0",
+-    "cksum": "923459061 23095",
++    "version": "5.17.0",
++    "cksum": "1128988054 23237",
+     "tables": {
+         "NB_Global": {
+             "columns": {
+@@ -345,6 +345,8 @@
+                                                              "snat",
+                                                              "dnat_and_snat"
+                                                                ]]}}},
++                "options": {"type": {"key": "string", "value": "string",
++                                     "min": 0, "max": "unlimited"}},
+                 "external_ids": {
+                     "type": {"key": "string", "value": "string",
+                              "min": 0, "max": "unlimited"}}},
+diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
+index bce7463d2..cf2c5136a 100644
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -2324,6 +2324,11 @@
+       </p>
+     </column>
+ 
++    <column name="options" key="stateless">
++      Indicates if a dnat_and_snat rule should lead to connection
++      tracking state or not.
++    </column>
++
+     <group title="Common Columns">
+       <column name="external_ids">
+         See <em>External IDs</em> at the beginning of this document.
+diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
+index 41d50b694..49aa527a0 100644
+--- a/ovn/utilities/ovn-nbctl.8.xml
++++ b/ovn/utilities/ovn-nbctl.8.xml
+@@ -665,7 +665,7 @@
+     <h1>NAT Commands</h1>
+ 
+     <dl>
+-      <dt>[<code>--may-exist</code>] <code>lr-nat-add</code> <var>router</var> <var>type</var> <var>external_ip</var> <var>logical_ip</var> [<var>logical_port</var> <var>external_mac</var>]</dt>
++      <dt>[<code>--may-exist</code>] [<code>--stateless</code>]<code>lr-nat-add</code> <var>router</var> <var>type</var> <var>external_ip</var> <var>logical_ip</var> [<var>logical_port</var> <var>external_mac</var>]</dt>
+       <dd>
+         <p>
+           Adds the specified NAT to <var>router</var>.
+@@ -681,7 +681,17 @@
+           The <var>logical_port</var> is the name of an existing logical
+           switch port where the <var>logical_ip</var> resides.
+           The <var>external_mac</var> is an Ethernet address.
++          The <var>--stateless</var>
+         </p>
++        <p>
++          When <code>--stateless</code> is specified then it implies that
++          we will be not use connection tracker, i.e internal ip and external
++          ip are 1:1 mapped. This implies that <code>--stateless</code> is
++          applicable only to dnat_and_snat type NAT rules.
++          An external ip with <code>--stateless</code> NAT cannot be shared
++          with any other NAT rule.
++        </p>
++
+         <p>
+           When <var>type</var> is <code>dnat</code>, the externally
+           visible IP address <var>external_ip</var> is DNATted to the
+diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
+index 112cc1d54..1a1d83e65 100644
+--- a/ovn/utilities/ovn-nbctl.c
++++ b/ovn/utilities/ovn-nbctl.c
+@@ -685,6 +685,7 @@ Policy commands:\n\
+   lr-policy-list ROUTER     print policies for ROUTER\n\
+ \n\
+ NAT commands:\n\
++  [--stateless]\n\
+   lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]\n\
+                             add a NAT to ROUTER\n\
+   lr-nat-del ROUTER [TYPE [IP]]\n\
+@@ -3945,6 +3946,13 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
+     }
+ 
+     bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
++    bool stateless = shash_find(&ctx->options, "--stateless") != NULL;
++
++    if (strcmp(nat_type, "dnat_and_snat") && stateless) {
++        ctl_error(ctx, "stateless is not applicable to dnat or snat types");
++        return;
++    }
++
+     int is_snat = !strcmp("snat", nat_type);
+     for (size_t i = 0; i < lr->n_nat; i++) {
+         const struct nbrec_nat *nat = lr->nat[i];
+@@ -3976,10 +3984,25 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
+                     return;
+                 }
+             }
++
++        }
++        if (!strcmp(nat_type, "dnat_and_snat") ||
++            !strcmp(nat->type, "dnat_and_snat")) {
++
++            if (!strcmp(nat->external_ip, external_ip)) {
++                struct smap nat_options = SMAP_INITIALIZER(&nat_options);
++                if (!strcmp(smap_get(&nat->options, "stateless"),
++                            "true") || stateless) {
++                    ctl_error(ctx, "%s, %s: External ip cannot be shared "
++                              "across stateless and stateful NATs",
++                              external_ip, new_logical_ip);
++                }
++            }
+         }
+     }
+ 
+     /* Create the NAT. */
++    struct smap nat_options = SMAP_INITIALIZER(&nat_options);
+     struct nbrec_nat *nat = nbrec_nat_insert(ctx->txn);
+     nbrec_nat_set_type(nat, nat_type);
+     nbrec_nat_set_external_ip(nat, external_ip);
+@@ -3988,7 +4011,12 @@ nbctl_lr_nat_add(struct ctl_context *ctx)
+         nbrec_nat_set_logical_port(nat, logical_port);
+         nbrec_nat_set_external_mac(nat, external_mac);
+     }
++
++    smap_add(&nat_options, "stateless", stateless ? "true":"false");
++    nbrec_nat_set_options(nat, &nat_options);
++
+     free(new_logical_ip);
++    smap_destroy(&nat_options);
+ 
+     /* Insert the NAT into the logical router. */
+     nbrec_logical_router_verify_nat(lr);
+@@ -5707,7 +5735,7 @@ static const struct ctl_command_syntax nbctl_commands[] = {
+     /* NAT commands. */
+     { "lr-nat-add", 4, 6,
+       "ROUTER TYPE EXTERNAL_IP LOGICAL_IP [LOGICAL_PORT EXTERNAL_MAC]", NULL,
+-      nbctl_lr_nat_add, NULL, "--may-exist", RW },
++      nbctl_lr_nat_add, NULL, "--may-exist,--stateless", RW },
+     { "lr-nat-del", 1, 3, "ROUTER [TYPE [IP]]", NULL,
+         nbctl_lr_nat_del, NULL, "--if-exists", RW },
+     { "lr-nat-list", 1, 1, "ROUTER", NULL, nbctl_lr_nat_list, NULL, "", RO },
+diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
+index 620b778b7..d5dce90f4 100644
+--- a/tests/ovn-nbctl.at
++++ b/tests/ovn-nbctl.at
+@@ -533,6 +533,39 @@ snat             30.0.0.1           192.168.1.0/24
+ snat             fd01::1            fd11::/64
+ ])
+ 
++AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0],
++[0
++])
++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat 40.0.0.2 192.168.1.4])
++AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0],
++[1
++])
++
++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat fd21::1 fd11::2])
++AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0],
++[2
++])
++
++AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat fd21::1])
++
++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat 40.0.0.2 192.168.1.4], [1], [],
++[ovn-nbctl: stateless is not applicable to dnat or snat types
++])
++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 snat 40.0.0.2 192.168.1.4], [1], [],
++[ovn-nbctl: stateless is not applicable to dnat or snat types
++])
++AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 40.0.0.2 192.168.1.5], [1], [],
++[ovn-nbctl: 40.0.0.2, 192.168.1.5: External ip cannot be shared across stateless and stateful NATs
++])
++AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 40.0.0.2 192.168.1.5], [1], [],
++[ovn-nbctl: 40.0.0.2, 192.168.1.5: External ip cannot be shared across stateless and stateful NATs
++])
++
++AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 40.0.0.3 192.168.1.6])
++AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat 40.0.0.3 192.168.1.7], [1], [],
++[ovn-nbctl: 40.0.0.3, 192.168.1.7: External ip cannot be shared across stateless and stateful NATs
++])
++
+ dnl Deletes the NATs
+ AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.3], [1], [],
+ [ovn-nbctl: no matching NAT with the type (dnat_and_snat) and external_ip (30.0.0.3)
+@@ -552,8 +585,10 @@ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         L
+ dnat             30.0.0.1           192.168.1.2
+ dnat             fd01::1            fd11::2
+ dnat_and_snat    30.0.0.2           192.168.1.3
++dnat_and_snat    40.0.0.2           192.168.1.4
+ dnat_and_snat    fd01::2            fd11::3
+ snat             30.0.0.1           192.168.1.0/24
++snat             40.0.0.3           192.168.1.6
+ snat             fd01::1            fd11::/64
+ ])
+ 
+@@ -561,8 +596,10 @@ AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat])
+ AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl
+ TYPE             EXTERNAL_IP        LOGICAL_IP            EXTERNAL_MAC         LOGICAL_PORT
+ dnat_and_snat    30.0.0.2           192.168.1.3
++dnat_and_snat    40.0.0.2           192.168.1.4
+ dnat_and_snat    fd01::2            fd11::3
+ snat             30.0.0.1           192.168.1.0/24
++snat             40.0.0.3           192.168.1.6
+ snat             fd01::1            fd11::/64
+ ])
+ 
+-- 
+2.23.0
+
diff --git a/SOURCES/0006-controller-Downgrade-a-warning-log-message.patch b/SOURCES/0006-controller-Downgrade-a-warning-log-message.patch
new file mode 100644
index 0000000..429a227
--- /dev/null
+++ b/SOURCES/0006-controller-Downgrade-a-warning-log-message.patch
@@ -0,0 +1,38 @@
+From 610bac28e0e241b3ea266fb7c3745415c54452f4 Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Fri, 25 Oct 2019 09:34:49 -0400
+Subject: [PATCH 6/6] controller: Downgrade a warning log message
+
+This log message was introduced in commit 5344f24ecb.  It gets hit
+under normal circumstances, so it would be better as a debug message
+instead of a warning.  I also expanded it to clarify that the next
+step will be to create the chassis record.
+
+This was found by trying to run the system-ovn.at tests, and they
+failed because of these unexpected warning log messages.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/chassis.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
+index b74a42cc8..4cd340124 100644
+--- a/ovn/controller/chassis.c
++++ b/ovn/controller/chassis.c
+@@ -484,8 +484,9 @@ chassis_get_record(struct ovsdb_idl_txn *ovnsb_idl_txn,
+         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);
++            VLOG_DBG("Could not find Chassis, will create it"
++                     ": stored (%s) ovs (%s)",
++                     chassis_info_id(&chassis_state), chassis_id);
+             if (ovnsb_idl_txn) {
+                 /* Recreate the chassis record.  */
+                 chassis_rec = sbrec_chassis_insert(ovnsb_idl_txn);
+-- 
+2.23.0
+
diff --git a/SOURCES/0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch b/SOURCES/0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch
new file mode 100644
index 0000000..c9e13f0
--- /dev/null
+++ b/SOURCES/0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch
@@ -0,0 +1,693 @@
+From 22b415470df9cafe1fec9f3095faf14172d1c4a6 Mon Sep 17 00:00:00 2001
+From: Ankur Sharma <ankur.sharma@nutanix.com>
+Date: Fri, 1 Nov 2019 01:27:39 +0000
+Subject: [PATCH 07/12] OVN: Use ip4.src and ip4.dst actions for NAT rules
+
+For dnat_and_snat rules which are meant to be stateless
+instead of using ct_snat/dnat OVN actions, we will use
+ip4.src/ip4.dst.
+
+This actions will do 1:1 mapping to inner ip to external ip,
+while recalculating the checksums.
+
+Signed-off-by: Ankur Sharma <ankur.sharma@nutanix.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml |  33 +++-
+ ovn/northd/ovn-northd.c     |  84 ++++++++--
+ tests/ovn-northd.at         |  95 +++++++++++
+ tests/ovn.at                | 311 ++++++++++++++++++++++++++++++++++++
+ 4 files changed, 508 insertions(+), 15 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 83c90d680..d1af97d15 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1840,7 +1840,10 @@ icmp6 {
+           <var>B</var>, a priority-90 flow matches
+           <code>ip &amp;&amp; ip4.dst == <var>B</var></code> or
+           <code>ip &amp;&amp; ip6.dst == <var>B</var></code>
+-          with an action <code>ct_snat; </code>.
++          with an action <code>ct_snat; </code>. If the NAT rule is of type
++          dnat_and_snat and has <code>stateless=true</code> in the
++          options, then the action would be <code>ip4/6.dst=
++          (<var>B</var>)</code>.
+         </p>
+ 
+         <p>
+@@ -1862,7 +1865,10 @@ icmp6 {
+           <code>ip &amp;&amp;
+           ip6.dst == <var>B</var> &amp;&amp; inport == <var>GW</var></code>
+           where <var>GW</var> is the logical router gateway port, with an
+-          action <code>ct_snat;</code>.
++          action <code>ct_snat;</code>. If the NAT rule is of type
++          dnat_and_snat and has <code>stateless=true</code> in the
++          options, then the action would be <code>ip4/6.dst=
++          (<var>B</var>)</code>.
+         </p>
+ 
+         <p>
+@@ -1990,7 +1996,10 @@ icmp6 {
+         Gateway router is configured to force SNAT any DNATed packet,
+         the above action will be replaced by
+         <code>flags.force_snat_for_dnat = 1; flags.loopback = 1;
+-        ct_dnat(<var>B</var>);</code>.
++        ct_dnat(<var>B</var>);</code>. If the NAT rule is of type
++        dnat_and_snat and has <code>stateless=true</code> in the
++        options, then the action would be <code>ip4/6.dst=
++        (<var>B</var>)</code>.
+       </li>
+ 
+       <li>
+@@ -2024,6 +2033,9 @@ icmp6 {
+           where <var>GW</var> is the logical router gateway port, with an
+           action <code>ct_dnat(<var>B</var>);</code>.  The match will
+           include <code>ip6.dst == <var>B</var></code> in the IPv6 case.
++          If the NAT rule is of type dnat_and_snat and has
++          <code>stateless=true</code> in the options, then the action
++          would be <code>ip4/6.dst=(<var>B</var>)</code>.
+         </p>
+ 
+         <p>
+@@ -2698,7 +2710,10 @@ nd_ns {
+           matches <code>ip &amp;&amp; ip4.src == <var>B</var>
+           &amp;&amp; outport == <var>GW</var></code>, where <var>GW</var>
+           is the logical router gateway port, with an action
+-          <code>ct_dnat;</code>.
++          <code>ct_dnat;</code>. If the NAT rule is of type
++          dnat_and_snat and has <code>stateless=true</code> in the
++          options, then the action would be <code>ip4/6.src=
++          (<var>B</var>)</code>.
+         </p>
+ 
+         <p>
+@@ -2765,7 +2780,10 @@ nd_ns {
+           <code>ip &amp;&amp; ip4.src == <var>A</var></code> with an action
+           <code>ct_snat(<var>B</var>);</code>.  The priority of the flow
+           is calculated based on the mask of <var>A</var>, with matches
+-          having larger masks getting higher priorities.
++          having larger masks getting higher priorities. If the NAT rule is
++          of type dnat_and_snat and has <code>stateless=true</code> in the
++          options, then the action would be <code>ip4/6.src=
++          (<var>B</var>)</code>.
+         </p>
+         <p>
+           A priority-0 logical flow with match <code>1</code> has actions
+@@ -2788,7 +2806,10 @@ nd_ns {
+           logical router gateway port, with an action
+           <code>ct_snat(<var>B</var>);</code>.  The priority of the flow
+           is calculated based on the mask of <var>A</var>, with matches
+-          having larger masks getting higher priorities.
++          having larger masks getting higher priorities. If the NAT rule
++          is of type dnat_and_snat and has <code>stateless=true</code>
++          in the options, then the action would be <code>ip4/6.src=
++          (<var>B</var>)</code>.
+         </p>
+ 
+         <p>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 27e90fcb2..8938e458f 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6779,6 +6779,18 @@ copy_ra_to_sb(struct ovn_port *op, const char *address_mode)
+     smap_destroy(&options);
+ }
+ 
++static inline bool
++lrouter_nat_is_stateless(const struct nbrec_nat *nat)
++{
++    const char *stateless = smap_get(&nat->options, "stateless");
++
++    if (stateless && !strcmp(stateless, "true")) {
++        return true;
++    }
++
++    return false;
++}
++
+ static void
+ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                     struct hmap *lflows, struct shash *meter_groups)
+@@ -7636,6 +7648,7 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+             ovs_be32 ip, mask;
+             struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
+             bool is_v6 = false;
++            bool stateless = lrouter_nat_is_stateless(nat);
+ 
+             char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
+             if (error || mask != OVS_BE32_MAX) {
+@@ -7719,16 +7732,25 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                 if (!od->l3dgw_port) {
+                     /* Gateway router. */
+                     ds_clear(&match);
++                    ds_clear(&actions);
+                     ds_put_format(&match, "ip && ip%s.dst == %s",
+                                   is_v6 ? "6" : "4",
+                                   nat->external_ip);
++                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                       ds_put_format(&actions, "ip%s.dst=%s; next;",
++                                     is_v6 ? "6" : "4", nat->logical_ip);
++                    } else {
++                       ds_put_cstr(&actions, "ct_snat;");
++                    }
++
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 90,
+-                                  ds_cstr(&match), "ct_snat;");
++                                  ds_cstr(&match), ds_cstr(&actions));
+                 } else {
+                     /* Distributed router. */
+ 
+                     /* Traffic received on l3dgw_port is subject to NAT. */
+                     ds_clear(&match);
++                    ds_clear(&actions);
+                     ds_put_format(&match, "ip && ip%s.dst == %s"
+                                           " && inport == %s",
+                                   is_v6 ? "6" : "4",
+@@ -7740,8 +7762,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                         ds_put_format(&match, " && is_chassis_resident(%s)",
+                                       od->l3redirect_port->json_key);
+                     }
++
++                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                        ds_put_format(&actions, "ip%s.dst=%s; next;",
++                                      is_v6 ? "6" : "4", nat->logical_ip);
++                    } else {
++                        ds_put_cstr(&actions, "ct_snat;");
++                    }
++
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_UNSNAT, 100,
+-                                  ds_cstr(&match), "ct_snat;");
++                                  ds_cstr(&match), ds_cstr(&actions));
+ 
+                     /* Traffic received on other router ports must be
+                      * redirected to the central instance of the l3dgw_port
+@@ -7778,8 +7808,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                         ds_put_format(&actions,
+                                       "flags.force_snat_for_dnat = 1; ");
+                     }
+-                    ds_put_format(&actions, "flags.loopback = 1; ct_dnat(%s);",
+-                                  nat->logical_ip);
++
++                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                        ds_put_format(&actions, "flags.loopback = 1; "
++                                      "ip%s.dst=%s; next;",
++                                      is_v6 ? "6" : "4", nat->logical_ip);
++                    } else {
++                        ds_put_format(&actions, "flags.loopback = 1; "
++                                      "ct_dnat(%s);", nat->logical_ip);
++                    }
++
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
+                                   ds_cstr(&match), ds_cstr(&actions));
+                 } else {
+@@ -7799,8 +7837,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                                       od->l3redirect_port->json_key);
+                     }
+                     ds_clear(&actions);
+-                    ds_put_format(&actions, "ct_dnat(%s);",
+-                                  nat->logical_ip);
++
++                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                        ds_put_format(&actions, "ip%s.dst=%s; next;",
++                                      is_v6 ? "6" : "4", nat->logical_ip);
++                    } else {
++                        ds_put_format(&actions, "ct_dnat(%s);",
++                                      nat->logical_ip);
++                    }
++
+                     ovn_lflow_add(lflows, od, S_ROUTER_IN_DNAT, 100,
+                                   ds_cstr(&match), ds_cstr(&actions));
+ 
+@@ -7844,7 +7889,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                     ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
+                                   ETH_ADDR_ARGS(mac));
+                 }
+-                ds_put_format(&actions, "ct_dnat;");
++
++                if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                    ds_put_format(&actions, "ip%s.src=%s; next;",
++                                  is_v6 ? "6" : "4", nat->external_ip);
++                } else {
++                    ds_put_format(&actions, "ct_dnat;");
++                }
++
+                 ovn_lflow_add(lflows, od, S_ROUTER_OUT_UNDNAT, 100,
+                               ds_cstr(&match), ds_cstr(&actions));
+             }
+@@ -7861,7 +7913,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                                   is_v6 ? "6" : "4",
+                                   nat->logical_ip);
+                     ds_clear(&actions);
+-                    ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
++
++                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                        ds_put_format(&actions, "ip%s.src=%s; next;",
++                                      is_v6 ? "6" : "4", nat->external_ip);
++                    } else {
++                        ds_put_format(&actions, "ct_snat(%s);",
++                                      nat->external_ip);
++                    }
+ 
+                     /* The priority here is calculated such that the
+                      * nat->logical_ip with the longest mask gets a higher
+@@ -7891,7 +7950,14 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
+                         ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
+                                       ETH_ADDR_ARGS(mac));
+                     }
+-                    ds_put_format(&actions, "ct_snat(%s);", nat->external_ip);
++
++                    if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
++                        ds_put_format(&actions, "ip%s.src=%s; next;",
++                                      is_v6 ? "6" : "4", nat->external_ip);
++                    } else {
++                        ds_put_format(&actions, "ct_snat(%s);",
++                                      nat->external_ip);
++                    }
+ 
+                     /* The priority here is calculated such that the
+                      * nat->logical_ip with the longest mask gets a higher
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index 42033d589..989ed4f47 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -966,3 +966,98 @@ OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0],
+ ])
+ 
+ AT_CLEANUP
++
++AT_SETUP([ovn -- check stateless dnat_and_snat rule])
++AT_SKIP_IF([test $HAVE_PYTHON = no])
++ovn_start
++
++ovn-sbctl chassis-add gw1 geneve 127.0.0.1
++
++ovn-nbctl lr-add R1
++ovn-nbctl lrp-add R1 R1-S1 02:ac:10:01:00:01 172.16.1.1/24
++
++ovn-nbctl ls-add S1
++ovn-nbctl lsp-add S1 S1-R1
++ovn-nbctl lsp-set-type S1-R1 router
++ovn-nbctl lsp-set-addresses S1-R1 router
++ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
++
++ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
++
++uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1`
++echo "CR-LRP UUID is: " $uuid
++
++# IPV4
++ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
++
++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++wc -l`])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.dst=| wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [0
++])
++
++ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
++
++ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++wc -l`])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.dst=| wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip4.src=| wc -l], [0], [2
++])
++ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
++
++# IPV6
++ovn-nbctl lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
++
++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++wc -l`])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.dst=| wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [0
++])
++
++ovn-nbctl lr-nat-del R1 dnat_and_snat  fd01::1
++ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat fd01::1 fd11::2
++
++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_in_unsnat | \
++wc -l`])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_snat | wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ct_dnat | wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.dst=| wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [2
++])
++
++AT_CLEANUP
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 951a55226..89bd3f174 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -16610,3 +16610,314 @@ as hv4 ovs-appctl fdb/show br-phys
+ OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
+ 
+ AT_CLEANUP
++
++AT_SETUP([ovn -- Stateless Floating IP])
++ovn_start
++
++# In this test cases we create 3 switches, all connected to same
++# physical network (through br-phys on each HV). LS1 and LS2 have
++# 1 VIF each. Each HV has 1 VIF port. The first digit
++# of VIF port name indicates the hypervisor it is bound to, e.g.
++# lp23 means VIF 3 on hv2.
++#
++# All the switches are connected to a logical router "router".
++#
++# There is an external logical switch, ls-north.
++# This test validates the stateless floating ip implementation.
++#
++# Each switch's VLAN tag and their logical switch ports are:
++#   - ls1:
++#       - tagged with VLAN 101
++#       - ports: lp11
++#   - ls2:
++#       - tagged with VLAN 201
++#       - ports: lp22
++#   - ls-north:
++#       - tagged with VLAN 1000
++# Note: a localnet port is created for each switch to connect to
++# physical network.
++
++for i in 1 2; do
++    ls_name=ls$i
++    ovn-nbctl ls-add $ls_name
++    ln_port_name=ln$i
++    if test $i -eq 1; then
++        ovn-nbctl lsp-add $ls_name $ln_port_name "" 101
++    elif test $i -eq 2; then
++        ovn-nbctl lsp-add $ls_name $ln_port_name "" 201
++    fi
++    ovn-nbctl lsp-set-addresses $ln_port_name unknown
++    ovn-nbctl lsp-set-type $ln_port_name localnet
++    ovn-nbctl lsp-set-options $ln_port_name network_name=phys
++done
++
++# lsp_to_ls LSP
++#
++# Prints the name of the logical switch that contains LSP.
++lsp_to_ls () {
++    case $1 in dnl (
++        lp?[[11]]) echo ls1 ;; dnl (
++        lp?[[12]]) echo ls2 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_hv () {
++    case $1 in dnl (
++        vif[[1]]?) echo hv1 ;; dnl (
++        vif[[2]]?) echo hv2 ;; dnl (
++        vif?[[north]]?) echo hv4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++ip_to_hex() {
++       printf "%02x%02x%02x%02x" "$@"
++}
++
++net_add n1
++for i in 1 2; do
++    sim_add hv$i
++    as hv$i
++    ovs-vsctl add-br br-phys
++    ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++    ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:$i$i"
++    ovn_attach n1 br-phys 192.168.0.$i
++
++    ovs-vsctl add-port br-int vif$i$i -- \
++        set Interface vif$i$i external-ids:iface-id=lp$i$i \
++                              options:tx_pcap=hv$i/vif$i$i-tx.pcap \
++                              options:rxq_pcap=hv$i/vif$i$i-rx.pcap \
++                              ofport-request=$i$i
++
++    lsp_name=lp$i$i
++    ls_name=$(lsp_to_ls $lsp_name)
++
++    ovn-nbctl lsp-add $ls_name $lsp_name
++    ovn-nbctl lsp-set-addresses $lsp_name "f0:00:00:00:00:$i$i 192.168.$i.$i"
++    ovn-nbctl lsp-set-port-security $lsp_name f0:00:00:00:00:$i$i
++
++    OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lsp_name` = xup])
++
++done
++
++ovn-nbctl ls-add ls-underlay
++ovn-nbctl lsp-add ls-underlay ln3 "" 1000
++ovn-nbctl lsp-set-addresses ln3 unknown
++ovn-nbctl lsp-set-type ln3 localnet
++ovn-nbctl lsp-set-options ln3 network_name=phys
++
++ovn-nbctl ls-add ls-north
++ovn-nbctl lsp-add ls-north ln4 "" 1000
++ovn-nbctl lsp-set-addresses ln4 unknown
++ovn-nbctl lsp-set-type ln4 localnet
++ovn-nbctl lsp-set-options ln4 network_name=phys
++
++# Add a VM on ls-north
++ovn-nbctl lsp-add ls-north lp-north
++ovn-nbctl lsp-set-addresses lp-north "f0:f0:00:00:00:11 172.31.0.10"
++ovn-nbctl lsp-set-port-security lp-north f0:f0:00:00:00:11
++
++# Add 3rd hypervisor
++sim_add hv3
++as hv3 ovs-vsctl add-br br-phys
++as hv3 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++as hv3 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:33"
++as hv3 ovn_attach n1 br-phys 192.168.0.3
++
++# Add 4th hypervisor
++sim_add hv4
++as hv4 ovs-vsctl add-br br-phys
++as hv4 ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
++as hv4 ovs-vsctl set open . external-ids:ovn-chassis-mac-mappings="phys:aa:bb:cc:dd:ee:44"
++as hv4 ovn_attach n1 br-phys 192.168.0.4
++
++as hv4 ovs-vsctl add-port br-int vif-north -- \
++        set Interface vif-north external-ids:iface-id=lp-north \
++                              options:tx_pcap=hv4/vif-north-tx.pcap \
++                              options:rxq_pcap=hv4/vif-north-rx.pcap \
++                              ofport-request=44
++
++ovn-nbctl lr-add router
++ovn-nbctl lrp-add router router-to-ls1 00:00:01:01:02:03 192.168.1.3/24
++ovn-nbctl lrp-add router router-to-ls2 00:00:01:01:02:05 192.168.2.3/24
++ovn-nbctl lrp-add router router-to-underlay 00:00:01:01:02:07 172.31.0.1/24
++
++ovn-nbctl lsp-add ls1 ls1-to-router -- set Logical_Switch_Port ls1-to-router type=router \
++          options:router-port=router-to-ls1 -- lsp-set-addresses ls1-to-router router
++ovn-nbctl lsp-add ls2 ls2-to-router -- set Logical_Switch_Port ls2-to-router type=router \
++          options:router-port=router-to-ls2 -- lsp-set-addresses ls2-to-router router
++ovn-nbctl lsp-add ls-underlay underlay-to-router -- set Logical_Switch_Port \
++                              underlay-to-router type=router \
++                              options:router-port=router-to-underlay \
++                              -- lsp-set-addresses underlay-to-router router
++
++ovn-nbctl lrp-set-gateway-chassis router-to-underlay hv3
++ovn-nbctl --stateless lr-nat-add router dnat_and_snat 172.31.0.100 192.168.1.1
++ovn-nbctl lrp-set-redirect-type router-to-underlay bridged
++
++ovn-nbctl --wait=sb sync
++
++
++OVN_POPULATE_ARP
++
++# lsp_to_ls LSP
++#
++# Prints the name of the logical switch that contains LSP.
++lsp_to_ls () {
++    case $1 in dnl (
++        lp?[[11]]) echo ls1 ;; dnl (
++        lp?[[12]]) echo ls2 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_ls () {
++    case $1 in dnl (
++        vif?[[11]]) echo ls1 ;; dnl (
++        vif?[[12]]) echo ls2 ;; dnl (
++        vif-north) echo ls-north ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++hv_to_num () {
++    case $1 in dnl (
++        hv1) echo 1 ;; dnl (
++        hv2) echo 2 ;; dnl (
++        hv3) echo 3 ;; dnl (
++        hv4) echo 4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_num () {
++    case $1 in dnl (
++        vif22) echo 22 ;; dnl (
++        vif21) echo 21 ;; dnl (
++        vif11) echo 11 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_hv () {
++    case $1 in dnl (
++        vif[[1]]?) echo hv1 ;; dnl (
++        vif[[2]]?) echo hv2 ;; dnl (
++        vif-north) echo hv4 ;; dnl (
++        *) AT_FAIL_IF([:]) ;;
++    esac
++}
++
++vif_to_lrp () {
++    echo router-to-`vif_to_ls $1`
++}
++
++ip_to_hex() {
++       printf "%02x%02x%02x%02x" "$@"
++}
++
++
++test_ip() {
++        # This packet has bad checksums but logical L3 routing doesn't check.
++        local inport=$1 src_mac=$2 dst_mac=$3 src_ip=$4 dst_ip=$5 outport=$6
++        local packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
++        shift; shift; shift; shift; shift
++        hv=`vif_to_hv $inport`
++        as $hv ovs-appctl netdev-dummy/receive $inport $packet
++        in_ls=`vif_to_ls $inport`
++        for outport; do
++            out_ls=`vif_to_ls $outport`
++            if test $in_ls = $out_ls; then
++                # Ports on the same logical switch receive exactly the same packet.
++                echo $packet
++            else
++                # Routing decrements TTL and updates source and dest MAC
++                # (and checksum).
++                out_lrp=`vif_to_lrp $outport`
++                # For North-South, packet will come via gateway chassis, i.e hv3
++                if test $inport = vif-north; then
++                    echo f0000000001100000101020308004500001c000000003f110100${src_ip}${dst_ip}0035111100080000 >> $outport.expected
++                fi
++                if test $outport = vif-north; then
++                    echo f0f00000001100000101020708004500001c000000003e111726ac1f0064${dst_ip}0035111100080000 >> $outport.expected
++                fi
++            fi >> $outport.expected
++        done
++}
++
++# Dump a bunch of info helpful for debugging if there's a failure.
++
++echo "------ OVN dump ------"
++ovn-nbctl show
++ovn-nbctl lr-nat-list router
++ovn-sbctl show
++ovn-sbctl list port_binding
++ovn-sbctl list mac_binding
++ovn-sbctl dump-flows
++
++echo "------ hv1 dump ------"
++as hv1 ovs-vsctl show
++as hv1 ovs-vsctl list Open_Vswitch
++
++echo "------ hv2 dump ------"
++as hv2 ovs-vsctl show
++as hv2 ovs-vsctl list Open_Vswitch
++
++echo "------ hv3 dump ------"
++as hv3 ovs-vsctl show
++as hv3 ovs-vsctl list Open_Vswitch
++
++echo "------ hv4 dump ------"
++as hv4 ovs-vsctl show
++as hv4 ovs-vsctl list Open_Vswitch
++
++echo "Send traffic South to Nouth"
++sip=`ip_to_hex 192 168 1 1`
++dip=`ip_to_hex 172 31 0 10`
++test_ip vif11 f00000000011 000001010203 $sip $dip vif-north
++
++# Confirm that South to North traffic works fine.
++OVN_CHECK_PACKETS_REMOVE_BROADCAST([hv4/vif-north-tx.pcap], [vif-north.expected])
++
++# Confirm that NATing happened without connection tracker
++
++AT_CHECK([ovn-sbctl dump-flows router | grep ct_snat | wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows router | grep ct_dnat | wc -l], [0], [0
++])
++
++AT_CHECK([ovn-sbctl dump-flows router | grep ip4.dst=| wc -l], [0], [2
++])
++
++AT_CHECK([ovn-sbctl dump-flows router | grep ip4.src=| wc -l], [0], [2
++])
++
++echo "----------- Post Traffic hv1 dump -----------"
++as hv1 ovs-ofctl dump-flows br-int
++as hv1 ovs-ofctl show br-phys
++as hv1 ovs-appctl fdb/show br-phys
++as hv1 ovs-dpctl dump-flows
++
++echo "----------- Post Traffic hv2 dump -----------"
++as hv2 ovs-ofctl dump-flows br-int
++as hv2 ovs-ofctl show br-phys
++as hv2 ovs-appctl fdb/show br-phys
++
++echo "----------- Post Traffic hv3 dump -----------"
++as hv3 ovs-ofctl dump-flows br-int
++as hv3 ovs-ofctl show br-phys
++as hv3 ovs-appctl dpctl/dump-conntrack
++as hv3 ovs-appctl fdb/show br-phys
++as hv3 ovs-dpctl dump-flows
++as hv3 ovs-ofctl dump-flows br-int
++
++echo "----------- Post Traffic hv4 dump -----------"
++as hv4 ovs-ofctl dump-flows br-int
++as hv4 ovs-ofctl show br-phys
++as hv4 ovs-appctl fdb/show br-phys
++
++OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
++
++AT_CLEANUP
+-- 
+2.23.0
+
diff --git a/SOURCES/0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch b/SOURCES/0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch
new file mode 100644
index 0000000..c6cad27
--- /dev/null
+++ b/SOURCES/0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch
@@ -0,0 +1,45 @@
+From ac3a1c6b5e29af2de1ae0e87fd186430e8f27229 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <nusiddiq@redhat.com>
+Date: Sun, 8 Sep 2019 19:29:29 +0530
+Subject: [PATCH 08/12] Fix the segfault seen in ovn-controller when running
+ tests
+
+The test case - "116: ovn -- 2 HVs, 2 lports/HV, localnet ports, DVR N-S Ping"
+is failing with the segfault in ovn-controller occationally.
+
+This patch fixes it.
+
+backtrace
+------
+Program terminated with signal SIGSEGV, Segmentation fault.
+0x0000000000422414 in put_remote_port_redirect_bridged (...)
+    at /usr/include/bits/byteswap.h:52
+52	  return __builtin_bswap32 (__bsx);
+[Current thread is 1 (Thread 0x7f985fbe04c0 (LWP 18625))]
+------
+
+Fixes: 03493b33c073("OVN: Vlan backed DVR N-S, redirect packet via localnet port")
+CC: Ankur Sharma <ankur.sharma@nutanix.com>
+Acked-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
+Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
+---
+ ovn/controller/physical.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
+index 9d553da9a..8f8a5ba27 100644
+--- a/ovn/controller/physical.c
++++ b/ovn/controller/physical.c
+@@ -280,6 +280,9 @@ put_remote_port_redirect_bridged(const struct
+         const struct sbrec_port_binding *ls_localnet_port;
+ 
+         ls_localnet_port = get_localnet_port(local_datapaths, ls_dp_key);
++        if (!ls_localnet_port) {
++            return;
++        }
+ 
+         src_mac = ofpact_put_SET_ETH_SRC(ofpacts_p);
+         src_mac->mac = binding_mac;
+-- 
+2.23.0
+
diff --git a/SOURCES/0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch b/SOURCES/0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch
new file mode 100644
index 0000000..9f4a0b6
--- /dev/null
+++ b/SOURCES/0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch
@@ -0,0 +1,1258 @@
+From 8c91aefae19e159549f27029ccf799c209c5d7dc Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Tue, 21 May 2019 23:30:26 +0530
+Subject: [PATCH 09/12] ovn-northd: Add support for Load Balancer health check
+
+The present Load balancer feature in OVN provides load balancing
+functionality to the back end ips without checking if the chosen
+backend ip is reachable or not. In case a back end service is
+down and if that IP is chosen, then packet will be lost.
+
+This patch series adds the health check feature for these
+backend IPs and  only active backend IPs are considered for
+load balancing. CMS needs to enable this functionality.
+In the SB DB a new table Service_Monitor is added. For every
+backend IP in the load balancer for which health check is configured,
+a new row in the Service_Monitor table is created. In the upcoming
+patch in this series, ovn-controller will monitor the services set
+in this table by generating a health check packet.
+
+Health checks are supported only for IPv4 Load balancers in this patch.
+
+Existing load balancers will be unaffected after this patch.
+
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.8.xml |  75 +++++-
+ ovn/northd/ovn-northd.c     | 492 +++++++++++++++++++++++++++++++++---
+ ovn/ovn-nb.ovsschema        |  25 +-
+ ovn/ovn-nb.xml              |  68 +++++
+ ovn/ovn-sb.ovsschema        |  33 ++-
+ ovn/ovn-sb.xml              |  85 +++++++
+ tests/ovn-northd.at         | 215 ++++++++++++++++
+ 7 files changed, 947 insertions(+), 46 deletions(-)
+
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index d1af97d15..9515c7790 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -308,6 +308,16 @@
+       previously created, it will be associated to the empty_lb logical flow
+     </p>
+ 
++    <p>
++      This table also has a priority-110 flow with the match
++      <code>eth.src == <var>E</var></code> for all logical switch
++      datapaths to move traffic to the next table. Where <var>E</var>
++      is the service monitor mac defined in the
++      <ref column="options:svc_monitor_mac" table="NB_Global"
++      db="OVN_Northbound"/> colum of <ref table="NB_Global"
++      db="OVN_Northbound"/> table.
++    </p>
++
+     <h3>Ingress Table 5: Pre-stateful</h3>
+ 
+     <p>
+@@ -476,7 +486,10 @@
+         </code>, where <var>args</var> contains comma separated IP addresses
+         (and optional port numbers) to load balance to.  The address family of
+         the IP addresses of <var>args</var> is the same as the address family
+-        of <var>VIP</var>
++        of <var>VIP</var>. If health check is enabled, then <var>args</var>
++        will only contain those endpoints whose service monitor status entry
++        in <code>OVN_Southbound</code> db is either <code>online</code> or
++        empty.
+       </li>
+       <li>
+         For all the configured load balancing rules for a switch in
+@@ -698,6 +711,51 @@ nd_na_router {
+         </p>
+       </li>
+ 
++      <li>
++        <p>
++          For each <var>SVC_MON_SRC_IP</var> defined in the value of
++          the <ref column="ip_port_mappings:ENDPOINT_IP"
++          table="Load_Balancer" db="OVN_Northbound"/> column of
++          <ref table="Load_Balancer" db="OVN_Northbound"/> table, priority-110
++          logical flow is added with the match
++          <code>arp.tpa == <var>SVC_MON_SRC_IP</var>
++          &amp;&amp; &amp;&amp; arp.op == 1</code> and applies the action
++        </p>
++
++        <pre>
++eth.dst = eth.src;
++eth.src = <var>E</var>;
++arp.op = 2; /* ARP reply. */
++arp.tha = arp.sha;
++arp.sha = <var>E</var>;
++arp.tpa = arp.spa;
++arp.spa = <var>A</var>;
++outport = inport;
++flags.loopback = 1;
++output;
++        </pre>
++
++        <p>
++          where <var>E</var> is the service monitor source mac defined in
++          the <ref column="options:svc_monitor_mac" table="NB_Global"
++          db="OVN_Northbound"/> column in the <ref table="NB_Global"
++          db="OVN_Northbound"/> table. This mac is used as the source mac
++          in the service monitor packets for the load balancer endpoint IP
++          health checks.
++        </p>
++
++        <p>
++          <var>SVC_MON_SRC_IP</var> is used as the source ip in the
++          service monitor IPv4 packets for the load balancer endpoint IP
++          health checks.
++        </p>
++
++        <p>
++          These flows are required if an ARP request is sent for the IP
++          <var>SVC_MON_SRC_IP</var>.
++        </p>
++      </li>
++
+       <li>
+         One priority-0 fallback flow that matches all packets and advances to
+         the next table.
+@@ -1100,6 +1158,16 @@ output;
+       tracker for packet de-fragmentation.
+     </p>
+ 
++    <p>
++      This table also has a priority-110 flow with the match
++      <code>eth.src == <var>E</var></code> for all logical switch
++      datapaths to move traffic to the next table. Where <var>E</var>
++      is the service monitor mac defined in the
++      <ref column="options:svc_monitor_mac" table="NB_Global"
++      db="OVN_Northbound"/> colum of <ref table="NB_Global"
++      db="OVN_Northbound"/> table.
++    </p>
++
+     <h3>Egress Table 1: <code>to-lport</code> Pre-ACLs</h3>
+ 
+     <p>
+@@ -1940,7 +2008,10 @@ icmp6 {
+         (and optional port numbers) to load balance to.  If the router is
+         configured to force SNAT any load-balanced packets, the above action
+         will be replaced by <code>flags.force_snat_for_lb = 1;
+-        ct_lb(<var>args</var>);</code>.
++        ct_lb(<var>args</var>);</code>. If health check is enabled, then
++        <var>args</var> will only contain those endpoints whose service
++        monitor status entry in <code>OVN_Southbound</code> db is
++        either <code>online</code> or empty.
+       </li>
+ 
+       <li>
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 8938e458f..1ded5dd9b 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -88,6 +88,13 @@ static struct eth_addr mac_prefix;
+ 
+ static bool controller_event_en;
+ 
++/* MAC allocated for service monitor usage. Just one mac is allocated
++ * for this purpose and ovn-controller's on each chassis will make use
++ * of this mac when sending out the packets to monitor the services
++ * defined in Service_Monitor Southbound table. Since these packets
++ * all locally handled, having just one mac is good enough. */
++static char svc_monitor_mac[ETH_ADDR_STRLEN + 1];
++
+ #define MAX_OVN_TAGS 4096
+ 
+ /* Pipeline stages. */
+@@ -2978,6 +2985,291 @@ cleanup_sb_ha_chassis_groups(struct northd_context *ctx,
+     }
+ }
+ 
++struct ovn_lb {
++    struct hmap_node hmap_node;
++
++    const struct nbrec_load_balancer *nlb; /* May be NULL. */
++
++    struct lb_vip *vips;
++    size_t n_vips;
++};
++
++struct lb_vip {
++    char *vip;
++    uint16_t vip_port;
++    int addr_family;
++    char *backend_ips;
++
++    bool health_check;
++    struct lb_vip_backend *backends;
++    size_t n_backends;
++};
++
++struct lb_vip_backend {
++    char *ip;
++    uint16_t port;
++
++    struct ovn_port *op; /* Logical port to which the ip belong to. */
++    bool health_check;
++    char *svc_mon_src_ip; /* Source IP to use for monitoring. */
++    const struct sbrec_service_monitor *sbrec_monitor;
++};
++
++
++static inline struct ovn_lb *
++ovn_lb_find(struct hmap *lbs, struct uuid *uuid)
++{
++    struct ovn_lb *lb;
++    size_t hash = uuid_hash(uuid);
++    HMAP_FOR_EACH_WITH_HASH (lb, hmap_node, hash, lbs) {
++        if (uuid_equals(&lb->nlb->header_.uuid, uuid)) {
++            return lb;
++        }
++    }
++
++    return NULL;
++}
++
++
++struct service_monitor_info {
++    struct hmap_node hmap_node;
++    const struct sbrec_service_monitor *sbrec_mon;
++    bool required;
++};
++
++
++static struct service_monitor_info *
++create_or_get_service_mon(struct northd_context *ctx,
++                          struct hmap *monitor_map,
++                          const char *ip, const char *logical_port,
++                          uint16_t service_port, const char *protocol)
++{
++    uint32_t hash = service_port;
++    hash = hash_string(ip, hash);
++    hash = hash_string(logical_port, hash);
++    struct service_monitor_info *mon_info;
++
++    HMAP_FOR_EACH_WITH_HASH (mon_info, hmap_node, hash, monitor_map) {
++        if (mon_info->sbrec_mon->port == service_port &&
++            !strcmp(mon_info->sbrec_mon->ip, ip) &&
++            !strcmp(mon_info->sbrec_mon->protocol, protocol) &&
++            !strcmp(mon_info->sbrec_mon->logical_port, logical_port)) {
++            return mon_info;
++        }
++    }
++
++    struct sbrec_service_monitor *sbrec_mon =
++        sbrec_service_monitor_insert(ctx->ovnsb_txn);
++    sbrec_service_monitor_set_ip(sbrec_mon, ip);
++    sbrec_service_monitor_set_port(sbrec_mon, service_port);
++    sbrec_service_monitor_set_logical_port(sbrec_mon, logical_port);
++    sbrec_service_monitor_set_protocol(sbrec_mon, protocol);
++    mon_info = xzalloc(sizeof *mon_info);
++    mon_info->sbrec_mon = sbrec_mon;
++    hmap_insert(monitor_map, &mon_info->hmap_node, hash);
++    return mon_info;
++}
++
++static struct ovn_lb *
++ovn_lb_create(struct northd_context *ctx, struct hmap *lbs,
++              const struct nbrec_load_balancer *nbrec_lb,
++              struct hmap *ports, struct hmap *monitor_map)
++{
++    struct ovn_lb *lb = xzalloc(sizeof *lb);
++
++    size_t hash = uuid_hash(&nbrec_lb->header_.uuid);
++    lb->nlb = nbrec_lb;
++    hmap_insert(lbs, &lb->hmap_node, hash);
++
++    lb->n_vips = smap_count(&nbrec_lb->vips);
++    lb->vips = xcalloc(lb->n_vips, sizeof (struct lb_vip));
++    struct smap_node *node;
++    size_t n_vips = 0;
++
++    SMAP_FOR_EACH (node, &nbrec_lb->vips) {
++        char *vip = NULL;
++        uint16_t port;
++        int addr_family;
++
++        ip_address_and_port_from_lb_key(node->key, &vip, &port,
++                                        &addr_family);
++        if (!vip) {
++            continue;
++        }
++
++        lb->vips[n_vips].vip = vip;
++        lb->vips[n_vips].vip_port = port;
++        lb->vips[n_vips].addr_family = addr_family;
++        lb->vips[n_vips].backend_ips = xstrdup(node->value);
++
++        struct nbrec_load_balancer_health_check *lb_health_check = NULL;
++        for (size_t i = 0; i < nbrec_lb->n_health_check; i++) {
++            if (!strcmp(nbrec_lb->health_check[i]->vip, node->key)) {
++                lb_health_check = nbrec_lb->health_check[i];
++                break;
++            }
++        }
++
++        char *tokstr = xstrdup(node->value);
++        char *save_ptr = NULL;
++        char *token;
++        size_t n_backends = 0;
++        /* Format for a backend ips : IP1:port1,IP2:port2,...". */
++        for (token = strtok_r(tokstr, ",", &save_ptr);
++            token != NULL;
++            token = strtok_r(NULL, ",", &save_ptr)) {
++            n_backends++;
++        }
++
++        free(tokstr);
++        tokstr = xstrdup(node->value);
++        save_ptr = NULL;
++
++        lb->vips[n_vips].n_backends = n_backends;
++        lb->vips[n_vips].backends = xcalloc(n_backends,
++                                            sizeof (struct lb_vip_backend));
++        lb->vips[n_vips].health_check = lb_health_check ? true: false;
++
++        size_t i = 0;
++        for (token = strtok_r(tokstr, ",", &save_ptr);
++            token != NULL;
++            token = strtok_r(NULL, ",", &save_ptr)) {
++            char *backend_ip;
++            uint16_t backend_port;
++
++            ip_address_and_port_from_lb_key(token, &backend_ip, &backend_port,
++                                            &addr_family);
++
++            if (!backend_ip) {
++                continue;
++            }
++
++            /* Get the logical port to which this ip belongs to. */
++            struct ovn_port *op = NULL;
++            char *svc_mon_src_ip = NULL;
++            const char *s = smap_get(&nbrec_lb->ip_port_mappings,
++                                     backend_ip);
++            if (s) {
++                char *port_name = xstrdup(s);
++                char *p = strstr(port_name, ":");
++                if (p) {
++                    *p = 0;
++                    p++;
++                    op = ovn_port_find(ports, port_name);
++                    svc_mon_src_ip = xstrdup(p);
++                }
++                free(port_name);
++            }
++
++            lb->vips[n_vips].backends[i].ip = backend_ip;
++            lb->vips[n_vips].backends[i].port = backend_port;
++            lb->vips[n_vips].backends[i].op = op;
++            lb->vips[n_vips].backends[i].svc_mon_src_ip = svc_mon_src_ip;
++
++            if (lb_health_check && op && svc_mon_src_ip) {
++                const char *protocol = nbrec_lb->protocol;
++                if (!protocol || !protocol[0]) {
++                    protocol = "tcp";
++                }
++                lb->vips[n_vips].backends[i].health_check = true;
++                struct service_monitor_info *mon_info =
++                    create_or_get_service_mon(ctx, monitor_map, backend_ip,
++                                              op->nbsp->name, backend_port,
++                                              protocol);
++
++                ovs_assert(mon_info);
++                sbrec_service_monitor_set_options(
++                    mon_info->sbrec_mon, &lb_health_check->options);
++                if (!mon_info->sbrec_mon->src_mac ||
++                    strcmp(mon_info->sbrec_mon->src_mac, svc_monitor_mac)) {
++                    sbrec_service_monitor_set_src_mac(mon_info->sbrec_mon,
++                                                      svc_monitor_mac);
++                }
++
++                if (!mon_info->sbrec_mon->src_ip ||
++                    strcmp(mon_info->sbrec_mon->src_ip, svc_mon_src_ip)) {
++                    sbrec_service_monitor_set_src_ip(mon_info->sbrec_mon,
++                                                     svc_mon_src_ip);
++                }
++
++                lb->vips[n_vips].backends[i].sbrec_monitor =
++                    mon_info->sbrec_mon;
++                mon_info->required = true;
++            } else {
++                lb->vips[n_vips].backends[i].health_check = false;
++            }
++
++            i++;
++        }
++
++        free(tokstr);
++        n_vips++;
++    }
++
++    return lb;
++}
++
++static void
++ovn_lb_destroy(struct ovn_lb *lb)
++{
++    for (size_t i = 0; i < lb->n_vips; i++) {
++        free(lb->vips[i].vip);
++        free(lb->vips[i].backend_ips);
++
++        for (size_t j = 0; j < lb->vips[i].n_backends; j++) {
++            free(lb->vips[i].backends[j].ip);
++            free(lb->vips[i].backends[j].svc_mon_src_ip);
++        }
++
++        free(lb->vips[i].backends);
++    }
++    free(lb->vips);
++}
++
++static void
++build_ovn_lbs(struct northd_context *ctx, struct hmap *ports,
++              struct hmap *lbs)
++{
++    hmap_init(lbs);
++    struct hmap monitor_map = HMAP_INITIALIZER(&monitor_map);
++
++    const struct sbrec_service_monitor *sbrec_mon;
++    SBREC_SERVICE_MONITOR_FOR_EACH (sbrec_mon, ctx->ovnsb_idl) {
++        uint32_t hash = sbrec_mon->port;
++        hash = hash_string(sbrec_mon->ip, hash);
++        hash = hash_string(sbrec_mon->logical_port, hash);
++        struct service_monitor_info *mon_info = xzalloc(sizeof *mon_info);
++        mon_info->sbrec_mon = sbrec_mon;
++        mon_info->required = false;
++        hmap_insert(&monitor_map, &mon_info->hmap_node, hash);
++    }
++
++    const struct nbrec_load_balancer *nbrec_lb;
++    NBREC_LOAD_BALANCER_FOR_EACH (nbrec_lb, ctx->ovnnb_idl) {
++        ovn_lb_create(ctx, lbs, nbrec_lb, ports, &monitor_map);
++    }
++
++    struct service_monitor_info *mon_info;
++    HMAP_FOR_EACH_POP (mon_info, hmap_node, &monitor_map) {
++        if (!mon_info->required) {
++            sbrec_service_monitor_delete(mon_info->sbrec_mon);
++        }
++
++        free(mon_info);
++    }
++    hmap_destroy(&monitor_map);
++}
++
++static void
++destroy_ovn_lbs(struct hmap *lbs)
++{
++    struct ovn_lb *lb;
++    HMAP_FOR_EACH_POP (lb, hmap_node, lbs) {
++        ovn_lb_destroy(lb);
++        free(lb);
++    }
++}
++
+ /* Updates the southbound Port_Binding table so that it contains the logical
+  * switch ports specified by the northbound database.
+  *
+@@ -4335,6 +4627,14 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows,
+     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
+                   "nd || nd_rs || nd_ra", "next;");
+ 
++    /* Do not send service monitor packets to conntrack. */
++    char *svc_check_match = xasprintf("eth.src == %s", svc_monitor_mac);
++    ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 110,
++                  svc_check_match, "next;");
++    ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 110,
++                  svc_check_match, "next;");
++    free(svc_check_match);
++
+     /* Allow all packets to go to next tables by default. */
+     ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 0, "1", "next;");
+     ovn_lflow_add(lflows, od, S_SWITCH_OUT_PRE_LB, 0, "1", "next;");
+@@ -5002,7 +5302,7 @@ build_lb(struct ovn_datapath *od, struct hmap *lflows)
+ }
+ 
+ static void
+-build_stateful(struct ovn_datapath *od, struct hmap *lflows)
++build_stateful(struct ovn_datapath *od, struct hmap *lflows, struct hmap *lbs)
+ {
+     /* Ingress and Egress stateful Table (Priority 0): Packets are
+      * allowed by default. */
+@@ -5036,47 +5336,69 @@ build_stateful(struct ovn_datapath *od, struct hmap *lflows)
+      * connection, so it is okay if we do not hit the above match on
+      * REGBIT_CONNTRACK_COMMIT. */
+     for (int i = 0; i < od->nbs->n_load_balancer; i++) {
+-        struct nbrec_load_balancer *lb = od->nbs->load_balancer[i];
+-        struct smap *vips = &lb->vips;
+-        struct smap_node *node;
++        struct ovn_lb *lb =
++            ovn_lb_find(lbs, &od->nbs->load_balancer[i]->header_.uuid);
++        ovs_assert(lb);
+ 
+-        SMAP_FOR_EACH (node, vips) {
+-            uint16_t port = 0;
+-            int addr_family;
++        for (size_t j = 0; j < lb->n_vips; j++) {
++            struct lb_vip *lb_vip = &lb->vips[j];
++            /* New connections in Ingress table. */
++            struct ds action = DS_EMPTY_INITIALIZER;
++            if (lb_vip->health_check) {
++                ds_put_cstr(&action, "ct_lb(");
++
++                size_t n_active_backends = 0;
++                for (size_t k = 0; k < lb_vip->n_backends; k++) {
++                    struct lb_vip_backend *backend = &lb_vip->backends[k];
++                    bool is_up = true;
++                    if (backend->health_check && backend->sbrec_monitor &&
++                        backend->sbrec_monitor->status &&
++                        strcmp(backend->sbrec_monitor->status, "online")) {
++                        is_up = false;
++                    }
+ 
+-            /* node->key contains IP:port or just IP. */
+-            char *ip_address = NULL;
+-            ip_address_and_port_from_lb_key(node->key, &ip_address, &port,
+-                                            &addr_family);
+-            if (!ip_address) {
+-                continue;
++                    if (is_up) {
++                        n_active_backends++;
++                        ds_put_format(&action, "%s:%"PRIu16",",
++                        backend->ip, backend->port);
++                    }
++                }
++
++                if (!n_active_backends) {
++                    ds_clear(&action);
++                    ds_put_cstr(&action, "drop;");
++                } else {
++                    ds_chomp(&action, ',');
++                    ds_put_cstr(&action, ");");
++                }
++            } else {
++                ds_put_format(&action, "ct_lb(%s);", lb_vip->backend_ips);
+             }
+ 
+-            /* New connections in Ingress table. */
+-            char *action = xasprintf("ct_lb(%s);", node->value);
+             struct ds match = DS_EMPTY_INITIALIZER;
+-            if (addr_family == AF_INET) {
+-                ds_put_format(&match, "ct.new && ip4.dst == %s", ip_address);
++            if (lb_vip->addr_family == AF_INET) {
++                ds_put_format(&match, "ct.new && ip4.dst == %s", lb_vip->vip);
+             } else {
+-                ds_put_format(&match, "ct.new && ip6.dst == %s", ip_address);
++                ds_put_format(&match, "ct.new && ip6.dst == %s", lb_vip->vip);
+             }
+-            if (port) {
+-                if (lb->protocol && !strcmp(lb->protocol, "udp")) {
+-                    ds_put_format(&match, " && udp.dst == %d", port);
++            if (lb_vip->vip_port) {
++                if (lb->nlb->protocol && !strcmp(lb->nlb->protocol, "udp")) {
++                    ds_put_format(&match, " && udp.dst == %d",
++                                  lb_vip->vip_port);
+                 } else {
+-                    ds_put_format(&match, " && tcp.dst == %d", port);
++                    ds_put_format(&match, " && tcp.dst == %d",
++                                  lb_vip->vip_port);
+                 }
+                 ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
+-                              120, ds_cstr(&match), action);
++                              120, ds_cstr(&match), ds_cstr(&action));
+             } else {
+                 ovn_lflow_add(lflows, od, S_SWITCH_IN_STATEFUL,
+-                              110, ds_cstr(&match), action);
++                              110, ds_cstr(&match), ds_cstr(&action));
+             }
+ 
+-            free(ip_address);
+             ds_destroy(&match);
+-            free(action);
+-       }
++            ds_destroy(&action);
++        }
+     }
+ }
+ 
+@@ -5354,7 +5676,8 @@ static void
+ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+                     struct hmap *port_groups, struct hmap *lflows,
+                     struct hmap *mcgroups, struct hmap *igmp_groups,
+-                    struct shash *meter_groups)
++                    struct shash *meter_groups,
++                    struct hmap *lbs)
+ {
+     /* This flow table structure is documented in ovn-northd(8), so please
+      * update ovn-northd.8.xml if you change anything. */
+@@ -5376,7 +5699,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         build_acls(od, lflows, port_groups);
+         build_qos(od, lflows);
+         build_lb(od, lflows);
+-        build_stateful(od, lflows);
++        build_stateful(od, lflows, lbs);
+     }
+ 
+     /* Logical switch ingress table 0: Admission control framework (priority
+@@ -5578,6 +5901,46 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         ovn_lflow_add(lflows, od, S_SWITCH_IN_ARP_ND_RSP, 0, "1", "next;");
+     }
+ 
++    /* Ingress table 11: ARP/ND responder for service monitor source ip.
++     * (priority 110)*/
++    struct ovn_lb *lb;
++    HMAP_FOR_EACH (lb, hmap_node, lbs) {
++        for (size_t i = 0; i < lb->n_vips; i++) {
++            if (!lb->vips[i].health_check) {
++                continue;
++            }
++
++            for (size_t j = 0; j < lb->vips[i].n_backends; j++) {
++                if (!lb->vips[i].backends[j].op ||
++                    !lb->vips[i].backends[j].svc_mon_src_ip) {
++                    continue;
++                }
++
++                ds_clear(&match);
++                ds_put_format(&match, "arp.tpa == %s && arp.op == 1",
++                              lb->vips[i].backends[j].svc_mon_src_ip);
++                ds_clear(&actions);
++                ds_put_format(&actions,
++                    "eth.dst = eth.src; "
++                    "eth.src = %s; "
++                    "arp.op = 2; /* ARP reply */ "
++                    "arp.tha = arp.sha; "
++                    "arp.sha = %s; "
++                    "arp.tpa = arp.spa; "
++                    "arp.spa = %s; "
++                    "outport = inport; "
++                    "flags.loopback = 1; "
++                    "output;",
++                    svc_monitor_mac, svc_monitor_mac,
++                    lb->vips[i].backends[j].svc_mon_src_ip);
++                ovn_lflow_add(lflows, lb->vips[i].backends[j].op->od,
++                              S_SWITCH_IN_ARP_ND_RSP, 110,
++                              ds_cstr(&match), ds_cstr(&actions));
++            }
++        }
++    }
++
++
+     /* Logical switch ingress table 12 and 13: DHCP options and response
+          * priority 100 flows. */
+     HMAP_FOR_EACH (op, key_node, ports) {
+@@ -9043,12 +9406,13 @@ static void
+ build_lflows(struct northd_context *ctx, struct hmap *datapaths,
+              struct hmap *ports, struct hmap *port_groups,
+              struct hmap *mcgroups, struct hmap *igmp_groups,
+-             struct shash *meter_groups)
++             struct shash *meter_groups,
++             struct hmap *lbs)
+ {
+     struct hmap lflows = HMAP_INITIALIZER(&lflows);
+ 
+     build_lswitch_flows(datapaths, ports, port_groups, &lflows, mcgroups,
+-                        igmp_groups, meter_groups);
++                        igmp_groups, meter_groups, lbs);
+     build_lrouter_flows(datapaths, ports, &lflows, meter_groups);
+ 
+     /* Push changes to the Logical_Flow table to database. */
+@@ -9765,9 +10129,11 @@ ovnnb_db_run(struct northd_context *ctx,
+     struct hmap mcast_groups;
+     struct hmap igmp_groups;
+     struct shash meter_groups = SHASH_INITIALIZER(&meter_groups);
++    struct hmap lbs;
+ 
+     build_datapaths(ctx, datapaths, lr_list);
+     build_ports(ctx, sbrec_chassis_by_name, datapaths, ports);
++    build_ovn_lbs(ctx, ports, &lbs);
+     build_ipam(datapaths, ports);
+     build_port_group_lswitches(ctx, &port_groups, ports);
+     build_lrouter_groups(ports, lr_list);
+@@ -9775,12 +10141,14 @@ ovnnb_db_run(struct northd_context *ctx,
+     build_mcast_groups(ctx, datapaths, ports, &mcast_groups, &igmp_groups);
+     build_meter_groups(ctx, &meter_groups);
+     build_lflows(ctx, datapaths, ports, &port_groups, &mcast_groups,
+-                 &igmp_groups, &meter_groups);
++                 &igmp_groups, &meter_groups, &lbs);
+ 
+     sync_address_sets(ctx);
+     sync_port_groups(ctx);
+     sync_meters(ctx);
+     sync_dns_entries(ctx, datapaths);
++    destroy_ovn_lbs(&lbs);
++    hmap_destroy(&lbs);
+ 
+     struct ovn_igmp_group *igmp_group, *next_igmp_group;
+ 
+@@ -9829,16 +10197,43 @@ ovnnb_db_run(struct northd_context *ctx,
+                      &addr.ea[0], &addr.ea[1], &addr.ea[2])) {
+             mac_prefix = addr;
+         }
+-    } else {
+-        struct smap options;
++    }
+ 
++    const char *monitor_mac = smap_get(&nb->options, "svc_monitor_mac");
++    if (monitor_mac) {
++        struct eth_addr addr;
++
++        memset(&addr, 0, sizeof addr);
++        if (eth_addr_from_string(monitor_mac, &addr)) {
++            snprintf(svc_monitor_mac, sizeof svc_monitor_mac,
++                     ETH_ADDR_FMT, ETH_ADDR_ARGS(addr));
++        } else {
++            monitor_mac = NULL;
++        }
++    }
++
++    if (!mac_addr_prefix || !monitor_mac) {
++        struct smap options;
+         smap_clone(&options, &nb->options);
+-        eth_addr_random(&mac_prefix);
+-        memset(&mac_prefix.ea[3], 0, 3);
+ 
+-        smap_add_format(&options, "mac_prefix",
+-                        "%02"PRIx8":%02"PRIx8":%02"PRIx8,
+-                        mac_prefix.ea[0], mac_prefix.ea[1], mac_prefix.ea[2]);
++        if (!mac_addr_prefix) {
++            eth_addr_random(&mac_prefix);
++            memset(&mac_prefix.ea[3], 0, 3);
++
++            smap_add_format(&options, "mac_prefix",
++                            "%02"PRIx8":%02"PRIx8":%02"PRIx8,
++                            mac_prefix.ea[0], mac_prefix.ea[1],
++                            mac_prefix.ea[2]);
++        }
++
++        if (!monitor_mac) {
++            struct eth_addr addr;
++            eth_addr_random(&addr);
++            snprintf(svc_monitor_mac, sizeof svc_monitor_mac,
++                     ETH_ADDR_FMT, ETH_ADDR_ARGS(addr));
++            smap_replace(&options, "svc_monitor_mac", svc_monitor_mac);
++        }
++
+         nbrec_nb_global_verify_options(nb);
+         nbrec_nb_global_set_options(nb, &options);
+ 
+@@ -10631,6 +11026,25 @@ main(int argc, char *argv[])
+                        &sbrec_ip_multicast_col_query_interval);
+     add_column_noalert(ovnsb_idl_loop.idl,
+                        &sbrec_ip_multicast_col_query_max_resp);
++    ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_service_monitor);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_ip);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_logical_port);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_port);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_options);
++    ovsdb_idl_add_column(ovnsb_idl_loop.idl,
++                         &sbrec_service_monitor_col_status);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_protocol);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_src_mac);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_src_ip);
++    add_column_noalert(ovnsb_idl_loop.idl,
++                       &sbrec_service_monitor_col_external_ids);
+ 
+     struct ovsdb_idl_index *sbrec_chassis_by_name
+         = chassis_index_create(ovnsb_idl_loop.idl);
+diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
+index 084305b24..12999a466 100644
+--- a/ovn/ovn-nb.ovsschema
++++ b/ovn/ovn-nb.ovsschema
+@@ -1,7 +1,7 @@
+ {
+     "name": "OVN_Northbound",
+-    "version": "5.17.0",
+-    "cksum": "1128988054 23237",
++    "version": "5.18.0",
++    "cksum": "2806349485 24196",
+     "tables": {
+         "NB_Global": {
+             "columns": {
+@@ -152,10 +152,31 @@
+                     "type": {"key": {"type": "string",
+                              "enum": ["set", ["tcp", "udp"]]},
+                              "min": 0, "max": 1}},
++                "health_check": {"type": {
++                    "key": {"type": "uuid",
++                            "refTable": "Load_Balancer_Health_Check",
++                            "refType": "strong"},
++                    "min": 0,
++                    "max": "unlimited"}},
++                "ip_port_mappings": {
++                    "type": {"key": "string", "value": "string",
++                             "min": 0, "max": "unlimited"}},
+                 "external_ids": {
+                     "type": {"key": "string", "value": "string",
+                              "min": 0, "max": "unlimited"}}},
+             "isRoot": true},
++        "Load_Balancer_Health_Check": {
++            "columns": {
++                "vip": {"type": "string"},
++                "options": {
++                     "type": {"key": "string",
++                              "value": "string",
++                              "min": 0,
++                              "max": "unlimited"}},
++                "external_ids": {
++                    "type": {"key": "string", "value": "string",
++                             "min": 0, "max": "unlimited"}}},
++            "isRoot": false},
+         "ACL": {
+             "columns": {
+                 "name": {"type": {"key": {"type": "string",
+diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
+index cf2c5136a..0d22e3280 100644
+--- a/ovn/ovn-nb.xml
++++ b/ovn/ovn-nb.xml
+@@ -1297,6 +1297,74 @@
+       </p>
+     </column>
+ 
++    <column name="health_check">
++      Load balancer health checks associated with this load balancer.
++      If health check is desired for a vip's endpoints defined in
++      the <ref column="vips" table="Load_Balancer" db="OVN_Northbound"/>
++      column, then a row in the table
++      <ref table="Load_Balancer_Health_Check" db="OVN_Northbound"/> should
++      be created and referenced here and L4 port should be defined
++      for the vip and it's endpoints. Health checks are supported only
++      for IPv4 load balancers.
++    </column>
++
++    <column name="ip_port_mappings">
++      <p>
++        This column is used if load balancer health checks are enabled.
++        This keeps a mapping of endpoint IP to the logical port name.
++        The source ip to be used for health checks is also expected to be
++        defined. The key of the mapping is the endpoint IP and the value
++        is in the format : <code>port_name:SRC_IP</code>
++      </p>
++
++      <p>
++        Eg. If there is a VIP entry:
++        <code>"10.0.0.10:80=10.0.0.4:8080,20.0.0.4:8080"</code>,
++        then the IP to port mappings should be defined as:
++        <code>"10.0.0.4"="sw0-p1:10.0.0.2"</code> and
++        <code>"20.0.0.4"="sw1-p1:20.0.0.2"</code>. <code>10.0.0.2</code>
++        and <code>20.0.0.2</code> will be used by <code>ovn-controller</code>
++        as source ip when it sends out health check packets.
++      </p>
++    </column>
++
++    <group title="Common Columns">
++      <column name="external_ids">
++        See <em>External IDs</em> at the beginning of this document.
++      </column>
++    </group>
++  </table>
++
++  <table name="Load_Balancer_Health_Check" title="load balancer">
++    <p>
++      Each row represents one load balancer health check. Health checks
++      are supported for IPv4 load balancers only.
++    </p>
++
++    <column name="vip">
++      <code>vip</code> whose endpoints should be monitored for health check.
++    </column>
++
++    <group title="Health check options">
++      <column name="options" key="interval" type='{"type": "integer"}'>
++        The interval, in seconds, between health checks.
++      </column>
++
++      <column name="options" key="timeout" type='{"type": "integer"}'>
++        The time, in seconds, after which a health check times out.
++      </column>
++
++      <column name="options" key="success_count" type='{"type": "integer"}'>
++        The number of successful checks after which the endpoint is
++        considered online.
++      </column>
++
++      <column name="options" key="failure_count" type='{"type": "integer"}'>
++        The number of failure checks after which the endpoint is considered
++        offline.
++      </column>
++    </group>
++
+     <group title="Common Columns">
+       <column name="external_ids">
+         See <em>External IDs</em> at the beginning of this document.
+diff --git a/ovn/ovn-sb.ovsschema b/ovn/ovn-sb.ovsschema
+index 5c013b17e..56af0ed3e 100644
+--- a/ovn/ovn-sb.ovsschema
++++ b/ovn/ovn-sb.ovsschema
+@@ -1,7 +1,7 @@
+ {
+     "name": "OVN_Southbound",
+-    "version": "2.5.0",
+-    "cksum": "1257419092 20387",
++    "version": "2.6.0",
++    "cksum": "4271405686 21646",
+     "tables": {
+         "SB_Global": {
+             "columns": {
+@@ -403,4 +403,31 @@
+                                            "refType": "weak"},
+                                    "min": 0, "max": "unlimited"}}},
+             "indexes": [["address", "datapath", "chassis"]],
+-            "isRoot": true}}}
++            "isRoot": true},
++        "Service_Monitor": {
++            "columns": {
++                "ip": {"type": "string"},
++                "protocol": {
++                    "type": {"key": {"type": "string",
++                             "enum": ["set", ["tcp", "udp"]]},
++                             "min": 0, "max": 1}},
++                "port": {"type": {"key": {"type": "integer",
++                                          "minInteger": 0,
++                                          "maxInteger": 32767}}},
++                "logical_port": {"type": "string"},
++                "src_mac": {"type": "string"},
++                "src_ip": {"type": "string"},
++                "status": {
++                    "type": {"key": {"type": "string",
++                             "enum": ["set", ["online", "offline", "error"]]},
++                             "min": 0, "max": 1}},
++                "options": {
++                    "type": {"key": "string", "value": "string",
++                             "min": 0, "max": "unlimited"}},
++                "external_ids": {
++                    "type": {"key": "string", "value": "string",
++                             "min": 0, "max": "unlimited"}}},
++            "indexes": [["logical_port", "ip", "port", "protocol"]],
++            "isRoot": true}
++    }
++}
+diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
+index e5fb51a9d..335f9031b 100644
+--- a/ovn/ovn-sb.xml
++++ b/ovn/ovn-sb.xml
+@@ -3743,4 +3743,89 @@ tcp.flags = RST;
+       The destination port bindings for this IGMP group.
+     </column>
+   </table>
++
++  <table name="Service_Monitor">
++    <p>
++      This table montiors a service for its liveliness. The service
++      can be an IPv4 tcp or a udp service. <code>ovn-controller</code>
++      periodically sends out service monitor packets and updates the
++      status of the service. Service monitoring for IPv6 services is
++      not supported.
++    </p>
++
++    <column name="ip">
++      IP of the service to be monitored. Only IPv4 is supported.
++    </column>
++
++    <column name="protocol">
++      The protocol of the service. It can be either <code>tcp</code> or
++      <code>udp</code>.
++    </column>
++
++    <column name="port">
++     The tcp or udp port of the service.
++    </column>
++
++    <column name="logical_port">
++      The VIF of logical port on which the service is running. The
++      <code>ovn-controller</code> which binds this <code>logical_port</code>
++      monitors the service by sending periodic monitor packets.
++    </column>
++
++    <column name="status">
++      <p>
++        The <code>ovn-controller</code> which binds the
++        <code>logical_port</code> updates the status to <code>online</code>
++        <code>offline</code> or <code>error</code>.
++      </p>
++
++      <p>
++        For tcp service, <code>ovn-controller</code> sends a
++        <code>TCP SYN</code> packet to the service and expects a
++        <code>TCP ACK</code> response to consider the service to be
++        <code>online</code>.
++      </p>
++
++      <p>
++        For udp service, <code>ovn-controller</code> sends a <code>udp</code>
++        packet to the service and doesn't expect any reply. If it receives
++        ICMP reply, then it considers the service to be <code>offline</code>.
++      </p>
++    </column>
++
++    <column name="src_mac">
++      Source Ethernet address to use in the service monitor packet.
++    </column>
++
++    <column name="src_ip">
++      Source IPv4 address to use in the service monitor packet.
++    </column>
++
++    <group title="Service monitor options">
++      <column name="options" key="interval" type='{"type": "integer"}'>
++        The interval, in seconds, between service monitor checks.
++      </column>
++
++      <column name="options" key="timeout" type='{"type": "integer"}'>
++        The time, in seconds, after which the service monitor check times
++        out.
++      </column>
++
++      <column name="options" key="success_count" type='{"type": "integer"}'>
++        The number of successful checks after which the service is
++        considered <code>online</code>.
++      </column>
++
++      <column name="options" key="failure_count" type='{"type": "integer"}'>
++        The number of failure checks after which the service is considered
++        <code>offline</code>.
++      </column>
++    </group>
++
++    <group title="Common Columns">
++      <column name="external_ids">
++        See <em>External IDs</em> at the beginning of this document.
++      </column>
++    </group>
++  </table>
+ </database>
+diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
+index 989ed4f47..da566f900 100644
+--- a/tests/ovn-northd.at
++++ b/tests/ovn-northd.at
+@@ -1061,3 +1061,218 @@ AT_CHECK([ovn-sbctl dump-flows R1 | grep ip6.src=| wc -l], [0], [2
+ ])
+ 
+ AT_CLEANUP
++
++AT_SETUP([ovn -- check Load balancer health check and Service Monitor sync])
++AT_SKIP_IF([test $HAVE_PYTHON = no])
++ovn_start
++
++ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80
++
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1
++
++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
++
++ovn-nbctl --wait=sb -- --id=@hc create \
++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
++health_check @hc
++
++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
++
++# create logical switches and ports
++ovn-nbctl ls-add sw0
++ovn-nbctl --wait=sb lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 \
++"00:00:00:00:00:03 10.0.0.3"
++
++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | wc -l`])
++
++ovn-nbctl ls-add sw1
++ovn-nbctl --wait=sb lsp-add sw1 sw1-p1 -- lsp-set-addresses sw1-p1 \
++"02:00:00:00:00:03 20.0.0.3"
++
++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l`])
++
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | wc -l`])
++
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
++
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l`])
++
++ovn-nbctl --wait=sb ls-lb-add sw0 lb1
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++])
++
++# Delete the Load_Balancer_Health_Check
++ovn-nbctl --wait=sb clear load_balancer . health_check
++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++])
++
++# Create the Load_Balancer_Health_Check again.
++ovn-nbctl --wait=sb -- --id=@hc create \
++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
++health_check @hc
++
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l`])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++])
++
++# Get the uuid of both the service_monitor
++sm_sw0_p1=`ovn-sbctl --bare --columns _uuid find service_monitor logical_port=sw0-p1`
++sm_sw1_p1=`ovn-sbctl --bare --columns _uuid find service_monitor logical_port=sw1-p1`
++
++# Set the service monitor for sw1-p1 to offline
++ovn-sbctl set service_monitor $sm_sw1_p1 status=offline
++
++OVS_WAIT_UNTIL([
++    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
++    test "$status" = "offline"])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
++])
++
++# Set the service monitor for sw0-p1 to offline
++ovn-sbctl set service_monitor $sm_sw0_p1 status=offline
++
++OVS_WAIT_UNTIL([
++    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw0-p1`
++    test "$status" = "offline"])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++])
++
++ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
++| grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;)
++])
++
++# Set the service monitor for sw0-p1 and sw1-p1 to online
++ovn-sbctl set service_monitor $sm_sw0_p1 status=online
++ovn-sbctl set service_monitor $sm_sw1_p1 status=online
++
++OVS_WAIT_UNTIL([
++    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
++    test "$status" = "online"])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++])
++
++# Set the service monitor for sw1-p1 to error
++ovn-sbctl set service_monitor $sm_sw1_p1 status=error
++OVS_WAIT_UNTIL([
++    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
++    test "$status" = "error"])
++
++ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
++| grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
++])
++
++# Add one more vip to lb1
++
++ovn-nbctl set load_balancer . vip:"10.0.0.40\:1000"="10.0.0.3:1000,20.0.0.3:80"
++
++# create health_check for new vip - 10.0.0.40
++ovn-nbctl --wait=sb -- --id=@hc create \
++Load_Balancer_Health_Check vip="10.0.0.40\:1000" -- add Load_Balancer . \
++health_check @hc
++
++# There should be totally 3 rows in service_monitor for -
++#    * 10.0.0.3:80
++#    * 10.0.0.3:1000
++#    * 20.0.0.3:80
++
++OVS_WAIT_UNTIL([test 3 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l`])
++
++# There should be 2 rows with logical_port=sw0-p1
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor logical_port=sw0-p1 | sed '/^$/d' | wc -l`])
++
++# There should be 1 row1 with port=1000
++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor port=1000 | sed '/^$/d' | wc -l`])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000);)
++])
++
++# Set the service monitor for sw1-p1 to online
++ovn-sbctl set service_monitor $sm_sw1_p1 status=online
++
++OVS_WAIT_UNTIL([
++    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
++    test "$status" = "online"])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);)
++])
++
++# Associate lb1 to sw1
++ovn-nbctl --wait=sb ls-lb-add sw1 lb1
++ovn-sbctl dump-flows sw1 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);)
++])
++
++# Now create lb2 same as lb1 but udp protocol.
++ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp
++lb2_uuid=`ovn-nbctl lb-list | grep udp | awk '{print $1}'`
++ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
++ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
++
++ovn-nbctl -- --id=@hc create Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer $lb2_uuid health_check @hc
++
++ovn-nbctl ls-lb-add sw0 lb2
++ovn-nbctl ls-lb-add sw1 lb2
++ovn-nbctl lr-lb-add lr0 lb2
++
++OVS_WAIT_UNTIL([test 5 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l`])
++
++# Change the svc_monitor_mac. This should get reflected in service_monitor table rows.
++ovn-nbctl set NB_Global . options:svc_monitor_mac="fe:a0:65:a2:01:03"
++
++OVS_WAIT_UNTIL([test 5 = `ovn-sbctl --bare --columns src_mac find \
++service_monitor | grep "fe:a0:65:a2:01:03" | wc -l`])
++
++# Change the source ip for 10.0.0.3 backend ip in lb2
++ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.100
++
++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns src_ip find \
++service_monitor logical_port=sw0-p1 | grep "10.0.0.100" | wc -l`])
++
++ovn-nbctl --wait=sb lb-del lb1
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find service_monitor | sed '/^$/d' | wc -l`])
++
++ovn-nbctl --wait=sb lb-del lb2
++OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
++
++AT_CLEANUP
+-- 
+2.23.0
+
diff --git a/SOURCES/0010-Add-a-new-action-handle_svc_check.patch b/SOURCES/0010-Add-a-new-action-handle_svc_check.patch
new file mode 100644
index 0000000..0f7b0fb
--- /dev/null
+++ b/SOURCES/0010-Add-a-new-action-handle_svc_check.patch
@@ -0,0 +1,192 @@
+From bb1755e8dcccfe3b4da44c36c8c1afa1b987f3d1 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Tue, 22 Oct 2019 12:07:46 +0530
+Subject: [PATCH 10/12] Add a new action - handle_svc_check
+
+This action will be used in an upcoming patch to handle the
+service monitor replies from ovn-controller when it sends
+out service monitor requests.
+
+This action gets translated to openflow controller action.
+
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ include/ovn/actions.h     | 17 +++++++++++++++-
+ ovn/lib/actions.c         | 42 +++++++++++++++++++++++++++++++++++++++
+ ovn/ovn-sb.xml            | 17 ++++++++++++++++
+ ovn/utilities/ovn-trace.c |  3 +++
+ tests/ovn.at              | 13 ++++++++++++
+ 5 files changed, 91 insertions(+), 1 deletion(-)
+
+diff --git a/include/ovn/actions.h b/include/ovn/actions.h
+index f4997e9c9..047a8d737 100644
+--- a/include/ovn/actions.h
++++ b/include/ovn/actions.h
+@@ -88,7 +88,8 @@ struct ovn_extend_table;
+     OVNACT(OVNFIELD_LOAD,     ovnact_load)            \
+     OVNACT(CHECK_PKT_LARGER,  ovnact_check_pkt_larger) \
+     OVNACT(TRIGGER_EVENT,     ovnact_controller_event) \
+-    OVNACT(BIND_VPORT,        ovnact_bind_vport)
++    OVNACT(BIND_VPORT,        ovnact_bind_vport)       \
++    OVNACT(HANDLE_SVC_CHECK,  ovnact_handle_svc_check)
+ 
+ /* enum ovnact_type, with a member OVNACT_<ENUM> for each action. */
+ enum OVS_PACKED_ENUM ovnact_type {
+@@ -352,6 +353,12 @@ struct ovnact_bind_vport {
+     struct expr_field vport_parent;     /* Logical virtual port's port name. */
+ };
+ 
++/* OVNACT_HANDLE_SVC_CHECK. */
++struct ovnact_handle_svc_check {
++    struct ovnact ovnact;
++    struct expr_field port;     /* Logical port name. */
++};
++
+ /* 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);
+@@ -537,6 +544,14 @@ enum action_opcode {
+      *    MFF_LOG_INPORT.
+      */
+     ACTION_OPCODE_BIND_VPORT,
++
++    /* "handle_svc_check(port)"."
++     *
++     * Arguments are passed through the packet metadata and data, as follows:
++     *
++     *     MFF_LOG_INPORT = port
++     */
++    ACTION_OPCODE_HANDLE_SVC_CHECK,
+ };
+ 
+ /* Header. */
+diff --git a/ovn/lib/actions.c b/ovn/lib/actions.c
+index 82a9334cb..5441922a7 100644
+--- a/ovn/lib/actions.c
++++ b/ovn/lib/actions.c
+@@ -2814,6 +2814,46 @@ ovnact_bind_vport_free(struct ovnact_bind_vport *bp)
+     free(bp->vport);
+ }
+ 
++static void
++parse_handle_svc_check(struct action_context *ctx OVS_UNUSED)
++{
++     if (!lexer_force_match(ctx->lexer, LEX_T_LPAREN)) {
++        return;
++    }
++
++    struct ovnact_handle_svc_check *svc_chk =
++        ovnact_put_HANDLE_SVC_CHECK(ctx->ovnacts);
++    action_parse_field(ctx, 0, false, &svc_chk->port);
++    lexer_force_match(ctx->lexer, LEX_T_RPAREN);
++}
++
++static void
++format_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk,
++                        struct ds *s)
++{
++    ds_put_cstr(s, "handle_svc_check(");
++    expr_field_format(&svc_chk->port, s);
++    ds_put_cstr(s, ");");
++}
++
++static void
++encode_HANDLE_SVC_CHECK(const struct ovnact_handle_svc_check *svc_chk,
++                        const struct ovnact_encode_params *ep OVS_UNUSED,
++                        struct ofpbuf *ofpacts)
++{
++    const struct arg args[] = {
++        { expr_resolve_field(&svc_chk->port), MFF_LOG_INPORT },
++    };
++    encode_setup_args(args, ARRAY_SIZE(args), ofpacts);
++    encode_controller_op(ACTION_OPCODE_HANDLE_SVC_CHECK, ofpacts);
++    encode_restore_args(args, ARRAY_SIZE(args), ofpacts);
++}
++
++static void
++ovnact_handle_svc_check_free(struct ovnact_handle_svc_check *sc OVS_UNUSED)
++{
++}
++
+ /* Parses an assignment or exchange or put_dhcp_opts action. */
+ static void
+ parse_set_action(struct action_context *ctx)
+@@ -2931,6 +2971,8 @@ parse_action(struct action_context *ctx)
+         parse_trigger_event(ctx, ovnact_put_TRIGGER_EVENT(ctx->ovnacts));
+     } else if (lexer_match_id(ctx->lexer, "bind_vport")) {
+         parse_bind_vport(ctx);
++    } else if (lexer_match_id(ctx->lexer, "handle_svc_check")) {
++        parse_handle_svc_check(ctx);
+     } else {
+         lexer_syntax_error(ctx->lexer, "expecting action");
+     }
+diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
+index 335f9031b..82167c488 100644
+--- a/ovn/ovn-sb.xml
++++ b/ovn/ovn-sb.xml
+@@ -2097,6 +2097,23 @@ tcp.flags = RST;
+             set to <var>P</var>.
+           </p>
+         </dd>
++
++        <dt><code>handle_svc_check(<var>P</var>);</code></dt>
++        <dd>
++          <p>
++            <b>Parameters</b>: logical port string field <var>P</var>.
++          </p>
++
++          <p>
++            Handles the service monitor reply received from the VIF of
++            the logical port <var>P</var>. <code>ovn-controller</code>
++            periodically sends out the service monitor packets for the
++            services configured in the <ref table="Service_Monitor"/>
++            table and this action updates the status of those services.
++          </p>
++
++          <p><b>Example:</b> <code>handle_svc_check(inport);</code></p>
++        </dd>
+       </dl>
+     </column>
+ 
+diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
+index 05d711358..79257335b 100644
+--- a/ovn/utilities/ovn-trace.c
++++ b/ovn/utilities/ovn-trace.c
+@@ -2221,6 +2221,9 @@ trace_actions(const struct ovnact *ovnacts, size_t ovnacts_len,
+ 
+         case OVNACT_BIND_VPORT:
+             break;
++
++        case OVNACT_HANDLE_SVC_CHECK:
++            break;
+         }
+     }
+     ds_destroy(&s);
+diff --git a/tests/ovn.at b/tests/ovn.at
+index 89bd3f174..f68ddcd33 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -1468,6 +1468,19 @@ bind_vport("xyzzy",;
+ bind_vport("xyzzy", inport;
+     Syntax error at `;' expecting `)'.
+ 
++# handle_svc_check
++handle_svc_check(inport);
++    encodes as controller(userdata=00.00.00.12.00.00.00.00)
++
++handle_svc_check(outport);
++    encodes as push:NXM_NX_REG14[],push:NXM_NX_REG15[],pop:NXM_NX_REG14[],controller(userdata=00.00.00.12.00.00.00.00),pop:NXM_NX_REG14[]
++
++handle_svc_check();
++    Syntax error at `)' expecting field name.
++
++handle_svc_check(reg0);
++    Cannot use numeric field reg0 where string field is required.
++
+ # Miscellaneous negative tests.
+ ;
+     Syntax error at `;'.
+-- 
+2.23.0
+
diff --git a/SOURCES/0011-Send-service-monitor-health-checks.patch b/SOURCES/0011-Send-service-monitor-health-checks.patch
new file mode 100644
index 0000000..ba4776f
--- /dev/null
+++ b/SOURCES/0011-Send-service-monitor-health-checks.patch
@@ -0,0 +1,1345 @@
+From b06fa16c0efeacc453f96e5640124acdf980bae7 Mon Sep 17 00:00:00 2001
+From: Numan Siddique <numans@ovn.org>
+Date: Tue, 22 Oct 2019 12:28:36 +0530
+Subject: [PATCH 11/12] Send service monitor health checks
+
+ovn-controller will periodically sends out the service monitor packets
+for the services configured in the SB DB Service_Monitor table. This
+patch makes use of the action - handle_svc_check to handle the service
+monitor reply packets from the service.
+
+This patch supports IPv4 TCP and UDP service monitoring. For TCP services,
+it sends out a TCP SYN packet and expects TCP ACK packet in response.
+If the response is received on time, the status of the service is set to
+"online", otherwise it is set to "offline".
+
+For UDP services, it sends out a empty UDP packet and doesn't expect any
+reply. In case the service is down, the host running the service, sends out
+ICMP type 3 code 4 (destination unreachable) packet. If ovn-controller receives this
+ICMP packet, it sets the status of the service to "offline".
+
+Right now only IPv4 service monitoring is supported. An upcoming patch will add
+the support for IPv6.
+
+Change-Id: Ia8457c020b6d6cea15b1e5a73989a54430d8d9de
+Acked-by: Mark Michelson <mmichels@redhat.com>
+Signed-off-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/controller/ovn-controller.c |   2 +
+ ovn/controller/pinctrl.c        | 775 ++++++++++++++++++++++++++++++--
+ ovn/controller/pinctrl.h        |   2 +
+ ovn/northd/ovn-northd.8.xml     |  10 +
+ ovn/northd/ovn-northd.c         |  18 +
+ tests/ovn.at                    | 119 +++++
+ tests/system-common-macros.at   |   1 +
+ tests/system-ovn.at             | 180 ++++++++
+ 8 files changed, 1081 insertions(+), 26 deletions(-)
+
+diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
+index cb3d63cbb..f2378ab99 100644
+--- a/ovn/controller/ovn-controller.c
++++ b/ovn/controller/ovn-controller.c
+@@ -2046,6 +2046,8 @@ main(int argc, char *argv[])
+                                 sbrec_dns_table_get(ovnsb_idl_loop.idl),
+                                 sbrec_controller_event_table_get(
+                                     ovnsb_idl_loop.idl),
++                                sbrec_service_monitor_table_get(
++                                    ovnsb_idl_loop.idl),
+                                 br_int, chassis,
+                                 &ed_runtime_data.local_datapaths,
+                                 &ed_runtime_data.active_tunnels);
+diff --git a/ovn/controller/pinctrl.c b/ovn/controller/pinctrl.c
+index c6dd69fb3..ae1b8e1ca 100644
+--- a/ovn/controller/pinctrl.c
++++ b/ovn/controller/pinctrl.c
+@@ -38,6 +38,7 @@
+ #include "openvswitch/ofp-switch.h"
+ #include "openvswitch/ofp-util.h"
+ #include "openvswitch/vlog.h"
++#include "lib/random.h"
+ 
+ #include "lib/dhcp.h"
+ #include "ovn-controller.h"
+@@ -284,6 +285,22 @@ static void run_put_vport_bindings(
+ static void wait_put_vport_bindings(struct ovsdb_idl_txn *ovnsb_idl_txn);
+ static void pinctrl_handle_bind_vport(const struct flow *md,
+                                       struct ofpbuf *userdata);
++static void pinctrl_handle_svc_check(struct rconn *swconn,
++                                     const struct flow *ip_flow,
++                                     struct dp_packet *pkt_in,
++                                     const struct match *md);
++static void init_svc_monitors(void);
++static void destroy_svc_monitors(void);
++static void sync_svc_monitors(
++    struct ovsdb_idl_txn *ovnsb_idl_txn,
++    const struct sbrec_service_monitor_table *svc_mon_table,
++    struct ovsdb_idl_index *sbrec_port_binding_by_name,
++    const struct sbrec_chassis *our_chassis)
++    OVS_REQUIRES(pinctrl_mutex);
++static void svc_monitors_run(struct rconn *swconn,
++                             long long int *svc_monitors_next_run_time)
++    OVS_REQUIRES(pinctrl_mutex);
++static void svc_monitors_wait(long long int svc_monitors_next_run_time);
+ 
+ COVERAGE_DEFINE(pinctrl_drop_put_mac_binding);
+ COVERAGE_DEFINE(pinctrl_drop_buffered_packets_map);
+@@ -446,6 +463,7 @@ pinctrl_init(void)
+     init_event_table();
+     ip_mcast_snoop_init();
+     init_put_vport_bindings();
++    init_svc_monitors();
+     pinctrl.br_int_name = NULL;
+     pinctrl_handler_seq = seq_create();
+     pinctrl_main_seq = seq_create();
+@@ -1983,6 +2001,13 @@ process_packet_in(struct rconn *swconn, const struct ofp_header *msg)
+         ovs_mutex_unlock(&pinctrl_mutex);
+         break;
+ 
++    case ACTION_OPCODE_HANDLE_SVC_CHECK:
++        ovs_mutex_lock(&pinctrl_mutex);
++        pinctrl_handle_svc_check(swconn, &headers, &packet,
++                                 &pin.flow_metadata);
++        ovs_mutex_unlock(&pinctrl_mutex);
++        break;
++
+     default:
+         VLOG_WARN_RL(&rl, "unrecognized packet-in opcode %"PRIu32,
+                      ntohl(ah->opcode));
+@@ -2052,6 +2077,7 @@ pinctrl_handler(void *arg_)
+     static long long int send_garp_time = LLONG_MAX;
+     /* Next multicast query (IGMP) in ms. */
+     static long long int send_mcast_query_time = LLONG_MAX;
++    static long long int svc_monitors_next_run_time = LLONG_MAX;
+ 
+     swconn = rconn_create(5, 0, DSCP_DEFAULT, 1 << OFP13_VERSION);
+ 
+@@ -2112,11 +2138,16 @@ pinctrl_handler(void *arg_)
+             }
+         }
+ 
++        ovs_mutex_lock(&pinctrl_mutex);
++        svc_monitors_run(swconn, &svc_monitors_next_run_time);
++        ovs_mutex_unlock(&pinctrl_mutex);
++
+         rconn_run_wait(swconn);
+         rconn_recv_wait(swconn);
+         send_garp_wait(send_garp_time);
+         ipv6_ra_wait(send_ipv6_ra_time);
+         ip_mcast_querier_wait(send_mcast_query_time);
++        svc_monitors_wait(svc_monitors_next_run_time);
+ 
+         new_seq = seq_read(pinctrl_handler_seq);
+         seq_wait(pinctrl_handler_seq, new_seq);
+@@ -2142,6 +2173,7 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+             struct ovsdb_idl_index *sbrec_ip_multicast_opts,
+             const struct sbrec_dns_table *dns_table,
+             const struct sbrec_controller_event_table *ce_table,
++            const struct sbrec_service_monitor_table *svc_mon_table,
+             const struct ovsrec_bridge *br_int,
+             const struct sbrec_chassis *chassis,
+             const struct hmap *local_datapaths,
+@@ -2178,6 +2210,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+     run_buffered_binding(sbrec_port_binding_by_datapath,
+                          sbrec_mac_binding_by_lport_ip,
+                          local_datapaths);
++    sync_svc_monitors(ovnsb_idl_txn, svc_mon_table, sbrec_port_binding_by_name,
++                      chassis);
+     ovs_mutex_unlock(&pinctrl_mutex);
+ }
+ 
+@@ -2726,6 +2760,7 @@ pinctrl_destroy(void)
+     destroy_put_vport_bindings();
+     destroy_dns_cache();
+     ip_mcast_snoop_destroy();
++    destroy_svc_monitors();
+     seq_destroy(pinctrl_main_seq);
+     seq_destroy(pinctrl_handler_seq);
+ }
+@@ -3158,6 +3193,36 @@ send_garp(struct rconn *swconn, struct garp_data *garp,
+     return garp->announce_time;
+ }
+ 
++static void
++pinctrl_compose_ipv4(struct dp_packet *packet, struct eth_addr eth_src,
++                     struct eth_addr eth_dst, ovs_be32 ipv4_src,
++                     ovs_be32 ipv4_dst, uint8_t ip_proto, uint8_t ttl,
++                     uint16_t ip_payload_len)
++{
++    dp_packet_clear(packet);
++    packet->packet_type = htonl(PT_ETH);
++
++    struct eth_header *eh = dp_packet_put_zeros(packet, sizeof *eh);
++    eh->eth_dst = eth_dst;
++    eh->eth_src = eth_src;
++
++    struct ip_header *nh = dp_packet_put_zeros(packet, sizeof *nh);
++
++    eh->eth_type = htons(ETH_TYPE_IP);
++    dp_packet_set_l3(packet, nh);
++    nh->ip_ihl_ver = IP_IHL_VER(5, 4);
++    nh->ip_tot_len = htons(sizeof(struct ip_header) + ip_payload_len);
++    nh->ip_tos = IP_DSCP_CS6;
++    nh->ip_proto = ip_proto;
++    nh->ip_frag_off = htons(IP_DF);
++
++    /* Setting tos and ttl to 0 and 1 respectively. */
++    packet_set_ipv4(packet, ipv4_src, ipv4_dst, 0, ttl);
++
++    nh->ip_csum = 0;
++    nh->ip_csum = csum(nh, sizeof *nh);
++}
++
+ /*
+  * Multicast snooping configuration.
+  */
+@@ -3775,32 +3840,11 @@ ip_mcast_querier_send(struct rconn *swconn, struct ip_mcast_snoop *ip_ms,
+     struct dp_packet packet;
+ 
+     dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
+-
+-    uint8_t ip_tos = 0;
+-    uint8_t igmp_ttl = 1;
+-
+-    dp_packet_clear(&packet);
+-    packet.packet_type = htonl(PT_ETH);
+-
+-    struct eth_header *eh = dp_packet_put_zeros(&packet, sizeof *eh);
+-    eh->eth_dst = ip_ms->cfg.query_eth_dst;
+-    eh->eth_src = ip_ms->cfg.query_eth_src;
+-
+-    struct ip_header *nh = dp_packet_put_zeros(&packet, sizeof *nh);
+-
+-    eh->eth_type = htons(ETH_TYPE_IP);
+-    dp_packet_set_l3(&packet, nh);
+-    nh->ip_ihl_ver = IP_IHL_VER(5, 4);
+-    nh->ip_tot_len = htons(sizeof(struct ip_header) +
+-                            sizeof(struct igmpv3_query_header));
+-    nh->ip_tos = IP_DSCP_CS6;
+-    nh->ip_proto = IPPROTO_IGMP;
+-    nh->ip_frag_off = htons(IP_DF);
+-    packet_set_ipv4(&packet, ip_ms->cfg.query_ipv4_src,
+-                    ip_ms->cfg.query_ipv4_dst, ip_tos, igmp_ttl);
+-
+-    nh->ip_csum = 0;
+-    nh->ip_csum = csum(nh, sizeof *nh);
++    pinctrl_compose_ipv4(&packet, ip_ms->cfg.query_eth_src,
++                         ip_ms->cfg.query_eth_dst,
++                         ip_ms->cfg.query_ipv4_src,
++                         ip_ms->cfg.query_ipv4_dst,
++                         IPPROTO_IGMP, 1, sizeof(struct igmpv3_query_header));
+ 
+     struct igmpv3_query_header *igh =
+         dp_packet_put_zeros(&packet, sizeof *igh);
+@@ -4693,3 +4737,682 @@ pinctrl_handle_bind_vport(
+ 
+     notify_pinctrl_main();
+ }
++
++enum svc_monitor_state {
++    SVC_MON_S_INIT,
++    SVC_MON_S_WAITING,
++    SVC_MON_S_ONLINE,
++    SVC_MON_S_OFFLINE,
++};
++
++enum svc_monitor_status {
++    SVC_MON_ST_UNKNOWN,
++    SVC_MON_ST_OFFLINE,
++    SVC_MON_ST_ONLINE,
++};
++
++enum svc_monitor_protocol {
++    SVC_MON_PROTO_TCP,
++    SVC_MON_PROTO_UDP,
++};
++
++/* Service monitor health checks. */
++struct svc_monitor {
++    struct hmap_node hmap_node;
++    struct ovs_list list_node;
++
++    /* Should be accessed only with in the main ovn-controller
++     * thread. */
++    const struct sbrec_service_monitor *sb_svc_mon;
++
++    /* key */
++    struct in6_addr ip;
++    uint32_t dp_key;
++    uint32_t port_key;
++    uint32_t proto_port; /* tcp/udp port */
++
++    struct eth_addr ea;
++    long long int timestamp;
++    bool is_ip6;
++
++    long long int wait_time;
++    long long int next_send_time;
++
++    struct smap options;
++    /* The interval, in milli seconds, between service monitor checks. */
++    int interval;
++
++    /* The time, in milli seconds, after which the service monitor check
++     * times out. */
++    int svc_timeout;
++
++    /* The number of successful checks after which the service is
++     * considered online. */
++    int success_count;
++    int n_success;
++
++    /* The number of failure checks after which the service is
++     * considered offline. */
++    int failure_count;
++    int n_failures;
++
++    enum svc_monitor_protocol protocol;
++    enum svc_monitor_state state;
++    enum svc_monitor_status status;
++    struct dp_packet pkt;
++
++    uint32_t seq_no;
++    ovs_be16 tp_src;
++
++    bool delete;
++};
++
++static struct hmap svc_monitors_map;
++static struct ovs_list svc_monitors;
++
++static void
++init_svc_monitors(void)
++{
++    hmap_init(&svc_monitors_map);
++    ovs_list_init(&svc_monitors);
++}
++
++static void
++destroy_svc_monitors(void)
++{
++    struct svc_monitor *svc;
++    HMAP_FOR_EACH_POP (svc, hmap_node, &svc_monitors_map) {
++
++    }
++
++    hmap_destroy(&svc_monitors_map);
++
++    LIST_FOR_EACH_POP (svc, list_node, &svc_monitors) {
++        smap_destroy(&svc->options);
++        free(svc);
++    }
++}
++
++
++static struct svc_monitor *
++pinctrl_find_svc_monitor(uint32_t dp_key, uint32_t port_key,
++                         const struct in6_addr *ip_key, uint32_t port,
++                         enum svc_monitor_protocol protocol,
++                         uint32_t hash)
++{
++    struct svc_monitor *svc;
++    HMAP_FOR_EACH_WITH_HASH (svc, hmap_node, hash, &svc_monitors_map) {
++        if (svc->dp_key == dp_key
++            && svc->port_key == port_key
++            && svc->proto_port == port
++            && IN6_ARE_ADDR_EQUAL(&svc->ip, ip_key)
++            && svc->protocol == protocol) {
++            return svc;
++        }
++    }
++    return NULL;
++}
++
++static void
++sync_svc_monitors(struct ovsdb_idl_txn *ovnsb_idl_txn,
++                  const struct sbrec_service_monitor_table *svc_mon_table,
++                  struct ovsdb_idl_index *sbrec_port_binding_by_name,
++                  const struct sbrec_chassis *our_chassis)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    bool changed = false;
++    struct svc_monitor *svc_mon;
++
++    LIST_FOR_EACH (svc_mon, list_node, &svc_monitors) {
++        svc_mon->delete = true;
++    }
++
++    const struct sbrec_service_monitor *sb_svc_mon;
++    SBREC_SERVICE_MONITOR_TABLE_FOR_EACH (sb_svc_mon, svc_mon_table) {
++        const struct sbrec_port_binding *pb
++            = lport_lookup_by_name(sbrec_port_binding_by_name,
++                                   sb_svc_mon->logical_port);
++        if (!pb) {
++            continue;
++        }
++
++        if (pb->chassis != our_chassis) {
++            continue;
++        }
++
++        struct in6_addr ip_addr;
++        ovs_be32 ip4;
++        if (ip_parse(sb_svc_mon->ip, &ip4)) {
++            ip_addr = in6_addr_mapped_ipv4(ip4);
++        } else {
++            continue;
++        }
++
++        struct eth_addr ea;
++        bool mac_found = false;
++        for (size_t i = 0; i < pb->n_mac; i++) {
++            struct lport_addresses laddrs;
++            if (!extract_lsp_addresses(pb->mac[i], &laddrs)) {
++                continue;
++            }
++
++            for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) {
++                if (ip4 == laddrs.ipv4_addrs[j].addr) {
++                    ea = laddrs.ea;
++                    mac_found = true;
++                    break;
++                }
++            }
++
++            if (mac_found) {
++                break;
++            }
++        }
++
++        if (!mac_found) {
++            continue;
++        }
++
++        uint32_t dp_key = pb->datapath->tunnel_key;
++        uint32_t port_key = pb->tunnel_key;
++        uint32_t hash =
++            hash_bytes(&ip_addr, sizeof ip_addr,
++                       hash_3words(dp_key, port_key, sb_svc_mon->port));
++
++        enum svc_monitor_protocol protocol;
++        if (!sb_svc_mon->protocol || strcmp(sb_svc_mon->protocol, "udp")) {
++            protocol = SVC_MON_PROTO_TCP;
++        } else {
++            protocol = SVC_MON_PROTO_UDP;
++        }
++
++        svc_mon = pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr,
++                                           sb_svc_mon->port, protocol, hash);
++
++        if (!svc_mon) {
++            svc_mon = xmalloc(sizeof *svc_mon);
++            svc_mon->dp_key = dp_key;
++            svc_mon->port_key = port_key;
++            svc_mon->proto_port = sb_svc_mon->port;
++            svc_mon->ip = ip_addr;
++            svc_mon->is_ip6 = false;
++            svc_mon->state = SVC_MON_S_INIT;
++            svc_mon->status = SVC_MON_ST_UNKNOWN;
++            svc_mon->protocol = protocol;
++
++            smap_init(&svc_mon->options);
++            svc_mon->interval =
++                smap_get_int(&svc_mon->options, "interval", 5) * 1000;
++            svc_mon->svc_timeout =
++                smap_get_int(&svc_mon->options, "timeout", 3) * 1000;
++            svc_mon->success_count =
++                smap_get_int(&svc_mon->options, "success_count", 1);
++            svc_mon->failure_count =
++                smap_get_int(&svc_mon->options, "failure_count", 1);
++            svc_mon->n_success = 0;
++            svc_mon->n_failures = 0;
++
++            hmap_insert(&svc_monitors_map, &svc_mon->hmap_node, hash);
++            ovs_list_push_back(&svc_monitors, &svc_mon->list_node);
++            changed = true;
++        }
++
++        svc_mon->sb_svc_mon = sb_svc_mon;
++        svc_mon->ea = ea;
++        if (!smap_equal(&svc_mon->options, &sb_svc_mon->options)) {
++            smap_destroy(&svc_mon->options);
++            smap_clone(&svc_mon->options, &sb_svc_mon->options);
++            svc_mon->interval =
++                smap_get_int(&svc_mon->options, "interval", 5) * 1000;
++            svc_mon->svc_timeout =
++                smap_get_int(&svc_mon->options, "timeout", 3) * 1000;
++            svc_mon->success_count =
++                smap_get_int(&svc_mon->options, "success_count", 1);
++            svc_mon->failure_count =
++                smap_get_int(&svc_mon->options, "failure_count", 1);
++            changed = true;
++        }
++
++        svc_mon->delete = false;
++    }
++
++    struct svc_monitor *next;
++    LIST_FOR_EACH_SAFE (svc_mon, next, list_node, &svc_monitors) {
++        if (svc_mon->delete) {
++            hmap_remove(&svc_monitors_map, &svc_mon->hmap_node);
++            ovs_list_remove(&svc_mon->list_node);
++            smap_destroy(&svc_mon->options);
++            free(svc_mon);
++            changed = true;
++        } else if (ovnsb_idl_txn) {
++            /* Update the status of the service monitor. */
++            if (svc_mon->status != SVC_MON_ST_UNKNOWN) {
++                if (svc_mon->status == SVC_MON_ST_ONLINE) {
++                    sbrec_service_monitor_set_status(svc_mon->sb_svc_mon,
++                                                     "online");
++                } else {
++                    sbrec_service_monitor_set_status(svc_mon->sb_svc_mon,
++                                                     "offline");
++                }
++            }
++        }
++    }
++
++    if (changed) {
++        notify_pinctrl_handler();
++    }
++
++}
++
++static uint16_t
++get_random_src_port(void)
++{
++    uint16_t random_src_port = random_uint16();
++    while (random_src_port < 1024) {
++        random_src_port = random_uint16();
++    }
++
++    return random_src_port;
++}
++
++static void
++svc_monitor_send_tcp_health_check__(struct rconn *swconn,
++                                    struct svc_monitor *svc_mon,
++                                    uint16_t ctl_flags,
++                                    ovs_be32 tcp_seq,
++                                    ovs_be32 tcp_ack,
++                                    ovs_be16 tcp_src)
++{
++    if (svc_mon->is_ip6) {
++        return;
++    }
++
++    /* Compose a TCP-SYN packet. */
++    uint64_t packet_stub[128 / 8];
++    struct dp_packet packet;
++
++    struct eth_addr eth_src;
++    eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, &eth_src);
++    ovs_be32 ip4_src;
++    ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
++
++    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
++    pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
++                         ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
++                         IPPROTO_TCP, 63, TCP_HEADER_LEN);
++
++    struct tcp_header *th = dp_packet_put_zeros(&packet, sizeof *th);
++    dp_packet_set_l4(&packet, th);
++    th->tcp_dst = htons(svc_mon->proto_port);
++    th->tcp_src = tcp_src;
++
++    th->tcp_ctl = htons((5 << 12) | ctl_flags);
++    put_16aligned_be32(&th->tcp_seq, tcp_seq);
++    put_16aligned_be32(&th->tcp_ack, tcp_ack);
++
++    th->tcp_winsz = htons(65160);
++
++    uint32_t csum;
++    csum = packet_csum_pseudoheader(dp_packet_l3(&packet));
++    csum = csum_continue(csum, th, dp_packet_size(&packet) -
++                         ((const unsigned char *)th -
++                         (const unsigned char *)dp_packet_eth(&packet)));
++    th->tcp_csum = csum_finish(csum);
++
++    uint64_t ofpacts_stub[4096 / 8];
++    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
++    enum ofp_version version = rconn_get_version(swconn);
++    put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
++    put_load(svc_mon->port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
++    put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts);
++    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
++    resubmit->in_port = OFPP_CONTROLLER;
++    resubmit->table_id = OFTABLE_LOCAL_OUTPUT;
++
++    struct ofputil_packet_out po = {
++        .packet = dp_packet_data(&packet),
++        .packet_len = dp_packet_size(&packet),
++        .buffer_id = UINT32_MAX,
++        .ofpacts = ofpacts.data,
++        .ofpacts_len = ofpacts.size,
++    };
++    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
++    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
++    queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
++    dp_packet_uninit(&packet);
++    ofpbuf_uninit(&ofpacts);
++}
++
++static void
++svc_monitor_send_udp_health_check(struct rconn *swconn,
++                                  struct svc_monitor *svc_mon,
++                                  ovs_be16 udp_src)
++{
++    if (svc_mon->is_ip6) {
++        return;
++    }
++
++    struct eth_addr eth_src;
++    eth_addr_from_string(svc_mon->sb_svc_mon->src_mac, &eth_src);
++    ovs_be32 ip4_src;
++    ip_parse(svc_mon->sb_svc_mon->src_ip, &ip4_src);
++
++    uint64_t packet_stub[128 / 8];
++    struct dp_packet packet;
++    dp_packet_use_stub(&packet, packet_stub, sizeof packet_stub);
++    pinctrl_compose_ipv4(&packet, eth_src, svc_mon->ea,
++                         ip4_src, in6_addr_get_mapped_ipv4(&svc_mon->ip),
++                         IPPROTO_UDP, 63, UDP_HEADER_LEN + 8);
++
++    struct udp_header *uh = dp_packet_put_zeros(&packet, sizeof *uh);
++    dp_packet_set_l4(&packet, uh);
++    uh->udp_dst = htons(svc_mon->proto_port);
++    uh->udp_src = udp_src;
++    uh->udp_len = htons(UDP_HEADER_LEN + 8);
++    uh->udp_csum = 0;
++    dp_packet_put_zeros(&packet, 8);
++
++    uint64_t ofpacts_stub[4096 / 8];
++    struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(ofpacts_stub);
++    enum ofp_version version = rconn_get_version(swconn);
++    put_load(svc_mon->dp_key, MFF_LOG_DATAPATH, 0, 64, &ofpacts);
++    put_load(svc_mon->port_key, MFF_LOG_OUTPORT, 0, 32, &ofpacts);
++    put_load(1, MFF_LOG_FLAGS, MLF_LOCAL_ONLY, 1, &ofpacts);
++    struct ofpact_resubmit *resubmit = ofpact_put_RESUBMIT(&ofpacts);
++    resubmit->in_port = OFPP_CONTROLLER;
++    resubmit->table_id = OFTABLE_LOCAL_OUTPUT;
++
++    struct ofputil_packet_out po = {
++        .packet = dp_packet_data(&packet),
++        .packet_len = dp_packet_size(&packet),
++        .buffer_id = UINT32_MAX,
++        .ofpacts = ofpacts.data,
++        .ofpacts_len = ofpacts.size,
++    };
++    match_set_in_port(&po.flow_metadata, OFPP_CONTROLLER);
++    enum ofputil_protocol proto = ofputil_protocol_from_ofp_version(version);
++    queue_msg(swconn, ofputil_encode_packet_out(&po, proto));
++    dp_packet_uninit(&packet);
++    ofpbuf_uninit(&ofpacts);
++}
++
++static void
++svc_monitor_send_health_check(struct rconn *swconn,
++                              struct svc_monitor *svc_mon)
++{
++    if (svc_mon->protocol == SVC_MON_PROTO_TCP) {
++        svc_mon->seq_no = random_uint32();
++        svc_mon->tp_src = htons(get_random_src_port());
++        svc_monitor_send_tcp_health_check__(swconn, svc_mon,
++                                            TCP_SYN,
++                                            htonl(svc_mon->seq_no), htonl(0),
++                                            svc_mon->tp_src);
++    } else {
++        if (!svc_mon->tp_src) {
++            svc_mon->tp_src = htons(get_random_src_port());
++        }
++        svc_monitor_send_udp_health_check(swconn, svc_mon, svc_mon->tp_src);
++    }
++
++    svc_mon->wait_time = time_msec() + svc_mon->svc_timeout;
++    svc_mon->state = SVC_MON_S_WAITING;
++}
++
++static void
++svc_monitors_run(struct rconn *swconn,
++                 long long int *svc_monitors_next_run_time)
++    OVS_REQUIRES(pinctrl_mutex)
++{
++    *svc_monitors_next_run_time = LLONG_MAX;
++    struct svc_monitor *svc_mon;
++    LIST_FOR_EACH (svc_mon, list_node, &svc_monitors) {
++        char ip_[INET6_ADDRSTRLEN + 1];
++        memset(ip_, 0, INET6_ADDRSTRLEN + 1);
++        ipv6_string_mapped(ip_, &svc_mon->ip);
++
++        long long int current_time = time_msec();
++        long long int next_run_time = LLONG_MAX;
++        enum svc_monitor_status old_status = svc_mon->status;
++        switch (svc_mon->state) {
++        case SVC_MON_S_INIT:
++            svc_monitor_send_health_check(swconn, svc_mon);
++            next_run_time = svc_mon->wait_time;
++            break;
++
++        case SVC_MON_S_WAITING:
++            if (current_time > svc_mon->wait_time) {
++                if (svc_mon->protocol ==  SVC_MON_PROTO_TCP) {
++                    svc_mon->n_failures++;
++                    svc_mon->state = SVC_MON_S_OFFLINE;
++                } else {
++                    svc_mon->n_success++;
++                    svc_mon->state = SVC_MON_S_ONLINE;
++                }
++                svc_mon->next_send_time = current_time + svc_mon->interval;
++                next_run_time = svc_mon->next_send_time;
++            } else {
++                next_run_time = svc_mon->wait_time - current_time;
++                next_run_time = svc_mon->wait_time;
++            }
++            break;
++
++        case SVC_MON_S_ONLINE:
++            if (svc_mon->n_success >= svc_mon->success_count) {
++                svc_mon->status = SVC_MON_ST_ONLINE;
++                svc_mon->n_success = 0;
++            }
++            if (current_time >= svc_mon->next_send_time) {
++                svc_monitor_send_health_check(swconn, svc_mon);
++                next_run_time = svc_mon->wait_time;
++            } else {
++                next_run_time = svc_mon->next_send_time;
++            }
++            break;
++
++        case SVC_MON_S_OFFLINE:
++            if (svc_mon->n_failures >= svc_mon->failure_count) {
++                svc_mon->status = SVC_MON_ST_OFFLINE;
++                svc_mon->n_failures = 0;
++            }
++
++            if (current_time >= svc_mon->next_send_time) {
++                svc_monitor_send_health_check(swconn, svc_mon);
++                next_run_time = svc_mon->wait_time;
++            } else {
++                next_run_time = svc_mon->next_send_time;
++            }
++            break;
++
++        default:
++            OVS_NOT_REACHED();
++        }
++
++        if (*svc_monitors_next_run_time > next_run_time) {
++            *svc_monitors_next_run_time = next_run_time;
++        }
++
++        if (old_status != svc_mon->status) {
++            /* Notify the main thread to update the status in the SB DB. */
++            notify_pinctrl_main();
++        }
++    }
++}
++
++static void
++svc_monitors_wait(long long int svc_monitors_next_run_time)
++{
++    if (!ovs_list_is_empty(&svc_monitors)) {
++        poll_timer_wait_until(svc_monitors_next_run_time);
++    }
++}
++
++static bool
++pinctrl_handle_tcp_svc_check(struct rconn *swconn,
++                             struct dp_packet *pkt_in,
++                             struct svc_monitor *svc_mon)
++{
++    struct tcp_header *th = dp_packet_l4(pkt_in);
++
++    if (!th) {
++        return false;
++    }
++
++    uint32_t tcp_seq = ntohl(get_16aligned_be32(&th->tcp_seq));
++    uint32_t tcp_ack = ntohl(get_16aligned_be32(&th->tcp_ack));
++
++    if (th->tcp_dst != svc_mon->tp_src) {
++       return false;
++    }
++
++    if (tcp_ack != (svc_mon->seq_no + 1)) {
++        return false;
++    }
++
++    /* Check for SYN flag and Ack flag. */
++    if ((TCP_FLAGS(th->tcp_ctl) & (TCP_SYN | TCP_ACK))
++         == (TCP_SYN | TCP_ACK)) {
++        svc_mon->n_success++;
++        svc_mon->state = SVC_MON_S_ONLINE;
++
++        /* Send RST-ACK packet. */
++        svc_monitor_send_tcp_health_check__(swconn, svc_mon, TCP_RST | TCP_ACK,
++                                            htonl(tcp_ack + 1),
++                                            htonl(tcp_seq + 1), th->tcp_dst);
++        /* Calculate next_send_time. */
++        svc_mon->next_send_time = time_msec() + svc_mon->interval;
++        return true;
++    }
++
++    /* Check if RST flag is set. */
++    if (TCP_FLAGS(th->tcp_ctl) & TCP_RST) {
++        svc_mon->n_failures++;
++        svc_mon->state = SVC_MON_S_OFFLINE;
++
++        /* Calculate next_send_time. */
++        svc_mon->next_send_time = time_msec() + svc_mon->interval;
++        return false;
++    }
++
++    return false;
++}
++
++static void
++pinctrl_handle_svc_check(struct rconn *swconn, const struct flow *ip_flow,
++                         struct dp_packet *pkt_in, const struct match *md)
++{
++    uint32_t dp_key = ntohll(md->flow.metadata);
++    uint32_t port_key = md->flow.regs[MFF_LOG_INPORT - MFF_REG0];
++    struct in6_addr ip_addr;
++    struct eth_header *in_eth = dp_packet_data(pkt_in);
++    struct ip_header *in_ip = dp_packet_l3(pkt_in);
++
++    if (in_ip->ip_proto != IPPROTO_TCP && in_ip->ip_proto != IPPROTO_ICMP) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl,
++                     "handle service check: Unsupported protocol - [%x]",
++                     in_ip->ip_proto);
++        return;
++    }
++
++    uint16_t in_ip_len = ntohs(in_ip->ip_tot_len);
++    if (in_ip_len < IP_HEADER_LEN) {
++        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++        VLOG_WARN_RL(&rl,
++                     "IP packet with invalid length (%u)",
++                     in_ip_len);
++        return;
++    }
++
++    if (in_eth->eth_type == htons(ETH_TYPE_IP)) {
++        ip_addr = in6_addr_mapped_ipv4(ip_flow->nw_src);
++    } else {
++        ip_addr = ip_flow->ipv6_dst;
++    }
++
++    if (in_ip->ip_proto == IPPROTO_TCP) {
++        uint32_t hash =
++            hash_bytes(&ip_addr, sizeof ip_addr,
++                       hash_3words(dp_key, port_key, ntohs(ip_flow->tp_src)));
++
++        struct svc_monitor *svc_mon =
++            pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr,
++                                     ntohs(ip_flow->tp_src),
++                                     SVC_MON_PROTO_TCP, hash);
++        if (!svc_mon) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "handle service check: Service monitor "
++                         "not found");
++            return;
++        }
++        pinctrl_handle_tcp_svc_check(swconn, pkt_in, svc_mon);
++    } else {
++        /* It's ICMP packet. */
++        struct icmp_header *ih = dp_packet_l4(pkt_in);
++        if (!ih) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "ICMPv4 packet with invalid header");
++            return;
++        }
++
++        if (ih->icmp_type != ICMP4_DST_UNREACH || ih->icmp_code != 3) {
++            return;
++        }
++
++        const char *end =
++            (char *)dp_packet_l4(pkt_in) + dp_packet_l4_size(pkt_in);
++
++        const struct ip_header *orig_ip_hr =
++            dp_packet_get_icmp_payload(pkt_in);
++        if (!orig_ip_hr) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "Original IP datagram not present in "
++                         "ICMP packet");
++            return;
++        }
++
++        if (ntohs(orig_ip_hr->ip_tot_len) !=
++            (IP_HEADER_LEN + UDP_HEADER_LEN + 8)) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "Invalid original IP datagram length present "
++                         "in ICMP packet");
++            return;
++        }
++
++        struct udp_header *orig_uh = (struct udp_header *) (orig_ip_hr + 1);
++        if ((char *)orig_uh >= end) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "Invalid UDP header in the original "
++                         "IP datagram");
++            return;
++        }
++
++        uint32_t hash =
++            hash_bytes(&ip_addr, sizeof ip_addr,
++                       hash_3words(dp_key, port_key, ntohs(orig_uh->udp_dst)));
++
++        struct svc_monitor *svc_mon =
++            pinctrl_find_svc_monitor(dp_key, port_key, &ip_addr,
++                                     ntohs(orig_uh->udp_dst),
++                                     SVC_MON_PROTO_UDP, hash);
++        if (!svc_mon) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "handle service check: Service monitor not "
++                         "found for ICMP packet");
++            return;
++        }
++
++        if (orig_uh->udp_src != svc_mon->tp_src) {
++            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
++            VLOG_WARN_RL(&rl, "handle service check: UDP src port doesn't "
++                         "match in the Original IP datagram of ICMP packet");
++            return;
++        }
++
++        /* The UDP service monitor is down. */
++        svc_mon->n_failures++;
++        svc_mon->state = SVC_MON_S_OFFLINE;
++
++        /* Calculate next_send_time. */
++        svc_mon->next_send_time = time_msec() + svc_mon->interval;
++    }
++}
+diff --git a/ovn/controller/pinctrl.h b/ovn/controller/pinctrl.h
+index fcfce6bcf..208272442 100644
+--- a/ovn/controller/pinctrl.h
++++ b/ovn/controller/pinctrl.h
+@@ -30,6 +30,7 @@ struct ovsrec_bridge;
+ struct sbrec_chassis;
+ struct sbrec_dns_table;
+ struct sbrec_controller_event_table;
++struct sbrec_service_monitor_table;
+ 
+ void pinctrl_init(void);
+ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+@@ -42,6 +43,7 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
+                  struct ovsdb_idl_index *sbrec_ip_multicast_opts,
+                  const struct sbrec_dns_table *,
+                  const struct sbrec_controller_event_table *,
++                 const struct sbrec_service_monitor_table *,
+                  const struct ovsrec_bridge *, const struct sbrec_chassis *,
+                  const struct hmap *local_datapaths,
+                  const struct sset *active_tunnels);
+diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
+index 9515c7790..956a10362 100644
+--- a/ovn/northd/ovn-northd.8.xml
++++ b/ovn/northd/ovn-northd.8.xml
+@@ -1009,6 +1009,16 @@ output;
+     </p>
+ 
+     <ul>
++      <li>
++        A priorirty-110 flow with the match
++        <code>eth.src == <var>E</var></code> for all logical switch
++        datapaths and applies the action <code>handle_svc_check(inport)</code>.
++        Where <var>E</var> is the service monitor mac defined in the
++        <ref column="options:svc_monitor_mac" table="NB_Global"
++        db="OVN_Northbound"/> colum of <ref table="NB_Global"
++        db="OVN_Northbound"/> table.
++      </li>
++
+       <li>
+         A priority-100 flow that punts all IGMP packets to
+         <code>ovn-controller</code> if IGMP snooping is enabled on the
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index 1ded5dd9b..ec8f9a70f 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -6182,6 +6182,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         }
+     }
+ 
++    char *svc_check_match = xasprintf("eth.dst == %s", svc_monitor_mac);
+     /* Ingress table 17: Destination lookup, broadcast and multicast handling
+      * (priority 70 - 100). */
+     HMAP_FOR_EACH (od, key_node, datapaths) {
+@@ -6189,6 +6190,9 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+             continue;
+         }
+ 
++        ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 110, svc_check_match,
++                      "handle_svc_check(inport);");
++
+         struct mcast_switch_info *mcast_sw_info = &od->mcast_info.sw;
+ 
+         if (mcast_sw_info->enabled) {
+@@ -6248,6 +6252,7 @@ build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
+         ovn_lflow_add(lflows, od, S_SWITCH_IN_L2_LKUP, 70, "eth.mcast",
+                       "outport = \""MC_FLOOD"\"; output;");
+     }
++    free(svc_check_match);
+ 
+     /* Ingress table 17: Add IP multicast flows learnt from IGMP
+      * (priority 90). */
+@@ -10521,6 +10526,11 @@ static const char *rbac_mac_binding_auth[] =
+ static const char *rbac_mac_binding_update[] =
+     {"logical_port", "ip", "mac", "datapath"};
+ 
++static const char *rbac_svc_monitor_auth[] =
++    {""};
++static const char *rbac_svc_monitor_auth_update[] =
++    {"status"};
++
+ static struct rbac_perm_cfg {
+     const char *table;
+     const char **auth;
+@@ -10562,6 +10572,14 @@ static struct rbac_perm_cfg {
+         .update = rbac_mac_binding_update,
+         .n_update = ARRAY_SIZE(rbac_mac_binding_update),
+         .row = NULL
++    },{
++        .table = "Service_Monitor",
++        .auth = rbac_svc_monitor_auth,
++        .n_auth = ARRAY_SIZE(rbac_svc_monitor_auth),
++        .insdel = false,
++        .update = rbac_svc_monitor_auth_update,
++        .n_update = ARRAY_SIZE(rbac_svc_monitor_auth_update),
++        .row = NULL
+     },{
+         .table = NULL,
+         .auth = NULL,
+diff --git a/tests/ovn.at b/tests/ovn.at
+index f68ddcd33..e2565f274 100644
+--- a/tests/ovn.at
++++ b/tests/ovn.at
+@@ -16932,5 +16932,124 @@ as hv4 ovs-ofctl show br-phys
+ as hv4 ovs-appctl fdb/show br-phys
+ 
+ OVN_CLEANUP([hv1],[hv2],[hv3],[hv4])
++AT_CLEANUP
++
++AT_SETUP([ovn -- Load balancer health checks])
++AT_KEYWORDS([lb])
++ovn_start
++
++net_add n1
++
++sim_add hv1
++as hv1
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.1
++ovs-vsctl -- add-port br-int hv1-vif1 -- \
++    set interface hv1-vif1 external-ids:iface-id=sw0-p1 \
++    options:tx_pcap=hv1/vif1-tx.pcap \
++    options:rxq_pcap=hv1/vif1-rx.pcap \
++    ofport-request=1
++ovs-vsctl -- add-port br-int hv1-vif2 -- \
++    set interface hv1-vif2 external-ids:iface-id=sw0-p2 \
++    options:tx_pcap=hv1/vif2-tx.pcap \
++    options:rxq_pcap=hv1/vif2-rx.pcap \
++    ofport-request=2
++
++sim_add hv2
++as hv2
++ovs-vsctl add-br br-phys
++ovn_attach n1 br-phys 192.168.0.2
++ovs-vsctl -- add-port br-int hv2-vif1 -- \
++    set interface hv2-vif1 external-ids:iface-id=sw1-p1 \
++    options:tx_pcap=hv2/vif1-tx.pcap \
++    options:rxq_pcap=hv2/vif1-rx.pcap \
++    ofport-request=1
++
++ovn-nbctl ls-add sw0
++
++ovn-nbctl lsp-add sw0 sw0-p1
++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
++ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
++
++ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
++ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
++
++# Create the second logical switch with one port
++ovn-nbctl ls-add sw1
++ovn-nbctl lsp-add sw1 sw1-p1
++ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
++ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
+ 
++# Create a logical router and attach both logical switches
++ovn-nbctl lr-add lr0
++ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
++ovn-nbctl lsp-add sw0 sw0-lr0
++ovn-nbctl lsp-set-type sw0-lr0 router
++ovn-nbctl lsp-set-addresses sw0-lr0 router
++ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24
++ovn-nbctl lsp-add sw1 sw1-lr0
++ovn-nbctl lsp-set-type sw1-lr0 router
++ovn-nbctl lsp-set-addresses sw1-lr0 router
++ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80
++
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
++
++ovn-nbctl --wait=sb -- --id=@hc create \
++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
++health_check @hc
++
++ovn-nbctl --wait=sb ls-lb-add sw0 lb1
++ovn-nbctl --wait=sb ls-lb-add sw1 lb1
++ovn-nbctl --wait=sb lr-lb-add lr0 lb1
++
++OVN_POPULATE_ARP
++ovn-nbctl --wait=hv sync
++
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
++service_monitor | sed '/^$/d' | wc -l`])
++
++ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
++])
++
++# get the svc monitor mac.
++svc_mon_src_mac=`ovn-nbctl get NB_Global . options:svc_monitor_mac | \
++sed s/":"//g | sed s/\"//g`
++
++OVS_WAIT_UNTIL(
++    [test 1 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
++grep "505400000003${svc_mon_src_mac}" | wc -l`]
++)
++
++OVS_WAIT_UNTIL(
++    [test 1 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \
++grep "405400000003${svc_mon_src_mac}" | wc -l`]
++)
++
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
++service_monitor | grep offline | wc -l`])
++
++OVS_WAIT_UNTIL(
++    [test 2 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
++grep "505400000003${svc_mon_src_mac}" | wc -l`]
++)
++
++OVS_WAIT_UNTIL(
++    [test 2 = `$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | \
++grep "405400000003${svc_mon_src_mac}" | wc -l`]
++)
++
++ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
++| grep priority=120 > lflows.txt
++AT_CHECK([cat lflows.txt], [0], [dnl
++  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;)
++])
++
++OVN_CLEANUP([hv1], [hv2])
+ AT_CLEANUP
+diff --git a/tests/system-common-macros.at b/tests/system-common-macros.at
+index 64bf5ec63..c8fa6f03f 100644
+--- a/tests/system-common-macros.at
++++ b/tests/system-common-macros.at
+@@ -267,6 +267,7 @@ m4_define([OVS_CHECK_FIREWALL],
+ #
+ m4_define([OVS_START_L7],
+    [PIDFILE=$(mktemp $2XXX.pid)
++    echo $PIDFILE > l7_pid_file
+     NETNS_DAEMONIZE([$1], [[$PYTHON $srcdir/test-l7.py $2]], [$PIDFILE])
+ 
+     dnl netstat doesn't print http over IPv6 as "http6"; drop the number.
+diff --git a/tests/system-ovn.at b/tests/system-ovn.at
+index b3f90aae2..7d1c65d85 100644
+--- a/tests/system-ovn.at
++++ b/tests/system-ovn.at
+@@ -2523,3 +2523,183 @@ as
+ OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
+ /connection dropped.*/d"])
+ AT_CLEANUP
++
++AT_SETUP([ovn -- Load balancer health checks])
++AT_KEYWORDS([lb])
++ovn_start
++
++OVS_TRAFFIC_VSWITCHD_START()
++ADD_BR([br-int])
++
++# Set external-ids in br-int needed for ovn-controller
++ovs-vsctl \
++        -- set Open_vSwitch . external-ids:system-id=hv1 \
++        -- set Open_vSwitch . external-ids:ovn-remote=unix:$ovs_base/ovn-sb/ovn-sb.sock \
++        -- set Open_vSwitch . external-ids:ovn-encap-type=geneve \
++        -- set Open_vSwitch . external-ids:ovn-encap-ip=169.0.0.1 \
++        -- set bridge br-int fail-mode=secure other-config:disable-in-band=true
++
++# Start ovn-controller
++start_daemon ovn-controller
++
++ovn-nbctl ls-add sw0
++
++ovn-nbctl lsp-add sw0 sw0-p1
++ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03 10.0.0.3"
++ovn-nbctl lsp-set-port-security sw0-p1 "50:54:00:00:00:03 10.0.0.3"
++
++ovn-nbctl lsp-add sw0 sw0-p2
++ovn-nbctl lsp-set-addresses sw0-p2 "50:54:00:00:00:04 10.0.0.4"
++ovn-nbctl lsp-set-port-security sw0-p2 "50:54:00:00:00:04 10.0.0.4"
++
++# Create the second logical switch with one port
++ovn-nbctl ls-add sw1
++ovn-nbctl lsp-add sw1 sw1-p1
++ovn-nbctl lsp-set-addresses sw1-p1 "40:54:00:00:00:03 20.0.0.3"
++ovn-nbctl lsp-set-port-security sw1-p1 "40:54:00:00:00:03 20.0.0.3"
++
++# Create a logical router and attach both logical switches
++ovn-nbctl lr-add lr0
++ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24
++ovn-nbctl lsp-add sw0 sw0-lr0
++ovn-nbctl lsp-set-type sw0-lr0 router
++ovn-nbctl lsp-set-addresses sw0-lr0 router
++ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
++
++ovn-nbctl lrp-add lr0 lr0-sw1 00:00:00:00:ff:02 20.0.0.1/24
++ovn-nbctl lsp-add sw1 sw1-lr0
++ovn-nbctl lsp-set-type sw1-lr0 router
++ovn-nbctl lsp-set-addresses sw1-lr0 router
++ovn-nbctl lsp-set-options sw1-lr0 router-port=lr0-sw1
++
++ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80
++
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
++ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
++
++ovn-nbctl --wait=sb -- --id=@hc create \
++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
++health_check @hc
++
++ovn-nbctl --wait=sb ls-lb-add sw0 lb1
++ovn-nbctl --wait=sb ls-lb-add sw1 lb1
++ovn-nbctl --wait=sb lr-lb-add lr0 lb1
++
++OVN_POPULATE_ARP
++ovn-nbctl --wait=hv sync
++
++ADD_NAMESPACES(sw0-p1)
++ADD_VETH(sw0-p1, sw0-p1, br-int, "10.0.0.3/24", "50:54:00:00:00:03", \
++         "10.0.0.1")
++
++ADD_NAMESPACES(sw1-p1)
++ADD_VETH(sw1-p1, sw1-p1, br-int, "20.0.0.3/24", "40:54:00:00:00:03", \
++         "20.0.0.1")
++
++ADD_NAMESPACES(sw0-p2)
++ADD_VETH(sw0-p2, sw0-p2, br-int, "10.0.0.4/24", "50:54:00:00:00:04", \
++         "10.0.0.1")
++
++# Wait until all the services are set to offline.
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
++service_monitor | sed '/^$/d' | grep offline | wc -l`])
++
++# Start webservers in 'sw0-p1' and 'sw1-p1'.
++OVS_START_L7([sw0-p1], [http])
++sw0_p1_pid_file=`cat l7_pid_file`
++OVS_START_L7([sw1-p1], [http])
++
++# Wait until the services are set to online.
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
++service_monitor | sed '/^$/d' | grep online | wc -l`])
++
++OVS_WAIT_UNTIL(
++    [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
++     test 1 = `cat lflows.txt | grep "ct_lb(10.0.0.3:80,20.0.0.3:80)" | wc -l`]
++)
++
++# From sw0-p2 send traffic to vip - 10.0.0.10
++for i in `seq 1 20`; do
++    echo Request $i
++    ovn-sbctl list service_monitor
++    NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
++done
++
++dnl Each server should have at least one connection.
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=10.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
++tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
++])
++
++# Stop webserer in sw0-p1
++kill `cat $sw0_p1_pid_file`
++
++# Wait until service_monitor for sw0-p1 is set to offline
++OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns status find \
++service_monitor logical_port=sw0-p1 | sed '/^$/d' | grep offline | wc -l`])
++
++OVS_WAIT_UNTIL(
++    [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
++     test 1 = `cat lflows.txt | grep "ct_lb(20.0.0.3:80)" | wc -l`]
++)
++
++ovs-appctl dpctl/flush-conntrack
++# From sw0-p2 send traffic to vip - 10.0.0.10
++for i in `seq 1 20`; do
++    echo Request $i
++    NS_CHECK_EXEC([sw0-p2], [wget 10.0.0.10 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
++done
++
++AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.0.0.10) | \
++sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
++tcp,orig=(src=10.0.0.4,dst=10.0.0.10,sport=<cleared>,dport=<cleared>),reply=(src=20.0.0.3,dst=10.0.0.4,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
++])
++
++# Create udp load balancer.
++ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp
++lb_udp=`ovn-nbctl lb-list | grep udp | awk '{print $1}'`
++
++echo "lb udp uuid = $lb_udp"
++
++ovn-nbctl list load_balancer
++
++ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
++ovn-nbctl --wait=sb set load_balancer $lb_udp ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
++
++ovn-nbctl --wait=sb -- --id=@hc create \
++Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer $lb_udp \
++health_check @hc
++
++ovn-nbctl --wait=sb ls-lb-add sw0 lb2
++ovn-nbctl --wait=sb ls-lb-add sw1 lb2
++ovn-nbctl --wait=sb lr-lb-add lr0 lb2
++
++sleep 10
++
++ovn-nbctl list load_balancer
++echo "*******Next is health check*******"
++ovn-nbctl list Load_Balancer_Health_Check
++echo "********************"
++ovn-sbctl list service_monitor
++
++# Wait until udp service_monitor are set to offline
++OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
++service_monitor protocol=udp | sed '/^$/d' | grep offline | wc -l`])
++
++OVS_APP_EXIT_AND_WAIT([ovn-controller])
++
++as ovn-sb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as ovn-nb
++OVS_APP_EXIT_AND_WAIT([ovsdb-server])
++
++as northd
++OVS_APP_EXIT_AND_WAIT([ovn-northd])
++
++as
++OVS_TRAFFIC_VSWITCHD_STOP(["/failed to query port patch-.*/d
++/connection dropped.*/d"])
++
++AT_CLEANUP
+-- 
+2.23.0
+
diff --git a/SOURCES/0012-northd-Remove-misleading-warning-log-message.patch b/SOURCES/0012-northd-Remove-misleading-warning-log-message.patch
new file mode 100644
index 0000000..c94e4d3
--- /dev/null
+++ b/SOURCES/0012-northd-Remove-misleading-warning-log-message.patch
@@ -0,0 +1,33 @@
+From 061eee986e8343df199c35d813ac14def610a92b Mon Sep 17 00:00:00 2001
+From: Russell Bryant <russell@ovn.org>
+Date: Sun, 1 Dec 2019 22:19:04 -0500
+Subject: [PATCH 12/12] northd: Remove misleading warning log message
+
+While debugging an ovn-kubernetes cluster, I spotted several
+"Duplicate MAC set" warning messages in the ovn-northd log.  It looks
+like this message was emitted from this code path by mistake, where
+it correctly avoided assigning a duplicate MAC address.  This patch
+turns off the warning for that case.
+
+Signed-off-by: Russell Bryant <russell@ovn.org>
+Acked-by: Numan Siddique <numans@ovn.org>
+---
+ ovn/northd/ovn-northd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
+index ec8f9a70f..55734b090 100644
+--- a/ovn/northd/ovn-northd.c
++++ b/ovn/northd/ovn-northd.c
+@@ -1394,7 +1394,7 @@ ipam_get_unused_mac(ovs_be32 ip)
+         mac_addr_suffix = ((base_addr + i) % (MAC_ADDR_SPACE - 1)) + 1;
+         mac64 =  eth_addr_to_uint64(mac_prefix) | mac_addr_suffix;
+         eth_addr_from_uint64(mac64, &mac);
+-        if (!ipam_is_duplicate_mac(&mac, mac64, true)) {
++        if (!ipam_is_duplicate_mac(&mac, mac64, false)) {
+             break;
+         }
+     }
+-- 
+2.23.0
+
diff --git a/SOURCES/arm64-armv8a-linuxapp-gcc-config b/SOURCES/arm64-armv8a-linuxapp-gcc-config
new file mode 100644
index 0000000..5813d7a
--- /dev/null
+++ b/SOURCES/arm64-armv8a-linuxapp-gcc-config
@@ -0,0 +1,540 @@
+# -*- cfg-sha: 9fc8b53ccd53cc8b64391f6252e1dba558ae660a73a72f10dcadff2ca5462243
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2015 Cavium, Inc
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Cavium, Inc
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2016 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2017 Intel Corporation
+# RTE_EXEC_ENV values are the directories in mk/exec-env/
+CONFIG_RTE_EXEC_ENV="linuxapp"
+# RTE_ARCH values are architecture we compile for. directories in mk/arch/
+CONFIG_RTE_ARCH="arm64"
+# machine can define specific variables or action for a specific board
+# RTE_MACHINE values are architecture we compile for. directories in mk/machine/
+CONFIG_RTE_MACHINE="armv8a"
+# The compiler we use.
+# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/
+CONFIG_RTE_TOOLCHAIN="gcc"
+# Use intrinsics or assembly code for key routines
+CONFIG_RTE_FORCE_INTRINSICS=y
+# Machine forces strict alignment constraints.
+CONFIG_RTE_ARCH_STRICT_ALIGN=n
+# Compile to share library
+CONFIG_RTE_BUILD_SHARED_LIB=n
+# Use newest code breaking previous ABI
+CONFIG_RTE_NEXT_ABI=n
+# Major ABI to overwrite library specific LIBABIVER
+CONFIG_RTE_MAJOR_ABI=
+# Machine's cache line size
+CONFIG_RTE_CACHE_LINE_SIZE=128
+# Memory model
+CONFIG_RTE_USE_C11_MEM_MODEL=y
+# Compile Environment Abstraction Layer
+CONFIG_RTE_LIBRTE_EAL=y
+CONFIG_RTE_MAX_LCORE=256
+CONFIG_RTE_MAX_NUMA_NODES=8
+CONFIG_RTE_MAX_HEAPS=32
+CONFIG_RTE_MAX_MEMSEG_LISTS=64
+# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages
+# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192
+CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768
+# a "type" is a combination of page size and NUMA node. total number of memseg
+# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split
+# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or
+# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of
+# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768
+CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072
+# global maximum usable amount of VA, in megabytes
+CONFIG_RTE_MAX_MEM_MB=524288
+CONFIG_RTE_MAX_MEMZONE=2560
+CONFIG_RTE_MAX_TAILQ=32
+CONFIG_RTE_ENABLE_ASSERT=n
+CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO
+CONFIG_RTE_LOG_HISTORY=256
+CONFIG_RTE_BACKTRACE=y
+CONFIG_RTE_LIBEAL_USE_HPET=n
+CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
+CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_EAL_IGB_UIO=n
+CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MAX_VFIO_GROUPS=64
+CONFIG_RTE_MAX_VFIO_CONTAINERS=64
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
+CONFIG_RTE_USE_LIBBSD=n
+# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing.
+# AVX512 is marked as experimental for now, will enable it after enough
+# field test and possible optimization.
+CONFIG_RTE_ENABLE_AVX=y
+CONFIG_RTE_ENABLE_AVX512=n
+# Default driver path (or "" to disable)
+CONFIG_RTE_EAL_PMD_PATH=""
+# Compile Environment Abstraction Layer to support Vmware TSC map
+CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=y
+# Compile architecture we compile for. PCI library
+CONFIG_RTE_LIBRTE_PCI=y
+# Compile architecture we compile for. argument parser library
+CONFIG_RTE_LIBRTE_KVARGS=y
+# Compile generic ethernet library
+CONFIG_RTE_LIBRTE_ETHER=y
+CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n
+CONFIG_RTE_MAX_ETHPORTS=32
+CONFIG_RTE_MAX_QUEUES_PER_PORT=1024
+CONFIG_RTE_LIBRTE_IEEE1588=n
+CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16
+CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
+CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n
+# Turn off Tx preparation stage
+# Warning: rte_eth_tx_prepare() can be safely disabled only if using a
+# driver which do not implement any Tx preparation.
+CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n
+# Common libraries, before Bus/PMDs
+CONFIG_RTE_LIBRTE_COMMON_DPAAX=n
+# Compile architecture we compile for. Intel FPGA bus
+CONFIG_RTE_LIBRTE_IFPGA_BUS=n
+# Compile PCI bus driver
+CONFIG_RTE_LIBRTE_PCI_BUS=y
+# Compile architecture we compile for. vdev bus
+CONFIG_RTE_LIBRTE_VDEV_BUS=y
+# Compile ARK PMD
+CONFIG_RTE_LIBRTE_ARK_PMD=n
+CONFIG_RTE_LIBRTE_ARK_PAD_TX=y
+CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n
+# Compile Aquantia Atlantic PMD driver
+CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n
+# Compile AMD PMD
+CONFIG_RTE_LIBRTE_AXGBE_PMD=n
+CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n
+# Compile burst-oriented Broadcom PMD driver
+CONFIG_RTE_LIBRTE_BNX2X_PMD=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n
+# Compile burst-oriented Broadcom BNXT PMD driver
+CONFIG_RTE_LIBRTE_BNXT_PMD=n
+# Compile burst-oriented Chelsio Terminator (CXGBE) PMD
+CONFIG_RTE_LIBRTE_CXGBE_PMD=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_CXGBE_TPUT=y
+# NXP DPAA Bus
+CONFIG_RTE_LIBRTE_DPAA_BUS=n
+CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA_PMD=n
+CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n
+# Compile NXP DPAA2 FSL-MC Bus
+CONFIG_RTE_LIBRTE_FSLMC_BUS=n
+# Compile Support Libraries for NXP DPAA2
+CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y
+# Compile burst-oriented NXP DPAA2 PMD driver
+CONFIG_RTE_LIBRTE_DPAA2_PMD=n
+CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n
+# Compile NXP ENETC PMD Driver
+CONFIG_RTE_LIBRTE_ENETC_PMD=n
+# Compile burst-oriented Amazon ENA PMD driver
+CONFIG_RTE_LIBRTE_ENA_PMD=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n
+# Compile burst-oriented Cisco ENIC PMD driver
+CONFIG_RTE_LIBRTE_ENIC_PMD=n
+# Compile burst-oriented IGB & EM PMD drivers
+CONFIG_RTE_LIBRTE_EM_PMD=n
+CONFIG_RTE_LIBRTE_IGB_PMD=y
+CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n
+# Compile burst-oriented IXGBE PMD driver
+CONFIG_RTE_LIBRTE_IXGBE_PMD=y
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n
+CONFIG_RTE_IXGBE_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n
+# Compile burst-oriented I40E PMD driver
+CONFIG_RTE_LIBRTE_I40E_PMD=y
+CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y
+CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4
+# Compile burst-oriented FM10K PMD
+CONFIG_RTE_LIBRTE_FM10K_PMD=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y
+CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y
+# Compile burst-oriented AVF PMD driver
+CONFIG_RTE_LIBRTE_AVF_PMD=n
+CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n
+# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD
+CONFIG_RTE_LIBRTE_MLX4_PMD=n
+CONFIG_RTE_LIBRTE_MLX4_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=n
+# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield
+# (MLX5) PMD
+CONFIG_RTE_LIBRTE_MLX5_PMD=n
+CONFIG_RTE_LIBRTE_MLX5_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=n
+# Compile burst-oriented Netronome NFP PMD driver
+CONFIG_RTE_LIBRTE_NFP_PMD=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n
+# QLogic 10G/25G/40G/50G/100G PMD
+CONFIG_RTE_LIBRTE_QEDE_PMD=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n
+#Provides abs path/name of architecture we compile for. firmware file.
+#Empty string denotes driver will use default firmware
+CONFIG_RTE_LIBRTE_QEDE_FW=""
+# Compile burst-oriented Solarflare libefx-based PMD
+CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n
+CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n
+# Compile software PMD backed by SZEDATA2 device
+CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n
+# Compile burst-oriented Cavium Thunderx NICVF PMD driver
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n
+# Compile burst-oriented Cavium LiquidIO PMD driver
+CONFIG_RTE_LIBRTE_LIO_PMD=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n
+# Compile burst-oriented Cavium OCTEONTX network PMD driver
+CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n
+# Compile WRS accelerated virtual port (AVP) guest PMD driver
+CONFIG_RTE_LIBRTE_AVP_PMD=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n
+# Compile burst-oriented VIRTIO PMD driver
+CONFIG_RTE_LIBRTE_VIRTIO_PMD=y
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n
+# Compile virtio device emulation inside virtio PMD driver
+CONFIG_RTE_VIRTIO_USER=n
+# Compile burst-oriented VMXNET3 PMD driver
+CONFIG_RTE_LIBRTE_VMXNET3_PMD=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n
+# Compile software PMD backed by AF_PACKET sockets (Linux only)
+CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n
+# Compile link bonding PMD library
+CONFIG_RTE_LIBRTE_PMD_BOND=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n
+# Compile fail-safe PMD
+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y
+# Compile Marvell PMD driver
+CONFIG_RTE_LIBRTE_MVPP2_PMD=n
+# Compile Marvell MVNETA PMD driver
+CONFIG_RTE_LIBRTE_MVNETA_PMD=n
+# Compile support for VMBus library
+CONFIG_RTE_LIBRTE_VMBUS=n
+# Compile native PMD for Hyper-V/Azure
+CONFIG_RTE_LIBRTE_NETVSC_PMD=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n
+# Compile virtual device driver for NetVSC on Hyper-V/Azure
+CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n
+# Compile null PMD
+CONFIG_RTE_LIBRTE_PMD_NULL=n
+# Compile software PMD backed by PCAP files
+CONFIG_RTE_LIBRTE_PMD_PCAP=n
+# Compile example software rings based PMD
+CONFIG_RTE_LIBRTE_PMD_RING=y
+CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16
+CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16
+# Compile SOFTNIC PMD
+CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n
+# Compile architecture we compile for. TAP PMD
+# It is enabled by default for Linux only.
+CONFIG_RTE_LIBRTE_PMD_TAP=y
+# Do prefetch of packet data within PMD driver receive function
+CONFIG_RTE_PMD_PACKET_PREFETCH=y
+# Compile generic wireless base band device library
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_BBDEV=n
+CONFIG_RTE_BBDEV_MAX_DEVS=128
+CONFIG_RTE_BBDEV_OFFLOAD_COST=n
+# Compile PMD for NULL bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y
+# Compile PMD for turbo software bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n
+# Compile generic crypto device library
+CONFIG_RTE_LIBRTE_CRYPTODEV=n
+CONFIG_RTE_CRYPTO_MAX_DEVS=64
+# Compile PMD for ARMv8 Crypto device
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n
+# Compile NXP CAAM JR crypto Driver
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n
+# Compile NXP DPAA2 crypto sec driver for CAAM HW
+CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n
+# NXP DPAA caam - crypto driver
+CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n
+CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4
+# Compile PMD for Cavium OCTEON TX crypto device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y
+# Compile PMD for QuickAssist based devices - see docs for details
+CONFIG_RTE_LIBRTE_PMD_QAT=n
+CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n
+# Max. number of QuickAssist devices, which can be detected and attached
+CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48
+CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16
+CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536
+# Compile PMD for virtio crypto devices
+CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n
+# Number of maximum virtio crypto devices
+CONFIG_RTE_MAX_VIRTIO_CRYPTO=32
+# Compile PMD for AESNI backed device
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n
+# Compile PMD for Software backed device
+CONFIG_RTE_LIBRTE_PMD_OPENSSL=n
+# Compile PMD for AESNI GCM device
+CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n
+# Compile PMD for SNOW 3G device
+CONFIG_RTE_LIBRTE_PMD_SNOW3G=n
+CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n
+# Compile PMD for KASUMI device
+CONFIG_RTE_LIBRTE_PMD_KASUMI=n
+# Compile PMD for ZUC device
+CONFIG_RTE_LIBRTE_PMD_ZUC=n
+# Compile PMD for Crypto Scheduler device
+CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n
+# Compile PMD for NULL Crypto device
+CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n
+# Compile PMD for AMD CCP crypto device
+CONFIG_RTE_LIBRTE_PMD_CCP=n
+# Compile PMD for Marvell Crypto device
+CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n
+# Compile generic security library
+CONFIG_RTE_LIBRTE_SECURITY=n
+# Compile generic compression device library
+CONFIG_RTE_LIBRTE_COMPRESSDEV=n
+CONFIG_RTE_COMPRESS_MAX_DEVS=64
+# Compile compressdev unit test
+CONFIG_RTE_COMPRESSDEV_TEST=n
+# Compile PMD for Octeontx ZIPVF compression device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n
+# Compile PMD for ISA-L compression device
+CONFIG_RTE_LIBRTE_PMD_ISAL=n
+# Compile PMD for ZLIB compression device
+CONFIG_RTE_LIBRTE_PMD_ZLIB=n
+# Compile generic event device library
+CONFIG_RTE_LIBRTE_EVENTDEV=n
+CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n
+CONFIG_RTE_EVENT_MAX_DEVS=16
+CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64
+CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32
+CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024
+CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32
+CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32
+# Compile PMD for skeleton event device
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n
+# Compile PMD for software event device
+CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n
+# Compile PMD for distributed software event device
+CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n
+# Compile PMD for octeontx sso event device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n
+# Compile PMD for OPDL event device
+CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n
+# Compile PMD for NXP DPAA event device
+CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n
+# Compile PMD for NXP DPAA2 event device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n
+# Compile raw device support
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_RAWDEV=n
+CONFIG_RTE_RAWDEV_MAX_DEVS=10
+CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n
+# Compile PMD for NXP DPAA2 CMDIF raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n
+# Compile PMD for NXP DPAA2 QDMA raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n
+# Compile PMD for Intel FPGA raw device
+CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n
+# Compile librte_ring
+CONFIG_RTE_LIBRTE_RING=y
+# Compile librte_mempool
+CONFIG_RTE_LIBRTE_MEMPOOL=y
+CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512
+CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
+# Compile Mempool drivers
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64
+CONFIG_RTE_DRIVER_MEMPOOL_RING=y
+CONFIG_RTE_DRIVER_MEMPOOL_STACK=y
+# Compile PMD for octeontx fpa mempool device
+CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n
+# Compile librte_mbuf
+CONFIG_RTE_LIBRTE_MBUF=y
+CONFIG_RTE_LIBRTE_MBUF_DEBUG=n
+CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
+CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
+CONFIG_RTE_PKTMBUF_HEADROOM=128
+# Compile librte_timer
+CONFIG_RTE_LIBRTE_TIMER=n
+CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
+# Compile librte_cfgfile
+CONFIG_RTE_LIBRTE_CFGFILE=n
+# Compile librte_cmdline
+CONFIG_RTE_LIBRTE_CMDLINE=y
+CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
+# Compile librte_hash
+CONFIG_RTE_LIBRTE_HASH=y
+CONFIG_RTE_LIBRTE_HASH_DEBUG=n
+# Compile librte_efd
+CONFIG_RTE_LIBRTE_EFD=n
+# Compile librte_member
+CONFIG_RTE_LIBRTE_MEMBER=y
+# Compile librte_jobstats
+CONFIG_RTE_LIBRTE_JOBSTATS=n
+# Compile architecture we compile for. device metrics library
+CONFIG_RTE_LIBRTE_METRICS=y
+# Compile architecture we compile for. bitrate statistics library
+CONFIG_RTE_LIBRTE_BITRATE=y
+# Compile architecture we compile for. latency statistics library
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+# Compile librte_telemetry
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+# Compile librte_lpm
+CONFIG_RTE_LIBRTE_LPM=n
+CONFIG_RTE_LIBRTE_LPM_DEBUG=n
+# Compile librte_acl
+CONFIG_RTE_LIBRTE_ACL=n
+CONFIG_RTE_LIBRTE_ACL_DEBUG=n
+# Compile librte_power
+CONFIG_RTE_LIBRTE_POWER=n
+CONFIG_RTE_LIBRTE_POWER_DEBUG=n
+CONFIG_RTE_MAX_LCORE_FREQS=64
+# Compile librte_net
+CONFIG_RTE_LIBRTE_NET=y
+# Compile librte_ip_frag
+CONFIG_RTE_LIBRTE_IP_FRAG=y
+CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n
+CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4
+CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n
+# Compile GRO library
+CONFIG_RTE_LIBRTE_GRO=y
+# Compile GSO library
+CONFIG_RTE_LIBRTE_GSO=y
+# Compile librte_meter
+CONFIG_RTE_LIBRTE_METER=y
+# Compile librte_classify
+CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n
+# Compile librte_sched
+CONFIG_RTE_LIBRTE_SCHED=n
+CONFIG_RTE_SCHED_DEBUG=n
+CONFIG_RTE_SCHED_RED=n
+CONFIG_RTE_SCHED_COLLECT_STATS=n
+CONFIG_RTE_SCHED_SUBPORT_TC_OV=n
+CONFIG_RTE_SCHED_PORT_N_GRINDERS=8
+CONFIG_RTE_SCHED_VECTOR=n
+# Compile architecture we compile for. distributor library
+CONFIG_RTE_LIBRTE_DISTRIBUTOR=n
+# Compile architecture we compile for. reorder library
+CONFIG_RTE_LIBRTE_REORDER=n
+# Compile librte_port
+CONFIG_RTE_LIBRTE_PORT=n
+CONFIG_RTE_PORT_STATS_COLLECT=n
+CONFIG_RTE_PORT_PCAP=n
+# Compile librte_table
+CONFIG_RTE_LIBRTE_TABLE=n
+CONFIG_RTE_TABLE_STATS_COLLECT=n
+# Compile librte_pipeline
+CONFIG_RTE_LIBRTE_PIPELINE=n
+CONFIG_RTE_PIPELINE_STATS_COLLECT=n
+# Compile librte_kni
+CONFIG_RTE_LIBRTE_KNI=n
+CONFIG_RTE_LIBRTE_PMD_KNI=n
+CONFIG_RTE_KNI_KMOD=n
+CONFIG_RTE_KNI_KMOD_ETHTOOL=n
+CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
+# Compile architecture we compile for. pdump library
+CONFIG_RTE_LIBRTE_PDUMP=y
+# Compile vhost user library
+CONFIG_RTE_LIBRTE_VHOST=y
+CONFIG_RTE_LIBRTE_VHOST_NUMA=y
+CONFIG_RTE_LIBRTE_VHOST_DEBUG=n
+# Compile vhost PMD
+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.
+CONFIG_RTE_LIBRTE_PMD_VHOST=y
+# Compile IFC driver
+# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO
+# should be enabled.
+CONFIG_RTE_LIBRTE_IFC_PMD=n
+# Compile librte_bpf
+CONFIG_RTE_LIBRTE_BPF=n
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
+# Compile architecture we compile for. test application
+CONFIG_RTE_APP_TEST=y
+CONFIG_RTE_APP_TEST_RESOURCE_TAR=n
+# Compile architecture we compile for. procinfo application
+CONFIG_RTE_PROC_INFO=y
+# Compile architecture we compile for. PMD test application
+CONFIG_RTE_TEST_PMD=n
+CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
+CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+# Compile architecture we compile for. bbdev test application
+CONFIG_RTE_TEST_BBDEV=n
+# Compile architecture we compile for. crypto performance application
+CONFIG_RTE_APP_CRYPTO_PERF=n
+# Compile architecture we compile for. eventdev application
+CONFIG_RTE_APP_EVENTDEV=n
+CONFIG_RTE_EXEC_ENV_LINUXAPP=y
+CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n
+# Common libraries, before Bus/PMDs
+# NXP DPAA BUS and drivers
+# NXP FSLMC BUS and DPAA2 drivers
+# NXP ENETC PMD Driver
+CONFIG_RTE_ARCH_ARM64=y
+CONFIG_RTE_ARCH_64=y
+# Maximum available cache line size in arm64 implementations.
+# Setting to maximum available cache line size in generic config
+# to address minimum DMA alignment across all arm64 implementations.
+# Accelarate rte_memcpy. Be sure to run unit test (memcpy_perf_autotest)
+# to determine architecture we compile for. best threshold in code. Refer to notes in source file
+# (lib/librte_eal/common/include/arch/arm/rte_memcpy_64.h) for more info.
+CONFIG_RTE_ARCH_ARM64_MEMCPY=n
+#CONFIG_RTE_ARM64_MEMCPY_ALIGNED_THRESHOLD=2048
+#CONFIG_RTE_ARM64_MEMCPY_UNALIGNED_THRESHOLD=512
+# Leave below RTE_ARM64_MEMCPY_xxx options commented out, unless there're
+# strong reasons.
+#CONFIG_RTE_ARM64_MEMCPY_SKIP_GCC_VER_CHECK=n
+#CONFIG_RTE_ARM64_MEMCPY_ALIGN_MASK=0xF
+#CONFIG_RTE_ARM64_MEMCPY_STRICT_ALIGN=n
+CONFIG_RTE_TOOLCHAIN_GCC=y
+CONFIG_RTE_LIBRTE_PMD_XENVIRT=n
diff --git a/SOURCES/configlib.sh b/SOURCES/configlib.sh
new file mode 100644
index 0000000..a1049b3
--- /dev/null
+++ b/SOURCES/configlib.sh
@@ -0,0 +1,105 @@
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# Core configuration file library.
+
+# Configurations are determined by sha values.  The way to determine is by
+# the special text:
+# $FILE_COMMENT_TYPE -*- cfg-sha: $SHA256 -*-
+
+export LC_ALL=C
+
+# check required binaries
+__check_reqd_binaries() {
+    local BIN __binaries=("egrep" "sort" "sha256sum" "sed")
+    for BIN in $__binaries; do
+        if ! type -P $BIN >/dev/null 2>&1; then
+            echo "Binary $BIN not found.  Please install."
+            exit 1
+        fi
+    done
+}
+
+# Calculates a sha from a file
+# The algorithm for generating a sha from a config is thus:
+#
+# 1. Remove all comment lines and blank lines
+# 2. Sort the content
+# 3. generate the sha-256 sum
+#
+# From a script perspective, this means:
+#   egrep -v ^\# %file% | egrep -v ^$ | sort -u | sha256sum
+#
+# Params:
+#  $1 = output variable
+#  $2 = file to use to calculate the shasum
+#  $3 = file comment type (defaults to # if unspecified)
+calc_sha() {
+    __check_reqd_binaries
+
+    if [ "$1" == "" ]; then
+        echo "Please pass in a storage variable."
+        return 1
+    fi
+
+    local __resultvar=$1
+    __retval=1
+    shift
+
+    local __file=$1
+    local cmnt=${2:-#}
+
+    if [ -f "$__file" ]; then
+        local __shasum=$(egrep -v ^"$cmnt" "$__file" | egrep -v ^$ | sort -u | sha256sum -t | cut -d" " -f1)
+        eval $__resultvar="'$__shasum'"
+        __retval=0
+    fi
+    return $__retval
+}
+
+# Retrieves a sha stored in a file
+# Param:
+#  $1 = output variable
+#  $2 = file to use to calculate the shasum
+#  $3 = file comment type (defaults to # if unspecified)
+retr_sha() {
+    __check_reqd_binaries
+
+    if [ "$1" == "" ]; then
+        echo "Please pass in a storage variable."
+        return 1
+    fi
+
+    local __resultvar=$1
+    __retval=1
+    shift
+
+    local __file=$1
+    local cmnt=${2:-#}
+
+    if [ -f "$__file" ]; then
+        if grep -q "$cmnt -\*- cfg-sha:" "$__file"; then
+            local __shasum=$(grep "$cmnt -\*- cfg-sha:" "$__file" | sed -e "s@$cmnt -\*- cfg-sha: @@" | cut -d" " -f1)
+            eval $__resultvar="'$__shasum'"
+            __retval=0
+        fi
+    fi
+    return $__retval
+}
+
+
+# Set a config value
+# set_conf dpdk_build_tree parameter value
+# dpdk_build_tree is the directory where the .config lives
+# parameter is the config parameter
+# value is the value to set for the config parameter
+set_conf() {
+    c="$1/.config"
+    shift
+
+    if grep -q "$1" "$c"; then
+        sed -i "s:^$1=.*$:$1=$2:g" $c
+    else
+        echo $1=$2 >> "$c"
+    fi
+}
+
diff --git a/SOURCES/gen_config_group.sh b/SOURCES/gen_config_group.sh
new file mode 100755
index 0000000..651a0c5
--- /dev/null
+++ b/SOURCES/gen_config_group.sh
@@ -0,0 +1,216 @@
+#!/bin/bash
+
+source configlib.sh
+
+# Generates arch configurations in the current directory based on
+# 1. an openvswitch.spec file
+# 2. an expanded dpdk tree
+
+if (( $# != 2 )); then
+    echo "$0: openvswitch.spec dpdk_tree" >&2
+    exit 1
+fi
+
+OVSSPEC="$1"
+DPDKDIR="$2"
+
+# accumulate all arch + name triples
+OVS_DPDK_CONF_MACH_ARCH=()
+for arch in $(grep %define\ dpdk_mach_arch "$OVSSPEC" | sed 's@%define dpdk_mach_arch @@')
+do
+    OVS_DPDK_CONF_MACH_ARCH+=($arch)
+done
+
+OVS_DPDK_CONF_MACH_TMPL=()
+for tmpl in $(grep %define\ dpdk_mach_tmpl "$OVSSPEC" | sed 's@%define dpdk_mach_tmpl @@')
+do
+    OVS_DPDK_CONF_MACH_TMPL+=($tmpl)
+done
+
+OVS_DPDK_CONF_MACH=()
+for mach in $(grep %define\ dpdk_mach\  "$OVSSPEC" | sed 's@%define dpdk_mach @@')
+do
+    OVS_DPDK_CONF_MACH+=($mach)
+done
+
+OVS_DPDK_TARGETS=()
+for ((i=0; i < ${#OVS_DPDK_CONF_MACH[@]}; i++));
+do
+    OVS_DPDK_TARGETS+=("${OVS_DPDK_CONF_MACH_ARCH[$i]}-${OVS_DPDK_CONF_MACH_TMPL[$i]}-linuxapp-gcc")
+    echo "DPDK-target: ${OVS_DPDK_TARGETS[$i]}"
+done
+
+OUTPUT_DIR=$(pwd)
+pushd "$DPDKDIR"
+for ((i=0; i < ${#OVS_DPDK_TARGETS[@]}; i++));
+do
+    echo "For ${OVS_DPDK_TARGETS[$i]}:"
+
+    echo "     a. Generating initial config"
+    echo "        make V=1 T=${OVS_DPDK_TARGETS[$i]} O=${OVS_DPDK_TARGETS[$i]}"
+    make V=1 T=${OVS_DPDK_TARGETS[$i]} O=${OVS_DPDK_TARGETS[$i]} -j8 config
+    ORIG_SHA=""
+    OUTDIR="${OVS_DPDK_TARGETS[$i]}"
+
+    echo "     b. calculating and applying sha"
+    calc_sha ORIG_SHA "${OUTDIR}/.config"
+    if [ "$ORIG_SHA" == "" ]; then
+        echo "ERROR: Unable to get sha for arch ${OVS_DPDK_TARGETS[$i]}"
+        exit 1
+    fi
+    echo "# -*- cfg-sha: ${ORIG_SHA}" > ${OUTDIR}/.config.new
+    cat "${OUTDIR}/.config" >> "${OUTDIR}/.config.new"
+    cp "${OUTDIR}/.config" "${OUTDIR}/.config.orig"
+    mv -f "${OUTDIR}/.config.new" "${OUTDIR}/.config"
+
+    echo "     c. setting initial configurations"
+    # these are the original setconf values from openvswitch.spec
+    set_conf "${OUTDIR}" CONFIG_RTE_MACHINE "\\\"${OVS_DPDK_CONF_MACH[$i]}\\\""
+
+    # Disable DPDK libraries not needed
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_TIMER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CFGFILE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_JOBSTATS n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_LPM n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_ACL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_POWER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_SCHED n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DISTRIBUTOR n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_REORDER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PORT n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_TABLE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PIPELINE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_KNI n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CRYPTODEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_SECURITY n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_FLOW_CLASSIFY n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BBDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_COMPRESSDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BPF n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DPAA_MEMPOOL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_CFGFILE n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_EFD n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_FLOW_CLASSIFY n
+
+    # Disable all eventdevs
+    for eventdev in $(grep _EVENTDEV= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $eventdev n
+    done
+
+    # Disable all rawdevs
+    for rawdev in $(grep _RAWDEV= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $rawdev n
+    done
+
+    # Disable virtio user
+    set_conf "${OUTDIR}" CONFIG_RTE_VIRTIO_USER n
+
+    # Enable vhost numa as libnuma dep is ok
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VHOST_NUMA y
+
+    # start by disabling ALL PMDs
+    for pmd in $(grep _PMD= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $pmd n
+    done
+
+    # PMDs which have their own naming scheme
+    # the default for this was 'n' at one point.  Make sure we keep it
+    # as such
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_QAT n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VHOST n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_KNI n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_XENVIRT n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_NULL n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_PCAP n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_BOND n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_AF_PACKET n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_SOFTNIC n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_DPAA_SEC n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_COMMON_DPAAX n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CAAM_JR n
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE n
+
+    # whitelist of enabled PMDs
+    # Soft PMDs to enable
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_RING y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_VHOST y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VIRTIO_PMD y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_TAP y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PMD_FAILSAFE y
+
+
+    # start by disabling all buses
+    for bus in $(grep _BUS= "${OUTDIR}/.config" | sed 's@=\(y\|n\)@@g')
+    do
+        set_conf "${OUTDIR}" $bus n
+    done
+
+    # blacklist buses that don't conform to std naming
+    # May override VMBUS later in arch specific section
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VMBUS n
+
+    # whitelist buses
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_PCI_BUS y
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VDEV_BUS y
+
+
+    # Disable some other miscellanous items related to test apps
+    set_conf "${OUTDIR}" CONFIG_RTE_TEST_BBDEV n
+    set_conf "${OUTDIR}" CONFIG_RTE_APP_CRYPTO_PERF n
+
+    # Disable kernel modules
+    set_conf "${OUTDIR}" CONFIG_RTE_EAL_IGB_UIO n
+    set_conf "${OUTDIR}" CONFIG_RTE_KNI_KMOD n
+
+    # Disable experimental stuff
+    set_conf "${OUTDIR}" CONFIG_RTE_NEXT_ABI n
+
+    # Arch specific
+    set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_I40E_PMD y
+    case "${OVS_DPDK_CONF_MACH_ARCH[i]}" in
+    x86_64)
+        # Hw PMD
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_BNXT_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_ENIC_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX4_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX5_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_NFP_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_QEDE_PMD y
+        # Sw PMD
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_NETVSC_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD y
+        # Bus
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_VMBUS y
+        ;&
+    arm64)
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_IXGBE_PMD y
+        set_conf "${OUTDIR}" CONFIG_RTE_LIBRTE_IGB_PMD y
+        ;;
+    esac
+
+    cp "${OUTDIR}/.config" "${OUTPUT_DIR}/${OVS_DPDK_TARGETS[$i]}-config"
+done
+popd >/dev/null
+
+echo -n "For each arch ( "
+for ((i=0; i < ${#OVS_DPDK_CONF_MACH_ARCH[@]}; i++));
+do
+    echo -n "${OVS_DPDK_CONF_MACH_ARCH[i]} "
+done
+echo "):"
+echo "1. ensure you enable the requisite hw"
diff --git a/SOURCES/ppc_64-power8-linuxapp-gcc-config b/SOURCES/ppc_64-power8-linuxapp-gcc-config
new file mode 100644
index 0000000..2319b68
--- /dev/null
+++ b/SOURCES/ppc_64-power8-linuxapp-gcc-config
@@ -0,0 +1,550 @@
+# -*- cfg-sha: ac783e64ca20c977a7c1c42e72e6dce151b31aa9aecfbfa121b45e49e938f418
+# BSD LICENSE
+# Copyright (C) IBM Corporation 2014.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of IBM Corporation nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2016 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2017 Intel Corporation
+# RTE_EXEC_ENV values are the directories in mk/exec-env/
+CONFIG_RTE_EXEC_ENV="linuxapp"
+# RTE_ARCH values are architecture we compile for. directories in mk/arch/
+CONFIG_RTE_ARCH="ppc_64"
+# machine can define specific variables or action for a specific board
+# RTE_MACHINE values are architecture we compile for. directories in mk/machine/
+CONFIG_RTE_MACHINE="power8"
+# The compiler we use.
+# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/
+CONFIG_RTE_TOOLCHAIN="gcc"
+# Use intrinsics or assembly code for key routines
+CONFIG_RTE_FORCE_INTRINSICS=n
+# Machine forces strict alignment constraints.
+CONFIG_RTE_ARCH_STRICT_ALIGN=n
+# Compile to share library
+CONFIG_RTE_BUILD_SHARED_LIB=n
+# Use newest code breaking previous ABI
+CONFIG_RTE_NEXT_ABI=n
+# Major ABI to overwrite library specific LIBABIVER
+CONFIG_RTE_MAJOR_ABI=
+# Machine's cache line size
+CONFIG_RTE_CACHE_LINE_SIZE=128
+# Memory model
+CONFIG_RTE_USE_C11_MEM_MODEL=n
+# Compile Environment Abstraction Layer
+CONFIG_RTE_LIBRTE_EAL=y
+CONFIG_RTE_MAX_LCORE=256
+CONFIG_RTE_MAX_NUMA_NODES=32
+CONFIG_RTE_MAX_HEAPS=32
+CONFIG_RTE_MAX_MEMSEG_LISTS=64
+# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages
+# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192
+CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768
+# a "type" is a combination of page size and NUMA node. total number of memseg
+# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split
+# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or
+# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of
+# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768
+CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072
+# global maximum usable amount of VA, in megabytes
+CONFIG_RTE_MAX_MEM_MB=524288
+CONFIG_RTE_MAX_MEMZONE=2560
+CONFIG_RTE_MAX_TAILQ=32
+CONFIG_RTE_ENABLE_ASSERT=n
+CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO
+CONFIG_RTE_LOG_HISTORY=256
+CONFIG_RTE_BACKTRACE=y
+CONFIG_RTE_LIBEAL_USE_HPET=n
+CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
+CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_EAL_IGB_UIO=n
+CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MAX_VFIO_GROUPS=64
+CONFIG_RTE_MAX_VFIO_CONTAINERS=64
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
+CONFIG_RTE_USE_LIBBSD=n
+# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing.
+# AVX512 is marked as experimental for now, will enable it after enough
+# field test and possible optimization.
+CONFIG_RTE_ENABLE_AVX=y
+CONFIG_RTE_ENABLE_AVX512=n
+# Default driver path (or "" to disable)
+CONFIG_RTE_EAL_PMD_PATH=""
+# Compile Environment Abstraction Layer to support Vmware TSC map
+CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=n
+# Compile architecture we compile for. PCI library
+CONFIG_RTE_LIBRTE_PCI=y
+# Compile architecture we compile for. argument parser library
+CONFIG_RTE_LIBRTE_KVARGS=y
+# Compile generic ethernet library
+CONFIG_RTE_LIBRTE_ETHER=y
+CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n
+CONFIG_RTE_MAX_ETHPORTS=32
+CONFIG_RTE_MAX_QUEUES_PER_PORT=1024
+CONFIG_RTE_LIBRTE_IEEE1588=n
+CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16
+CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
+CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n
+# Turn off Tx preparation stage
+# Warning: rte_eth_tx_prepare() can be safely disabled only if using a
+# driver which do not implement any Tx preparation.
+CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n
+# Common libraries, before Bus/PMDs
+CONFIG_RTE_LIBRTE_COMMON_DPAAX=n
+# Compile architecture we compile for. Intel FPGA bus
+CONFIG_RTE_LIBRTE_IFPGA_BUS=n
+# Compile PCI bus driver
+CONFIG_RTE_LIBRTE_PCI_BUS=y
+# Compile architecture we compile for. vdev bus
+CONFIG_RTE_LIBRTE_VDEV_BUS=y
+# Compile ARK PMD
+CONFIG_RTE_LIBRTE_ARK_PMD=n
+CONFIG_RTE_LIBRTE_ARK_PAD_TX=y
+CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n
+# Compile Aquantia Atlantic PMD driver
+CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n
+# Compile AMD PMD
+CONFIG_RTE_LIBRTE_AXGBE_PMD=n
+CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n
+# Compile burst-oriented Broadcom PMD driver
+CONFIG_RTE_LIBRTE_BNX2X_PMD=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n
+# Compile burst-oriented Broadcom BNXT PMD driver
+CONFIG_RTE_LIBRTE_BNXT_PMD=n
+# Compile burst-oriented Chelsio Terminator (CXGBE) PMD
+CONFIG_RTE_LIBRTE_CXGBE_PMD=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_CXGBE_TPUT=y
+# NXP DPAA Bus
+CONFIG_RTE_LIBRTE_DPAA_BUS=n
+CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA_PMD=n
+CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n
+# Compile NXP DPAA2 FSL-MC Bus
+CONFIG_RTE_LIBRTE_FSLMC_BUS=n
+# Compile Support Libraries for NXP DPAA2
+CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y
+# Compile burst-oriented NXP DPAA2 PMD driver
+CONFIG_RTE_LIBRTE_DPAA2_PMD=n
+CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n
+# Compile NXP ENETC PMD Driver
+CONFIG_RTE_LIBRTE_ENETC_PMD=n
+# Compile burst-oriented Amazon ENA PMD driver
+CONFIG_RTE_LIBRTE_ENA_PMD=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n
+# Compile burst-oriented Cisco ENIC PMD driver
+CONFIG_RTE_LIBRTE_ENIC_PMD=n
+# Compile burst-oriented IGB & EM PMD drivers
+CONFIG_RTE_LIBRTE_EM_PMD=n
+CONFIG_RTE_LIBRTE_IGB_PMD=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n
+# Compile burst-oriented IXGBE PMD driver
+CONFIG_RTE_LIBRTE_IXGBE_PMD=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n
+CONFIG_RTE_IXGBE_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n
+# Compile burst-oriented I40E PMD driver
+CONFIG_RTE_LIBRTE_I40E_PMD=y
+CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y
+CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4
+# Compile burst-oriented FM10K PMD
+CONFIG_RTE_LIBRTE_FM10K_PMD=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y
+CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y
+# Compile burst-oriented AVF PMD driver
+CONFIG_RTE_LIBRTE_AVF_PMD=n
+CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n
+# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD
+CONFIG_RTE_LIBRTE_MLX4_PMD=n
+CONFIG_RTE_LIBRTE_MLX4_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=n
+# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield
+# (MLX5) PMD
+CONFIG_RTE_LIBRTE_MLX5_PMD=n
+CONFIG_RTE_LIBRTE_MLX5_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=n
+# Compile burst-oriented Netronome NFP PMD driver
+CONFIG_RTE_LIBRTE_NFP_PMD=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n
+# QLogic 10G/25G/40G/50G/100G PMD
+CONFIG_RTE_LIBRTE_QEDE_PMD=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n
+#Provides abs path/name of architecture we compile for. firmware file.
+#Empty string denotes driver will use default firmware
+CONFIG_RTE_LIBRTE_QEDE_FW=""
+# Compile burst-oriented Solarflare libefx-based PMD
+CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n
+CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n
+# Compile software PMD backed by SZEDATA2 device
+CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n
+# Compile burst-oriented Cavium Thunderx NICVF PMD driver
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n
+# Compile burst-oriented Cavium LiquidIO PMD driver
+CONFIG_RTE_LIBRTE_LIO_PMD=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n
+# Compile burst-oriented Cavium OCTEONTX network PMD driver
+CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n
+# Compile WRS accelerated virtual port (AVP) guest PMD driver
+CONFIG_RTE_LIBRTE_AVP_PMD=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n
+# Compile burst-oriented VIRTIO PMD driver
+CONFIG_RTE_LIBRTE_VIRTIO_PMD=y
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n
+# Compile virtio device emulation inside virtio PMD driver
+CONFIG_RTE_VIRTIO_USER=n
+# Compile burst-oriented VMXNET3 PMD driver
+CONFIG_RTE_LIBRTE_VMXNET3_PMD=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n
+# Compile software PMD backed by AF_PACKET sockets (Linux only)
+CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n
+# Compile link bonding PMD library
+CONFIG_RTE_LIBRTE_PMD_BOND=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n
+# Compile fail-safe PMD
+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y
+# Compile Marvell PMD driver
+CONFIG_RTE_LIBRTE_MVPP2_PMD=n
+# Compile Marvell MVNETA PMD driver
+CONFIG_RTE_LIBRTE_MVNETA_PMD=n
+# Compile support for VMBus library
+CONFIG_RTE_LIBRTE_VMBUS=n
+# Compile native PMD for Hyper-V/Azure
+CONFIG_RTE_LIBRTE_NETVSC_PMD=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n
+# Compile virtual device driver for NetVSC on Hyper-V/Azure
+CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=n
+# Compile null PMD
+CONFIG_RTE_LIBRTE_PMD_NULL=n
+# Compile software PMD backed by PCAP files
+CONFIG_RTE_LIBRTE_PMD_PCAP=n
+# Compile example software rings based PMD
+CONFIG_RTE_LIBRTE_PMD_RING=y
+CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16
+CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16
+# Compile SOFTNIC PMD
+CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n
+# Compile architecture we compile for. TAP PMD
+# It is enabled by default for Linux only.
+CONFIG_RTE_LIBRTE_PMD_TAP=y
+# Do prefetch of packet data within PMD driver receive function
+CONFIG_RTE_PMD_PACKET_PREFETCH=y
+# Compile generic wireless base band device library
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_BBDEV=n
+CONFIG_RTE_BBDEV_MAX_DEVS=128
+CONFIG_RTE_BBDEV_OFFLOAD_COST=n
+# Compile PMD for NULL bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y
+# Compile PMD for turbo software bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n
+# Compile generic crypto device library
+CONFIG_RTE_LIBRTE_CRYPTODEV=n
+CONFIG_RTE_CRYPTO_MAX_DEVS=64
+# Compile PMD for ARMv8 Crypto device
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n
+# Compile NXP CAAM JR crypto Driver
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n
+# Compile NXP DPAA2 crypto sec driver for CAAM HW
+CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n
+# NXP DPAA caam - crypto driver
+CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n
+CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4
+# Compile PMD for Cavium OCTEON TX crypto device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y
+# Compile PMD for QuickAssist based devices - see docs for details
+CONFIG_RTE_LIBRTE_PMD_QAT=n
+CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n
+# Max. number of QuickAssist devices, which can be detected and attached
+CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48
+CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16
+CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536
+# Compile PMD for virtio crypto devices
+CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n
+# Number of maximum virtio crypto devices
+CONFIG_RTE_MAX_VIRTIO_CRYPTO=32
+# Compile PMD for AESNI backed device
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n
+# Compile PMD for Software backed device
+CONFIG_RTE_LIBRTE_PMD_OPENSSL=n
+# Compile PMD for AESNI GCM device
+CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n
+# Compile PMD for SNOW 3G device
+CONFIG_RTE_LIBRTE_PMD_SNOW3G=n
+CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n
+# Compile PMD for KASUMI device
+CONFIG_RTE_LIBRTE_PMD_KASUMI=n
+# Compile PMD for ZUC device
+CONFIG_RTE_LIBRTE_PMD_ZUC=n
+# Compile PMD for Crypto Scheduler device
+CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n
+# Compile PMD for NULL Crypto device
+CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n
+# Compile PMD for AMD CCP crypto device
+CONFIG_RTE_LIBRTE_PMD_CCP=n
+# Compile PMD for Marvell Crypto device
+CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n
+# Compile generic security library
+CONFIG_RTE_LIBRTE_SECURITY=n
+# Compile generic compression device library
+CONFIG_RTE_LIBRTE_COMPRESSDEV=n
+CONFIG_RTE_COMPRESS_MAX_DEVS=64
+# Compile compressdev unit test
+CONFIG_RTE_COMPRESSDEV_TEST=n
+# Compile PMD for Octeontx ZIPVF compression device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n
+# Compile PMD for ISA-L compression device
+CONFIG_RTE_LIBRTE_PMD_ISAL=n
+# Compile PMD for ZLIB compression device
+CONFIG_RTE_LIBRTE_PMD_ZLIB=n
+# Compile generic event device library
+CONFIG_RTE_LIBRTE_EVENTDEV=n
+CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n
+CONFIG_RTE_EVENT_MAX_DEVS=16
+CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64
+CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32
+CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024
+CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32
+CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32
+# Compile PMD for skeleton event device
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n
+# Compile PMD for software event device
+CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n
+# Compile PMD for distributed software event device
+CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n
+# Compile PMD for octeontx sso event device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n
+# Compile PMD for OPDL event device
+CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n
+# Compile PMD for NXP DPAA event device
+CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n
+# Compile PMD for NXP DPAA2 event device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n
+# Compile raw device support
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_RAWDEV=n
+CONFIG_RTE_RAWDEV_MAX_DEVS=10
+CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n
+# Compile PMD for NXP DPAA2 CMDIF raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n
+# Compile PMD for NXP DPAA2 QDMA raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n
+# Compile PMD for Intel FPGA raw device
+CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n
+# Compile librte_ring
+CONFIG_RTE_LIBRTE_RING=y
+# Compile librte_mempool
+CONFIG_RTE_LIBRTE_MEMPOOL=y
+CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512
+CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
+# Compile Mempool drivers
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64
+CONFIG_RTE_DRIVER_MEMPOOL_RING=y
+CONFIG_RTE_DRIVER_MEMPOOL_STACK=y
+# Compile PMD for octeontx fpa mempool device
+CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n
+# Compile librte_mbuf
+CONFIG_RTE_LIBRTE_MBUF=y
+CONFIG_RTE_LIBRTE_MBUF_DEBUG=n
+CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
+CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
+CONFIG_RTE_PKTMBUF_HEADROOM=128
+# Compile librte_timer
+CONFIG_RTE_LIBRTE_TIMER=n
+CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
+# Compile librte_cfgfile
+CONFIG_RTE_LIBRTE_CFGFILE=n
+# Compile librte_cmdline
+CONFIG_RTE_LIBRTE_CMDLINE=y
+CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
+# Compile librte_hash
+CONFIG_RTE_LIBRTE_HASH=y
+CONFIG_RTE_LIBRTE_HASH_DEBUG=n
+# Compile librte_efd
+CONFIG_RTE_LIBRTE_EFD=n
+# Compile librte_member
+CONFIG_RTE_LIBRTE_MEMBER=y
+# Compile librte_jobstats
+CONFIG_RTE_LIBRTE_JOBSTATS=n
+# Compile architecture we compile for. device metrics library
+CONFIG_RTE_LIBRTE_METRICS=y
+# Compile architecture we compile for. bitrate statistics library
+CONFIG_RTE_LIBRTE_BITRATE=y
+# Compile architecture we compile for. latency statistics library
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+# Compile librte_telemetry
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+# Compile librte_lpm
+CONFIG_RTE_LIBRTE_LPM=n
+CONFIG_RTE_LIBRTE_LPM_DEBUG=n
+# Compile librte_acl
+CONFIG_RTE_LIBRTE_ACL=n
+CONFIG_RTE_LIBRTE_ACL_DEBUG=n
+# Compile librte_power
+CONFIG_RTE_LIBRTE_POWER=n
+CONFIG_RTE_LIBRTE_POWER_DEBUG=n
+CONFIG_RTE_MAX_LCORE_FREQS=64
+# Compile librte_net
+CONFIG_RTE_LIBRTE_NET=y
+# Compile librte_ip_frag
+CONFIG_RTE_LIBRTE_IP_FRAG=y
+CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n
+CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4
+CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n
+# Compile GRO library
+CONFIG_RTE_LIBRTE_GRO=y
+# Compile GSO library
+CONFIG_RTE_LIBRTE_GSO=y
+# Compile librte_meter
+CONFIG_RTE_LIBRTE_METER=y
+# Compile librte_classify
+CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n
+# Compile librte_sched
+CONFIG_RTE_LIBRTE_SCHED=n
+CONFIG_RTE_SCHED_DEBUG=n
+CONFIG_RTE_SCHED_RED=n
+CONFIG_RTE_SCHED_COLLECT_STATS=n
+CONFIG_RTE_SCHED_SUBPORT_TC_OV=n
+CONFIG_RTE_SCHED_PORT_N_GRINDERS=8
+CONFIG_RTE_SCHED_VECTOR=n
+# Compile architecture we compile for. distributor library
+CONFIG_RTE_LIBRTE_DISTRIBUTOR=n
+# Compile architecture we compile for. reorder library
+CONFIG_RTE_LIBRTE_REORDER=n
+# Compile librte_port
+CONFIG_RTE_LIBRTE_PORT=n
+CONFIG_RTE_PORT_STATS_COLLECT=n
+CONFIG_RTE_PORT_PCAP=n
+# Compile librte_table
+CONFIG_RTE_LIBRTE_TABLE=n
+CONFIG_RTE_TABLE_STATS_COLLECT=n
+# Compile librte_pipeline
+CONFIG_RTE_LIBRTE_PIPELINE=n
+CONFIG_RTE_PIPELINE_STATS_COLLECT=n
+# Compile librte_kni
+CONFIG_RTE_LIBRTE_KNI=n
+CONFIG_RTE_LIBRTE_PMD_KNI=n
+CONFIG_RTE_KNI_KMOD=n
+CONFIG_RTE_KNI_KMOD_ETHTOOL=n
+CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
+# Compile architecture we compile for. pdump library
+CONFIG_RTE_LIBRTE_PDUMP=y
+# Compile vhost user library
+CONFIG_RTE_LIBRTE_VHOST=y
+CONFIG_RTE_LIBRTE_VHOST_NUMA=y
+CONFIG_RTE_LIBRTE_VHOST_DEBUG=n
+# Compile vhost PMD
+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.
+CONFIG_RTE_LIBRTE_PMD_VHOST=y
+# Compile IFC driver
+# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO
+# should be enabled.
+CONFIG_RTE_LIBRTE_IFC_PMD=n
+# Compile librte_bpf
+CONFIG_RTE_LIBRTE_BPF=n
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
+# Compile architecture we compile for. test application
+CONFIG_RTE_APP_TEST=y
+CONFIG_RTE_APP_TEST_RESOURCE_TAR=n
+# Compile architecture we compile for. procinfo application
+CONFIG_RTE_PROC_INFO=y
+# Compile architecture we compile for. PMD test application
+CONFIG_RTE_TEST_PMD=n
+CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
+CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+# Compile architecture we compile for. bbdev test application
+CONFIG_RTE_TEST_BBDEV=n
+# Compile architecture we compile for. crypto performance application
+CONFIG_RTE_APP_CRYPTO_PERF=n
+# Compile architecture we compile for. eventdev application
+CONFIG_RTE_APP_EVENTDEV=n
+CONFIG_RTE_EXEC_ENV_LINUXAPP=y
+CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n
+# Common libraries, before Bus/PMDs
+# NXP DPAA BUS and drivers
+# NXP FSLMC BUS and DPAA2 drivers
+# NXP ENETC PMD Driver
+CONFIG_RTE_ARCH_PPC_64=y
+CONFIG_RTE_ARCH_64=y
+CONFIG_RTE_TOOLCHAIN_GCC=y
+# Note: Power doesn't have this support
+# Note: Initially, all of architecture we compile for. PMD drivers compilation are turned off on Power
+# Will turn on them only after architecture we compile for. successful testing on Power
+CONFIG_RTE_LIBRTE_PMD_XENVIRT=n
diff --git a/SOURCES/set_config.sh b/SOURCES/set_config.sh
new file mode 100755
index 0000000..002386b
--- /dev/null
+++ b/SOURCES/set_config.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+# Copyright (C) 2017, Red Hat, Inc.
+#
+# set_config.sh will copy a configuration from $1 to $2, in the process
+# checking that the sha header for $1 matches the header in $2
+
+source configlib.sh
+
+if (( $# < 2 )); then
+    echo "$0: source dest [comment-marker]"
+    exit 1
+fi
+
+if [ ! -f "$1" ]; then
+    echo "Source file $1 must exist."
+    exit 1
+fi
+src_file=$1
+shift
+
+if [ ! -f "$1" ]; then
+    echo "Dest file $1 must exist."
+    exit 1
+fi
+dst_file=$1
+shift
+
+comment_sep=${1:-#}
+
+export LANG=en_US.utf8
+
+DEST_FILE_SHA=""
+SRC_FILE_SHA=""
+
+calc_sha DEST_FILE_SHA "$dst_file" "$comment_sep" || echo "Failed to calc sha"
+retr_sha SRC_FILE_SHA "$src_file" "$comment_sep" || echo "Failed to retrieve sha"
+
+if [ "$DEST_FILE_SHA" != "$SRC_FILE_SHA" ]; then
+    echo "ERROR: The requisite starting sha from $dst_file does not match the"
+    echo "       specified sha in $src_file."
+    echo "[ $DEST_FILE_SHA ] vs [ $SRC_FILE_SHA ]"
+    exit 1
+fi
+
+mv "$dst_file" "$dst_file".OLD
+cp "$src_file" "$dst_file"
+echo "copied 1 config file."
+exit 0
diff --git a/SOURCES/x86_64-native-linuxapp-gcc-config b/SOURCES/x86_64-native-linuxapp-gcc-config
new file mode 100644
index 0000000..4b7a7ea
--- /dev/null
+++ b/SOURCES/x86_64-native-linuxapp-gcc-config
@@ -0,0 +1,525 @@
+# -*- cfg-sha: 2ba93102021dc5d38494cf5090c3ecaca37db13153dd558b1511a56f2a3d9b10
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2016 Intel Corporation
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2017 Intel Corporation
+# RTE_EXEC_ENV values are the directories in mk/exec-env/
+CONFIG_RTE_EXEC_ENV="linuxapp"
+# RTE_ARCH values are architecture we compile for. directories in mk/arch/
+CONFIG_RTE_ARCH="x86_64"
+# machine can define specific variables or action for a specific board
+# RTE_MACHINE values are architecture we compile for. directories in mk/machine/
+CONFIG_RTE_MACHINE="default"
+# The compiler we use.
+# RTE_TOOLCHAIN values are architecture we compile for. directories in mk/toolchain/
+CONFIG_RTE_TOOLCHAIN="gcc"
+# Use intrinsics or assembly code for key routines
+CONFIG_RTE_FORCE_INTRINSICS=n
+# Machine forces strict alignment constraints.
+CONFIG_RTE_ARCH_STRICT_ALIGN=n
+# Compile to share library
+CONFIG_RTE_BUILD_SHARED_LIB=n
+# Use newest code breaking previous ABI
+CONFIG_RTE_NEXT_ABI=n
+# Major ABI to overwrite library specific LIBABIVER
+CONFIG_RTE_MAJOR_ABI=
+# Machine's cache line size
+CONFIG_RTE_CACHE_LINE_SIZE=64
+# Memory model
+CONFIG_RTE_USE_C11_MEM_MODEL=n
+# Compile Environment Abstraction Layer
+CONFIG_RTE_LIBRTE_EAL=y
+CONFIG_RTE_MAX_LCORE=128
+CONFIG_RTE_MAX_NUMA_NODES=8
+CONFIG_RTE_MAX_HEAPS=32
+CONFIG_RTE_MAX_MEMSEG_LISTS=64
+# each memseg list will be limited to either RTE_MAX_MEMSEG_PER_LIST pages
+# or RTE_MAX_MEM_MB_PER_LIST megabytes worth of memory, whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_LIST=8192
+CONFIG_RTE_MAX_MEM_MB_PER_LIST=32768
+# a "type" is a combination of page size and NUMA node. total number of memseg
+# lists per type will be limited to either RTE_MAX_MEMSEG_PER_TYPE pages (split
+# over multiple lists of RTE_MAX_MEMSEG_PER_LIST pages), or
+# RTE_MAX_MEM_MB_PER_TYPE megabytes of memory (split over multiple lists of
+# RTE_MAX_MEM_MB_PER_LIST), whichever is smaller
+CONFIG_RTE_MAX_MEMSEG_PER_TYPE=32768
+CONFIG_RTE_MAX_MEM_MB_PER_TYPE=131072
+# global maximum usable amount of VA, in megabytes
+CONFIG_RTE_MAX_MEM_MB=524288
+CONFIG_RTE_MAX_MEMZONE=2560
+CONFIG_RTE_MAX_TAILQ=32
+CONFIG_RTE_ENABLE_ASSERT=n
+CONFIG_RTE_LOG_DP_LEVEL=RTE_LOG_INFO
+CONFIG_RTE_LOG_HISTORY=256
+CONFIG_RTE_BACKTRACE=y
+CONFIG_RTE_LIBEAL_USE_HPET=n
+CONFIG_RTE_EAL_ALLOW_INV_SOCKET_ID=n
+CONFIG_RTE_EAL_ALWAYS_PANIC_ON_ERROR=n
+CONFIG_RTE_EAL_IGB_UIO=n
+CONFIG_RTE_EAL_VFIO=y
+CONFIG_RTE_MAX_VFIO_GROUPS=64
+CONFIG_RTE_MAX_VFIO_CONTAINERS=64
+CONFIG_RTE_MALLOC_DEBUG=n
+CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES=y
+CONFIG_RTE_USE_LIBBSD=n
+# Recognize/ignore architecture we compile for. AVX/AVX512 CPU flags for performance/power testing.
+# AVX512 is marked as experimental for now, will enable it after enough
+# field test and possible optimization.
+CONFIG_RTE_ENABLE_AVX=y
+CONFIG_RTE_ENABLE_AVX512=n
+# Default driver path (or "" to disable)
+CONFIG_RTE_EAL_PMD_PATH=""
+# Compile Environment Abstraction Layer to support Vmware TSC map
+CONFIG_RTE_LIBRTE_EAL_VMWARE_TSC_MAP_SUPPORT=y
+# Compile architecture we compile for. PCI library
+CONFIG_RTE_LIBRTE_PCI=y
+# Compile architecture we compile for. argument parser library
+CONFIG_RTE_LIBRTE_KVARGS=y
+# Compile generic ethernet library
+CONFIG_RTE_LIBRTE_ETHER=y
+CONFIG_RTE_LIBRTE_ETHDEV_DEBUG=n
+CONFIG_RTE_MAX_ETHPORTS=32
+CONFIG_RTE_MAX_QUEUES_PER_PORT=1024
+CONFIG_RTE_LIBRTE_IEEE1588=n
+CONFIG_RTE_ETHDEV_QUEUE_STAT_CNTRS=16
+CONFIG_RTE_ETHDEV_RXTX_CALLBACKS=y
+CONFIG_RTE_ETHDEV_PROFILE_WITH_VTUNE=n
+# Turn off Tx preparation stage
+# Warning: rte_eth_tx_prepare() can be safely disabled only if using a
+# driver which do not implement any Tx preparation.
+CONFIG_RTE_ETHDEV_TX_PREPARE_NOOP=n
+# Common libraries, before Bus/PMDs
+CONFIG_RTE_LIBRTE_COMMON_DPAAX=n
+# Compile architecture we compile for. Intel FPGA bus
+CONFIG_RTE_LIBRTE_IFPGA_BUS=n
+# Compile PCI bus driver
+CONFIG_RTE_LIBRTE_PCI_BUS=y
+# Compile architecture we compile for. vdev bus
+CONFIG_RTE_LIBRTE_VDEV_BUS=y
+# Compile ARK PMD
+CONFIG_RTE_LIBRTE_ARK_PMD=n
+CONFIG_RTE_LIBRTE_ARK_PAD_TX=y
+CONFIG_RTE_LIBRTE_ARK_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_STATS=n
+CONFIG_RTE_LIBRTE_ARK_DEBUG_TRACE=n
+# Compile Aquantia Atlantic PMD driver
+CONFIG_RTE_LIBRTE_ATLANTIC_PMD=n
+# Compile AMD PMD
+CONFIG_RTE_LIBRTE_AXGBE_PMD=n
+CONFIG_RTE_LIBRTE_AXGBE_PMD_DEBUG=n
+# Compile burst-oriented Broadcom PMD driver
+CONFIG_RTE_LIBRTE_BNX2X_PMD=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_BNX2X_MF_SUPPORT=n
+CONFIG_RTE_LIBRTE_BNX2X_DEBUG_PERIODIC=n
+# Compile burst-oriented Broadcom BNXT PMD driver
+CONFIG_RTE_LIBRTE_BNXT_PMD=y
+# Compile burst-oriented Chelsio Terminator (CXGBE) PMD
+CONFIG_RTE_LIBRTE_CXGBE_PMD=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_REG=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_CXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_CXGBE_TPUT=y
+# NXP DPAA Bus
+CONFIG_RTE_LIBRTE_DPAA_BUS=n
+CONFIG_RTE_LIBRTE_DPAA_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA_PMD=n
+CONFIG_RTE_LIBRTE_DPAA_HWDEBUG=n
+# Compile NXP DPAA2 FSL-MC Bus
+CONFIG_RTE_LIBRTE_FSLMC_BUS=n
+# Compile Support Libraries for NXP DPAA2
+CONFIG_RTE_LIBRTE_DPAA2_MEMPOOL=n
+CONFIG_RTE_LIBRTE_DPAA2_USE_PHYS_IOVA=y
+# Compile burst-oriented NXP DPAA2 PMD driver
+CONFIG_RTE_LIBRTE_DPAA2_PMD=n
+CONFIG_RTE_LIBRTE_DPAA2_DEBUG_DRIVER=n
+# Compile NXP ENETC PMD Driver
+CONFIG_RTE_LIBRTE_ENETC_PMD=n
+# Compile burst-oriented Amazon ENA PMD driver
+CONFIG_RTE_LIBRTE_ENA_PMD=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_ENA_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_ENA_COM_DEBUG=n
+# Compile burst-oriented Cisco ENIC PMD driver
+CONFIG_RTE_LIBRTE_ENIC_PMD=y
+# Compile burst-oriented IGB & EM PMD drivers
+CONFIG_RTE_LIBRTE_EM_PMD=n
+CONFIG_RTE_LIBRTE_IGB_PMD=y
+CONFIG_RTE_LIBRTE_E1000_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_E1000_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_E1000_PF_DISABLE_STRIP_CRC=n
+# Compile burst-oriented IXGBE PMD driver
+CONFIG_RTE_LIBRTE_IXGBE_PMD=y
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_IXGBE_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_IXGBE_PF_DISABLE_STRIP_CRC=n
+CONFIG_RTE_IXGBE_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_IXGBE_BYPASS=n
+# Compile burst-oriented I40E PMD driver
+CONFIG_RTE_LIBRTE_I40E_PMD=y
+CONFIG_RTE_LIBRTE_I40E_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_I40E_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_I40E_RX_ALLOW_BULK_ALLOC=y
+CONFIG_RTE_LIBRTE_I40E_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_I40E_16BYTE_RX_DESC=n
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_PF=64
+CONFIG_RTE_LIBRTE_I40E_QUEUE_NUM_PER_VM=4
+# Compile burst-oriented FM10K PMD
+CONFIG_RTE_LIBRTE_FM10K_PMD=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_FM10K_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_FM10K_RX_OLFLAGS_ENABLE=y
+CONFIG_RTE_LIBRTE_FM10K_INC_VECTOR=y
+# Compile burst-oriented AVF PMD driver
+CONFIG_RTE_LIBRTE_AVF_PMD=n
+CONFIG_RTE_LIBRTE_AVF_INC_VECTOR=y
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_TX_FREE=n
+CONFIG_RTE_LIBRTE_AVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVF_16BYTE_RX_DESC=n
+# Compile burst-oriented Mellanox ConnectX-3 (MLX4) PMD
+CONFIG_RTE_LIBRTE_MLX4_PMD=y
+CONFIG_RTE_LIBRTE_MLX4_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX4_DLOPEN_DEPS=y
+# Compile burst-oriented Mellanox ConnectX-4, ConnectX-5 & Bluefield
+# (MLX5) PMD
+CONFIG_RTE_LIBRTE_MLX5_PMD=y
+CONFIG_RTE_LIBRTE_MLX5_DEBUG=n
+CONFIG_RTE_LIBRTE_MLX5_DLOPEN_DEPS=y
+# Compile burst-oriented Netronome NFP PMD driver
+CONFIG_RTE_LIBRTE_NFP_PMD=y
+CONFIG_RTE_LIBRTE_NFP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NFP_DEBUG_RX=n
+# QLogic 10G/25G/40G/50G/100G PMD
+CONFIG_RTE_LIBRTE_QEDE_PMD=y
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_QEDE_DEBUG_RX=n
+#Provides abs path/name of architecture we compile for. firmware file.
+#Empty string denotes driver will use default firmware
+CONFIG_RTE_LIBRTE_QEDE_FW=""
+# Compile burst-oriented Solarflare libefx-based PMD
+CONFIG_RTE_LIBRTE_SFC_EFX_PMD=n
+CONFIG_RTE_LIBRTE_SFC_EFX_DEBUG=n
+# Compile software PMD backed by SZEDATA2 device
+CONFIG_RTE_LIBRTE_PMD_SZEDATA2=n
+# Compile burst-oriented Cavium Thunderx NICVF PMD driver
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_PMD=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_THUNDERX_NICVF_DEBUG_TX=n
+# Compile burst-oriented Cavium LiquidIO PMD driver
+CONFIG_RTE_LIBRTE_LIO_PMD=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_MBOX=n
+CONFIG_RTE_LIBRTE_LIO_DEBUG_REGS=n
+# Compile burst-oriented Cavium OCTEONTX network PMD driver
+CONFIG_RTE_LIBRTE_OCTEONTX_PMD=n
+# Compile WRS accelerated virtual port (AVP) guest PMD driver
+CONFIG_RTE_LIBRTE_AVP_PMD=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_AVP_DEBUG_BUFFERS=n
+# Compile burst-oriented VIRTIO PMD driver
+CONFIG_RTE_LIBRTE_VIRTIO_PMD=y
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VIRTIO_DEBUG_DUMP=n
+# Compile virtio device emulation inside virtio PMD driver
+CONFIG_RTE_VIRTIO_USER=n
+# Compile burst-oriented VMXNET3 PMD driver
+CONFIG_RTE_LIBRTE_VMXNET3_PMD=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_VMXNET3_DEBUG_TX_FREE=n
+# Compile software PMD backed by AF_PACKET sockets (Linux only)
+CONFIG_RTE_LIBRTE_PMD_AF_PACKET=n
+# Compile link bonding PMD library
+CONFIG_RTE_LIBRTE_PMD_BOND=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB=n
+CONFIG_RTE_LIBRTE_BOND_DEBUG_ALB_L1=n
+# Compile fail-safe PMD
+CONFIG_RTE_LIBRTE_PMD_FAILSAFE=y
+# Compile Marvell PMD driver
+CONFIG_RTE_LIBRTE_MVPP2_PMD=n
+# Compile Marvell MVNETA PMD driver
+CONFIG_RTE_LIBRTE_MVNETA_PMD=n
+# Compile support for VMBus library
+CONFIG_RTE_LIBRTE_VMBUS=y
+# Compile native PMD for Hyper-V/Azure
+CONFIG_RTE_LIBRTE_NETVSC_PMD=y
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_RX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_TX=n
+CONFIG_RTE_LIBRTE_NETVSC_DEBUG_DUMP=n
+# Compile virtual device driver for NetVSC on Hyper-V/Azure
+CONFIG_RTE_LIBRTE_VDEV_NETVSC_PMD=y
+# Compile null PMD
+CONFIG_RTE_LIBRTE_PMD_NULL=n
+# Compile software PMD backed by PCAP files
+CONFIG_RTE_LIBRTE_PMD_PCAP=n
+# Compile example software rings based PMD
+CONFIG_RTE_LIBRTE_PMD_RING=y
+CONFIG_RTE_PMD_RING_MAX_RX_RINGS=16
+CONFIG_RTE_PMD_RING_MAX_TX_RINGS=16
+# Compile SOFTNIC PMD
+CONFIG_RTE_LIBRTE_PMD_SOFTNIC=n
+# Compile architecture we compile for. TAP PMD
+# It is enabled by default for Linux only.
+CONFIG_RTE_LIBRTE_PMD_TAP=y
+# Do prefetch of packet data within PMD driver receive function
+CONFIG_RTE_PMD_PACKET_PREFETCH=y
+# Compile generic wireless base band device library
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_BBDEV=n
+CONFIG_RTE_BBDEV_MAX_DEVS=128
+CONFIG_RTE_BBDEV_OFFLOAD_COST=n
+# Compile PMD for NULL bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_NULL=y
+# Compile PMD for turbo software bbdev device
+CONFIG_RTE_LIBRTE_PMD_BBDEV_TURBO_SW=n
+# Compile generic crypto device library
+CONFIG_RTE_LIBRTE_CRYPTODEV=n
+CONFIG_RTE_CRYPTO_MAX_DEVS=64
+# Compile PMD for ARMv8 Crypto device
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO=n
+CONFIG_RTE_LIBRTE_PMD_ARMV8_CRYPTO_DEBUG=n
+# Compile NXP CAAM JR crypto Driver
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR=n
+CONFIG_RTE_LIBRTE_PMD_CAAM_JR_BE=n
+# Compile NXP DPAA2 crypto sec driver for CAAM HW
+CONFIG_RTE_LIBRTE_PMD_DPAA2_SEC=n
+# NXP DPAA caam - crypto driver
+CONFIG_RTE_LIBRTE_PMD_DPAA_SEC=n
+CONFIG_RTE_LIBRTE_DPAA_MAX_CRYPTODEV=4
+# Compile PMD for Cavium OCTEON TX crypto device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_CRYPTO=y
+# Compile PMD for QuickAssist based devices - see docs for details
+CONFIG_RTE_LIBRTE_PMD_QAT=n
+CONFIG_RTE_LIBRTE_PMD_QAT_SYM=n
+# Max. number of QuickAssist devices, which can be detected and attached
+CONFIG_RTE_PMD_QAT_MAX_PCI_DEVICES=48
+CONFIG_RTE_PMD_QAT_COMP_SGL_MAX_SEGMENTS=16
+CONFIG_RTE_PMD_QAT_COMP_IM_BUFFER_SIZE=65536
+# Compile PMD for virtio crypto devices
+CONFIG_RTE_LIBRTE_PMD_VIRTIO_CRYPTO=n
+# Number of maximum virtio crypto devices
+CONFIG_RTE_MAX_VIRTIO_CRYPTO=32
+# Compile PMD for AESNI backed device
+CONFIG_RTE_LIBRTE_PMD_AESNI_MB=n
+# Compile PMD for Software backed device
+CONFIG_RTE_LIBRTE_PMD_OPENSSL=n
+# Compile PMD for AESNI GCM device
+CONFIG_RTE_LIBRTE_PMD_AESNI_GCM=n
+# Compile PMD for SNOW 3G device
+CONFIG_RTE_LIBRTE_PMD_SNOW3G=n
+CONFIG_RTE_LIBRTE_PMD_SNOW3G_DEBUG=n
+# Compile PMD for KASUMI device
+CONFIG_RTE_LIBRTE_PMD_KASUMI=n
+# Compile PMD for ZUC device
+CONFIG_RTE_LIBRTE_PMD_ZUC=n
+# Compile PMD for Crypto Scheduler device
+CONFIG_RTE_LIBRTE_PMD_CRYPTO_SCHEDULER=n
+# Compile PMD for NULL Crypto device
+CONFIG_RTE_LIBRTE_PMD_NULL_CRYPTO=n
+# Compile PMD for AMD CCP crypto device
+CONFIG_RTE_LIBRTE_PMD_CCP=n
+# Compile PMD for Marvell Crypto device
+CONFIG_RTE_LIBRTE_PMD_MVSAM_CRYPTO=n
+# Compile generic security library
+CONFIG_RTE_LIBRTE_SECURITY=n
+# Compile generic compression device library
+CONFIG_RTE_LIBRTE_COMPRESSDEV=n
+CONFIG_RTE_COMPRESS_MAX_DEVS=64
+# Compile compressdev unit test
+CONFIG_RTE_COMPRESSDEV_TEST=n
+# Compile PMD for Octeontx ZIPVF compression device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_ZIPVF=n
+# Compile PMD for ISA-L compression device
+CONFIG_RTE_LIBRTE_PMD_ISAL=n
+# Compile PMD for ZLIB compression device
+CONFIG_RTE_LIBRTE_PMD_ZLIB=n
+# Compile generic event device library
+CONFIG_RTE_LIBRTE_EVENTDEV=n
+CONFIG_RTE_LIBRTE_EVENTDEV_DEBUG=n
+CONFIG_RTE_EVENT_MAX_DEVS=16
+CONFIG_RTE_EVENT_MAX_QUEUES_PER_DEV=64
+CONFIG_RTE_EVENT_TIMER_ADAPTER_NUM_MAX=32
+CONFIG_RTE_EVENT_ETH_INTR_RING_SIZE=1024
+CONFIG_RTE_EVENT_CRYPTO_ADAPTER_MAX_INSTANCE=32
+CONFIG_RTE_EVENT_ETH_TX_ADAPTER_MAX_INSTANCE=32
+# Compile PMD for skeleton event device
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV=n
+CONFIG_RTE_LIBRTE_PMD_SKELETON_EVENTDEV_DEBUG=n
+# Compile PMD for software event device
+CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV=n
+# Compile PMD for distributed software event device
+CONFIG_RTE_LIBRTE_PMD_DSW_EVENTDEV=n
+# Compile PMD for octeontx sso event device
+CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF=n
+# Compile PMD for OPDL event device
+CONFIG_RTE_LIBRTE_PMD_OPDL_EVENTDEV=n
+# Compile PMD for NXP DPAA event device
+CONFIG_RTE_LIBRTE_PMD_DPAA_EVENTDEV=n
+# Compile PMD for NXP DPAA2 event device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_EVENTDEV=n
+# Compile raw device support
+# EXPERIMENTAL: API may change without prior notice
+CONFIG_RTE_LIBRTE_RAWDEV=n
+CONFIG_RTE_RAWDEV_MAX_DEVS=10
+CONFIG_RTE_LIBRTE_PMD_SKELETON_RAWDEV=n
+# Compile PMD for NXP DPAA2 CMDIF raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_CMDIF_RAWDEV=n
+# Compile PMD for NXP DPAA2 QDMA raw device
+CONFIG_RTE_LIBRTE_PMD_DPAA2_QDMA_RAWDEV=n
+# Compile PMD for Intel FPGA raw device
+CONFIG_RTE_LIBRTE_PMD_IFPGA_RAWDEV=n
+# Compile librte_ring
+CONFIG_RTE_LIBRTE_RING=y
+# Compile librte_mempool
+CONFIG_RTE_LIBRTE_MEMPOOL=y
+CONFIG_RTE_MEMPOOL_CACHE_MAX_SIZE=512
+CONFIG_RTE_LIBRTE_MEMPOOL_DEBUG=n
+# Compile Mempool drivers
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET=y
+CONFIG_RTE_DRIVER_MEMPOOL_BUCKET_SIZE_KB=64
+CONFIG_RTE_DRIVER_MEMPOOL_RING=y
+CONFIG_RTE_DRIVER_MEMPOOL_STACK=y
+# Compile PMD for octeontx fpa mempool device
+CONFIG_RTE_LIBRTE_OCTEONTX_MEMPOOL=n
+# Compile librte_mbuf
+CONFIG_RTE_LIBRTE_MBUF=y
+CONFIG_RTE_LIBRTE_MBUF_DEBUG=n
+CONFIG_RTE_MBUF_DEFAULT_MEMPOOL_OPS="ring_mp_mc"
+CONFIG_RTE_MBUF_REFCNT_ATOMIC=y
+CONFIG_RTE_PKTMBUF_HEADROOM=128
+# Compile librte_timer
+CONFIG_RTE_LIBRTE_TIMER=n
+CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
+# Compile librte_cfgfile
+CONFIG_RTE_LIBRTE_CFGFILE=n
+# Compile librte_cmdline
+CONFIG_RTE_LIBRTE_CMDLINE=y
+CONFIG_RTE_LIBRTE_CMDLINE_DEBUG=n
+# Compile librte_hash
+CONFIG_RTE_LIBRTE_HASH=y
+CONFIG_RTE_LIBRTE_HASH_DEBUG=n
+# Compile librte_efd
+CONFIG_RTE_LIBRTE_EFD=n
+# Compile librte_member
+CONFIG_RTE_LIBRTE_MEMBER=y
+# Compile librte_jobstats
+CONFIG_RTE_LIBRTE_JOBSTATS=n
+# Compile architecture we compile for. device metrics library
+CONFIG_RTE_LIBRTE_METRICS=y
+# Compile architecture we compile for. bitrate statistics library
+CONFIG_RTE_LIBRTE_BITRATE=y
+# Compile architecture we compile for. latency statistics library
+CONFIG_RTE_LIBRTE_LATENCY_STATS=y
+# Compile librte_telemetry
+CONFIG_RTE_LIBRTE_TELEMETRY=n
+# Compile librte_lpm
+CONFIG_RTE_LIBRTE_LPM=n
+CONFIG_RTE_LIBRTE_LPM_DEBUG=n
+# Compile librte_acl
+CONFIG_RTE_LIBRTE_ACL=n
+CONFIG_RTE_LIBRTE_ACL_DEBUG=n
+# Compile librte_power
+CONFIG_RTE_LIBRTE_POWER=n
+CONFIG_RTE_LIBRTE_POWER_DEBUG=n
+CONFIG_RTE_MAX_LCORE_FREQS=64
+# Compile librte_net
+CONFIG_RTE_LIBRTE_NET=y
+# Compile librte_ip_frag
+CONFIG_RTE_LIBRTE_IP_FRAG=y
+CONFIG_RTE_LIBRTE_IP_FRAG_DEBUG=n
+CONFIG_RTE_LIBRTE_IP_FRAG_MAX_FRAG=4
+CONFIG_RTE_LIBRTE_IP_FRAG_TBL_STAT=n
+# Compile GRO library
+CONFIG_RTE_LIBRTE_GRO=y
+# Compile GSO library
+CONFIG_RTE_LIBRTE_GSO=y
+# Compile librte_meter
+CONFIG_RTE_LIBRTE_METER=y
+# Compile librte_classify
+CONFIG_RTE_LIBRTE_FLOW_CLASSIFY=n
+# Compile librte_sched
+CONFIG_RTE_LIBRTE_SCHED=n
+CONFIG_RTE_SCHED_DEBUG=n
+CONFIG_RTE_SCHED_RED=n
+CONFIG_RTE_SCHED_COLLECT_STATS=n
+CONFIG_RTE_SCHED_SUBPORT_TC_OV=n
+CONFIG_RTE_SCHED_PORT_N_GRINDERS=8
+CONFIG_RTE_SCHED_VECTOR=n
+# Compile architecture we compile for. distributor library
+CONFIG_RTE_LIBRTE_DISTRIBUTOR=n
+# Compile architecture we compile for. reorder library
+CONFIG_RTE_LIBRTE_REORDER=n
+# Compile librte_port
+CONFIG_RTE_LIBRTE_PORT=n
+CONFIG_RTE_PORT_STATS_COLLECT=n
+CONFIG_RTE_PORT_PCAP=n
+# Compile librte_table
+CONFIG_RTE_LIBRTE_TABLE=n
+CONFIG_RTE_TABLE_STATS_COLLECT=n
+# Compile librte_pipeline
+CONFIG_RTE_LIBRTE_PIPELINE=n
+CONFIG_RTE_PIPELINE_STATS_COLLECT=n
+# Compile librte_kni
+CONFIG_RTE_LIBRTE_KNI=n
+CONFIG_RTE_LIBRTE_PMD_KNI=n
+CONFIG_RTE_KNI_KMOD=n
+CONFIG_RTE_KNI_KMOD_ETHTOOL=n
+CONFIG_RTE_KNI_PREEMPT_DEFAULT=y
+# Compile architecture we compile for. pdump library
+CONFIG_RTE_LIBRTE_PDUMP=y
+# Compile vhost user library
+CONFIG_RTE_LIBRTE_VHOST=y
+CONFIG_RTE_LIBRTE_VHOST_NUMA=y
+CONFIG_RTE_LIBRTE_VHOST_DEBUG=n
+# Compile vhost PMD
+# To compile, CONFIG_RTE_LIBRTE_VHOST should be enabled.
+CONFIG_RTE_LIBRTE_PMD_VHOST=y
+# Compile IFC driver
+# To compile, CONFIG_RTE_LIBRTE_VHOST and CONFIG_RTE_EAL_VFIO
+# should be enabled.
+CONFIG_RTE_LIBRTE_IFC_PMD=n
+# Compile librte_bpf
+CONFIG_RTE_LIBRTE_BPF=n
+# allow load BPF from ELF files (requires libelf)
+CONFIG_RTE_LIBRTE_BPF_ELF=n
+# Compile architecture we compile for. test application
+CONFIG_RTE_APP_TEST=y
+CONFIG_RTE_APP_TEST_RESOURCE_TAR=n
+# Compile architecture we compile for. procinfo application
+CONFIG_RTE_PROC_INFO=y
+# Compile architecture we compile for. PMD test application
+CONFIG_RTE_TEST_PMD=n
+CONFIG_RTE_TEST_PMD_RECORD_CORE_CYCLES=n
+CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
+# Compile architecture we compile for. bbdev test application
+CONFIG_RTE_TEST_BBDEV=n
+# Compile architecture we compile for. crypto performance application
+CONFIG_RTE_APP_CRYPTO_PERF=n
+# Compile architecture we compile for. eventdev application
+CONFIG_RTE_APP_EVENTDEV=n
+CONFIG_RTE_EXEC_ENV_LINUXAPP=y
+CONFIG_RTE_LIBRTE_VHOST_POSTCOPY=n
+# Common libraries, before Bus/PMDs
+# NXP DPAA BUS and drivers
+# NXP FSLMC BUS and DPAA2 drivers
+# NXP ENETC PMD Driver
+CONFIG_RTE_ARCH_X86_64=y
+CONFIG_RTE_ARCH_X86=y
+CONFIG_RTE_ARCH_64=y
+CONFIG_RTE_TOOLCHAIN_GCC=y
+CONFIG_RTE_LIBRTE_PMD_XENVIRT=n
diff --git a/SPECS/ovn2.11.spec b/SPECS/ovn2.11.spec
new file mode 100644
index 0000000..5397655
--- /dev/null
+++ b/SPECS/ovn2.11.spec
@@ -0,0 +1,1057 @@
+# Spec file for Open Virtual Network (OVN).
+
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# Copying and distribution of this file, with or without modification,
+# are permitted in any medium without royalty provided the copyright
+# notice and this notice are preserved.  This file is offered as-is,
+# without warranty of any kind.
+#
+# If tests have to be skipped while building, specify the '--without check'
+# option. For example:
+#     rpmbuild -bb --without check rhel/ovn-fedora.spec
+#
+
+# This defines the base package name's version.
+%define pkgver 2.11
+%define pkgname ovn%{pkgver}
+
+#%%global commit0 f11b99776c46831184ac30065c6cdf911061bb5a
+#%%global date 20190223
+#%%global shortcommit0 %(c=%{commit0}; echo ${c:0:7})
+
+# If libcap-ng isn't available and there is no need for running OVS
+# as regular user, specify the '--without libcapng'
+%bcond_without libcapng
+
+# option to build ovn-docker package
+%bcond_with ovn_docker
+
+# Enable PIE, bz#955181
+%global _hardened_build 1
+
+# some distros (e.g: RHEL-7) don't define _rundir macro yet
+# Fedora 15 onwards uses /run as _rundir
+%if 0%{!?_rundir:1}
+%define _rundir /run
+%endif
+
+# Build python2 (that provides python) and python3 subpackages on Fedora
+# Build only python3 (that provides python) subpackage on RHEL8
+# Build only python subpackage on RHEL7
+%if 0%{?rhel} > 7 || 0%{?fedora}
+# Use Python3
+%global _py python3
+%global _py2 python2
+%global with_python3 1
+%if 0%{?fedora}
+%global with_python2 1
+%else
+%global with_python2 0
+%endif
+# On RHEL8 Sphinx is included in buildroot
+%global external_sphinx 1
+%else
+# Use Python2
+%global _py python
+%global _py2 python
+%global with_python2 1
+%global with_python3 0
+# Don't use external sphinx (RHV doesn't have optional repositories enabled)
+%global external_sphinx 0
+%endif
+
+Name: %{pkgname}
+Summary: Open Virtual Network support
+Group: System Environment/Daemons
+URL: http://www.openvswitch.org/
+Version: %{pkgver}.1
+Release: 53%{?commit0:.%{date}git%{shortcommit0}}%{?dist}
+Provides: openvswitch%{pkgver}-ovn-common = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-common < 2.11.0-1
+
+# Nearly all of openvswitch is ASL 2.0.  The bugtool is LGPLv2+, and the
+# lib/sflow*.[ch] files are SISSL
+License: ASL 2.0 and LGPLv2+ and SISSL
+
+%if 0%{?commit0:1}
+Source: https://github.com/openvswitch/ovs/archive/%{commit0}.tar.gz#/openvswitch-%{shortcommit0}.tar.gz
+%else
+Source: https://www.openvswitch.org/releases/openvswitch-%{version}.tar.gz
+%endif
+
+%define docutilsver 0.12
+%define pygmentsver 1.4
+%define sphinxver   1.1.3
+Source100: https://pypi.io/packages/source/d/docutils/docutils-%{docutilsver}.tar.gz
+Source101: https://pypi.io/packages/source/P/Pygments/Pygments-%{pygmentsver}.tar.gz
+Source102: https://pypi.io/packages/source/S/Sphinx/Sphinx-%{sphinxver}.tar.gz
+
+Source500: configlib.sh
+Source501: gen_config_group.sh
+Source502: set_config.sh
+
+# Important: source503 is used as the actual copy file
+# @TODO: this causes a warning - fix it?
+Source504: arm64-armv8a-linuxapp-gcc-config
+Source505: ppc_64-power8-linuxapp-gcc-config
+Source506: x86_64-native-linuxapp-gcc-config
+
+# ovn-patches
+
+# OVN (including OVS if required) backports (0 - 399)
+# Bug 1741057
+Patch2: 0001-Make-pidfile_is_running-more-robust-against-empty-pi.patch
+
+# Bug 1749739
+Patch10: 0001-Handle-GARP-reply-packets-from-provider-networks-onl.patch
+
+# Bug 1751942
+Patch20: 0001-ovn-Exclude-inport-and-outport-symbol-tables-from-co.patch
+
+# Bug 1743577
+Patch30: 0001-northd-introduce-build_empty_lb_event_flow-routine.patch
+Patch31: 0002-add-meter-support-to-trigger_event-action.patch
+Patch32: 0003-northd-add-rate-limiting-support-for-SB-controller-e.patch
+Patch33: 0004-northd-add-empty_lb-controller_event-for-logical-rou.patch
+
+# Bug 1756466
+Patch40: 0001-Disable-conjunction-by-force-cross-product-for-all-t.patch
+
+# Bug 1580542
+Patch50: 0001-Add-egress-QoS-mapping-for-non-tunnel-interfaces.patch
+Patch51: 0002-northd-add-the-possibility-to-define-localnet-as-qos.patch
+Patch52: 0003-northd-introduce-logical-flow-for-localnet-egress-sh.patch
+
+# Bug 1729846, 1757714 & 1757715
+Patch60: 0001-ovn-northd-Use-HMAP_FOR_EACH-when-adding-multicast-l.patch
+Patch61: 0002-ovn-northd-Add-IGMP-Relay-support.patch
+Patch62: 0003-Learn-the-mac-binding-only-if-required.patch
+Patch63: 0004-ovn-northd-Add-static-IP-multicast-flood-configurati.patch
+Patch64: 0001-ovn-northd-Fix-IP-multicast-flooding-to-mrouter.patch
+
+# Bug 1699332
+Patch70: 0001-Add-RDNSS-support-to-OVN.patch
+
+# Bug 1764718
+Patch80: 0001-Add-DNSSL-support-to-OVN.patch
+
+# Bug 1764032
+Patch90: 0001-Revert-conjunctive-match-removal-patches.patch
+Patch91: 0002-Combine-conjunctions-with-identical-matches-into-one.patch
+Patch92: 0001-Partially-revert-Exclude-inport-and-outport-symbol-t.patch
+
+# Bug 1768347
+Patch100: 0001-northd-Fix-table-ID-for-IPv6-router-ingress.patch
+Patch101: 0002-actions-Add-IPv6-support-to-lflow-NAT-actions.patch
+Patch102: 0003-ovn-nbctl-Allow-IPv6-NAT-rules-to-be-added.patch
+Patch103: 0004-northd-Add-lflows-for-IPv6-NAT.patch
+Patch104: 0005-system-ovn-Add-IPv6-NAT-test-cases.patch
+Patch105: 0006-controller-Downgrade-a-warning-log-message.patch
+
+#Bug 1762341
+Patch110: 0001-Fix-virtual-port-binding-when-the-parents-are-schedu.patch
+
+#Bug 1769043
+Patch120: 0001-Prevent-erroneous-duplicate-IP-address-messages.patch
+
+# Bug 1770953
+Patch130: 0001-lflow.c-Fix-memory-leak-of-lflow_ref_list_node-ref_n.patch
+
+# Bug 1769709
+Patch140: 0001-ovn-northd-Validate-dnat_and_snat-external_mac-logic.patch
+
+# Bug 1762777
+Patch150: 0001-Fix-ha-chassis-failover-issues-for-stale-ha-chassis-.patch
+
+# Bug 1773605
+Patch160: 0001-northd-Match-IPv4-or-IPv6-for-MAC-resolution.patch
+Patch161: 0002-Skip-IPv6-NS-packets-in-router-egress-SNAT-pipeline.patch
+Patch162: 0001-Fix-testsuite-85-ensure-one-gw-controller-restart-in.patch
+
+# Bug 1756945
+Patch170: 0001-ovn-northd-Fix-get_router_load_balancer_ips-for-mixe.patch
+Patch171: 0002-ovn-northd-Limit-ARP-ND-broadcast-domain-whenever-po.patch
+Patch172: 0003-ovn-northd-Avoid-empty-address-list-when-limiting-AR.patch
+
+# Bug 1779112
+# Bug 1778164
+# Bug 1779124
+Patch180: 0001-ovn-controller-Consider-non-virtual-ports-first-when.patch
+Patch181: 0001-ovn-controller-Add-missing-port-group-lflow-referenc.patch
+Patch182: 0001-ovn-controller-Add-command-to-trigger-an-I-P-full-re.patch
+
+# Bug 1779212
+Patch190: 0001-Add-support-to-Default-Router-Preference-PRF-RFC-419.patch
+Patch191: 0002-Add-support-for-Route-Info-Option-in-RA-RFC-4191.patch
+
+# Bug 1703162
+Patch200: 0001-OVN-Do-not-replace-router-port-mac-on-gateway-chassi.patch
+Patch201: 0002-OVN-Vlan-backed-DVR-N-S-redirect-type-option.patch
+Patch202: 0003-OVN-Vlan-backed-DVR-N-S-avoid-get_arp-on-non-redirec.patch
+Patch203: 0004-OVN-Vlan-backed-DVR-N-S-redirect-packet-via-localnet.patch
+Patch204: 0005-Replace-chassis-mac-with-router-port-mac-on-destinat.patch
+Patch205: 0006-OVN-ADD-nbctl-cli-to-mark-a-dnat_and_snat-rule-as-st.patch
+Patch206: 0007-OVN-Use-ip4.src-and-ip4.dst-actions-for-NAT-rules.patch
+Patch207: 0008-Fix-the-segfault-seen-in-ovn-controller-when-running.patch
+Patch208: 0009-ovn-northd-Add-support-for-Load-Balancer-health-chec.patch
+Patch209: 0010-Add-a-new-action-handle_svc_check.patch
+Patch210: 0011-Send-service-monitor-health-checks.patch
+Patch211: 0012-northd-Remove-misleading-warning-log-message.patch
+
+# Bug 1787319
+Patch215: 0001-ovn-controller-Refactor-I-P-engine_run-tracking.patch
+Patch216: 0002-ovn-controller-Add-per-node-states-to-I-P-engine.patch
+Patch217: 0003-ovn-controller-Add-separate-I-P-engine-node-for-proc.patch
+Patch218: 0004-ovn-controller-Fix-use-of-dangling-pointers-in-I-P-r.patch
+
+# Bug 1787361
+Patch220: 0001-ovn-controller-Run-I-P-engine-even-when-no-SB-txn-is.patch
+
+# Bug 1787000
+Patch230: 0001-pinctrl.c-Fix-maybe-uninitialized-warnings-with-old-.patch
+Patch231: 0002-DNSSL-copy-dnssl-string-in-order-to-avoid-truncated-.patch
+Patch232: 0003-RA-Route-Info-Option-copy-route-info-string-in-order.patch
+
+# Bug 1788456
+Patch240: 0001-ovn-controller-Don-t-monitor-connection-table-column.patch
+Patch241: 0002-Restrict-ARP-IPv6-ND-replies-for-LB-VIP-only-on-chas.patch
+
+# Bug 1791388
+Patch250: 0001-RHEL-only-Add-compatibility-with-openvswitch2.11-2.1.patch
+
+# Bug 1781223
+Patch260: 0001-nbctl-Log-the-source-of-duplicate-IP-addresses.patch
+Patch261: 0002-northd-Log-all-dynamic-address-assignments.patch
+Patch262: 0003-northd-Load-config-before-processing-nbdb-contents.patch
+Patch263: 0004-tests-Updated-expected-log-message.patch
+
+# Bug 1703162
+Patch270: 0001-ovn-northd-Fix-Pre-LB-logical-flows-with-IPv4-and-IP.patch
+Patch271: 0002-ovn-northd-Consider-load-balancer-active-backends-in.patch
+
+# Bug 1795697
+Patch280: 0001-ovn-northd-Address-scale-issues-with-DNAT-flows.patch
+
+# Bug 1797873
+Patch290: 0001-controller-grant-cap_net_admin-to-ovn-controller.patch
+
+# Bug 1788193
+Patch300: 0001-pinctrl-fix-IP-buffering-with-connection-tracking.patch
+Patch301: 0002-Manage-ARP-process-locally-in-a-DVR-scenario.patch
+
+# Bug 1811119
+Patch310: 0001-Broadcast-DHCPREPLY-when-BROADCAST-flag-is-set.patch
+
+# Bug 1813050
+Patch320: 0001-ovn-northd-Add-lflows-to-by-pass-the-svc-monitor-pac.patch
+
+# Bug 1805709
+Patch330: 0001-ovn-northd-Don-t-add-arp-responder-flows-for-lports-.patch
+
+# Bug 1816620
+Patch340: 0001-ovn-northd-Forward-ARP-requests-on-localnet-ports.patch
+Patch341: 0002-ovn.at-Fix-ARP-test-that-fails-due-to-timing.patch
+
+# Bug 1815316
+Patch350: 0001-controller-use-LLA-IPv6-address-as-NS-source-address.patch
+
+# Bug 1818844
+Patch360: 0001-ovn-controller-Fix-potential-segfault-with-virtual-p.patch
+Patch361: 0001-ovn-controller-Skip-vport-bindings-done-through-OVS-.patch
+
+# Bug 1803005
+Patch370: 0001-ovn-ctl-Provide-the-option-to-configure-inactive-pro.patch
+
+# Bug 1814127
+Patch380: 0001-ovn-northd-Fix-IP-local-multicast-flooding.patch
+
+# Bug 1825483
+Patch390: 0001-pinctrl-Handle-service-monitors-even-if-the-lport-do.patch
+
+# Bug 1827526
+Patch400: 0001-northd-Allow-64-after-ipv6_prefix.patch
+
+# Bug 1835750
+Patch410: 0001-Make-is_switch-in-lflow.c-a-util-function.patch
+Patch411: 0002-ovn-controller-Minimize-SB-DB-port_binding-lookups.patch
+Patch412: 0003-ovn-controller.c-Fix-memory-leak-of-local_datapath-p.patch
+Patch413: 0004-ovn-controller-Remove-ports-from-struct-local_datapa.patch
+
+# Bug 1834433
+Patch420: 0001-ovn-northd-Remove-useless-flow-for-GW_REDIRECT.patch
+Patch421: 0002-Revert-Manage-ARP-process-locally-in-a-DVR-scenario.patch
+Patch422: 0003-controller-fix-ip-buffering-with-static-routes.patch
+Patch423: 0004-northd-manage-ARP-request-locally-for-FIP-traffic.patch
+
+# Bug 1848401
+Patch430: 0001-ovsdb-idl-Try-committing-the-pending-txn-in-ovsdb_id.patch
+Patch431: 0002-northd-By-pass-IPv6-Router-Adv-and-Router-Solicitati.patch
+
+# Bug 1801734
+Patch440: 0001-pinctrl-Support-DHCPRELEASE-and-DHCPINFORM-in-native.patch
+
+# Bug 1804595
+patch450: 0001-Honour-router_preference-for-solicited-RA.patch
+
+# Bug 1848818
+Patch460: 0001-Rely-on-unique-name-for-ovn-qos-meters.patch
+
+# Bug 1846849
+Patch470: 0001-DNS-Make-DNS-lookups-case-insensitive.patch
+
+# Bug 1825701
+Patch480: 0001-northd-Fix-IPAM-IPv4-start-address-calculation.patch
+
+# FIXME Sphinx is used to generate some manpages, unfortunately, on RHEL, it's
+# in the -optional repository and so we can't require it directly since RHV
+# doesn't have the -optional repository enabled and so TPS fails
+%if %{external_sphinx}
+BuildRequires: %{_py}-sphinx
+%else
+# Sphinx dependencies
+BuildRequires: %{_py}-devel
+BuildRequires: %{_py}-setuptools
+#BuildRequires: %{_py}-docutils
+BuildRequires: %{_py}-jinja2
+BuildRequires: %{_py}-nose
+#BuildRequires: %{_py}-pygments
+# docutils dependencies
+BuildRequires: %{_py}-imaging
+# pygments dependencies
+BuildRequires: %{_py}-nose
+%endif
+
+BuildRequires: gcc gcc-c++ make
+BuildRequires: autoconf automake libtool
+BuildRequires: systemd-units openssl openssl-devel
+%if %{with_python3}
+BuildRequires: python3-devel python3-six python3-setuptools
+%endif
+%if %{with_python2}
+BuildRequires: %{_py2}-devel %{_py2}-six %{_py2}-setuptools
+%endif
+BuildRequires: desktop-file-utils
+BuildRequires: groff-base graphviz
+BuildRequires: unbound-devel
+
+# make check dependencies
+BuildRequires: procps-ng
+%if %{with_python2}
+BuildRequires: pyOpenSSL
+%else
+BuildRequires: python3-pyOpenSSL
+%endif
+
+%if %{with libcapng}
+BuildRequires: libcap-ng libcap-ng-devel
+%endif
+
+Requires: hostname openssl iproute module-init-tools
+
+Requires(post): systemd-units
+Requires(preun): systemd-units
+Requires(postun): systemd-units
+
+# to skip running checks, pass --without check
+%bcond_without check
+
+%description
+OVN, the Open Virtual Network, is a system to support virtual network
+abstraction.  OVN complements the existing capabilities of OVS to add
+native support for virtual network abstractions, such as virtual L2 and L3
+overlays and security groups.
+
+%package central
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Requires: firewalld-filesystem
+Provides: openvswitch%{pkgver}-ovn-central = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-central < 2.11.0-1
+
+%description central
+OVN DB servers and ovn-northd running on a central node.
+
+%package host
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Requires: firewalld-filesystem
+Provides: openvswitch%{pkgver}-ovn-host = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-host < 2.11.0-1
+
+%description host
+OVN controller running on each host.
+
+%package vtep
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname}
+Provides: openvswitch%{pkgver}-ovn-vtep = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-vtep < 2.11.0-1
+
+%description vtep
+OVN vtep controller
+
+%if %{with ovn_docker}
+%package docker
+Summary: Open Virtual Network support
+License: ASL 2.0
+Requires: %{pkgname} %{_py2}-openvswitch
+Provides: openvswitch%{pkgver}-ovn-docker = %{?epoch:%{epoch}:}%{version}-%{release}
+Obsoletes: openvswitch%{pkgver}-ovn-docker < 2.11.0-1
+
+%description docker
+Docker network plugins for OVN.
+%endif
+
+%prep
+%autosetup -N -c
+%if 0%{?commit0:1}
+find ovs-%{commit0}/ -maxdepth 1 -mindepth 1 -exec mv -t . {} +
+rmdir ovs-%{commit0}/
+%else
+find openvswitch-%{version}/ -maxdepth 1 -mindepth 1 -exec mv -t . {} +
+rmdir openvswitch-%{version}/
+%endif
+%autopatch -p 1
+%if ! %{external_sphinx}
+%setup -q -D -T -a 100 -a 101 -a 102
+%endif
+
+%build
+# Build Sphinx on RHEL
+%if ! %{external_sphinx}
+export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}%{_builddir}/pytmp/lib/python"
+for x in docutils-%{docutilsver} Pygments-%{pygmentsver} Sphinx-%{sphinxver}; do
+    pushd "$x"
+    %{_py} setup.py install --home %{_builddir}/pytmp
+    popd
+done
+
+export PATH="$PATH:%{_builddir}/pytmp/bin"
+%endif
+
+%if 0%{?commit0:1}
+# fix the snapshot unreleased version to be the released one.
+sed -i.old -e "s/^AC_INIT(openvswitch,.*,/AC_INIT(openvswitch, %{version},/" configure.ac
+%endif
+./boot.sh
+
+%configure \
+%if %{with libcapng}
+        --enable-libcapng \
+%else
+        --disable-libcapng \
+%endif
+        --enable-ssl \
+        --with-pkidir=%{_sharedstatedir}/openvswitch/pki
+make %{?_smp_mflags}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make install DESTDIR=$RPM_BUILD_ROOT
+
+for service in ovn-controller ovn-controller-vtep ovn-northd; do
+        install -p -D -m 0644 \
+                        rhel/usr_lib_systemd_system_${service}.service \
+                        $RPM_BUILD_ROOT%{_unitdir}/${service}.service
+done
+
+rm -rf $RPM_BUILD_ROOT/%{_datadir}/openvswitch/python/
+
+install -d -m 0755 $RPM_BUILD_ROOT/%{_sharedstatedir}/openvswitch
+
+install -d $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/
+install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-central-firewall-service.xml \
+        $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ovn-central-firewall-service.xml
+install -p -m 0644 rhel/usr_lib_firewalld_services_ovn-host-firewall-service.xml \
+        $RPM_BUILD_ROOT%{_prefix}/lib/firewalld/services/ovn-host-firewall-service.xml
+
+install -d -m 0755 $RPM_BUILD_ROOT%{_prefix}/lib/ocf/resource.d/ovn
+ln -s %{_datadir}/openvswitch/scripts/ovndb-servers.ocf \
+      $RPM_BUILD_ROOT%{_prefix}/lib/ocf/resource.d/ovn/ovndb-servers
+
+# remove OVS unpackages files
+rm -f $RPM_BUILD_ROOT%{_bindir}/ovs*
+rm -f $RPM_BUILD_ROOT%{_bindir}/vtep-ctl
+rm -f $RPM_BUILD_ROOT%{_sbindir}/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man1/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man5/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man5/vtep*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man7/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man8/ovs*
+rm -f $RPM_BUILD_ROOT%{_mandir}/man8/vtep*
+rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/ovs*
+rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/vswitch.ovsschema
+rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/vtep.ovsschema
+rm -f $RPM_BUILD_ROOT%{_datadir}/openvswitch/scripts/ovs*
+rm -rf $RPM_BUILD_ROOT%{_datadir}/openvswitch/bugtool-plugins
+rm -f $RPM_BUILD_ROOT%{_includedir}/openvswitch/*
+rm -f $RPM_BUILD_ROOT%{_includedir}/openflow/*
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.a
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+rm -f $RPM_BUILD_ROOT%{_libdir}/pkgconfig/*.pc
+rm -f $RPM_BUILD_ROOT%{_includedir}/openvswitch/*
+rm -f $RPM_BUILD_ROOT%{_includedir}/openflow/*
+rm -f $RPM_BUILD_ROOT%{_includedir}/ovn/*
+rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-appctl-bashcomp.bash
+rm -f $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/ovs-vsctl-bashcomp.bash
+rm -rf $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/openvswitch
+
+%if %{without ovn_docker}
+rm -f $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-overlay-driver \
+        $RPM_BUILD_ROOT/%{_bindir}/ovn-docker-underlay-driver
+%endif
+
+%check
+%if %{with check}
+    touch resolv.conf
+    export OVS_RESOLV_CONF=$(pwd)/resolv.conf
+    if make check TESTSUITEFLAGS='%{_smp_mflags} -k ovn' ||
+       make check TESTSUITEFLAGS='--recheck -k ovn'; then :;
+    else
+        cat tests/testsuite.log
+        # Presently a test case - "2796: ovn -- ovn-controller incremental processing"
+        # is failing on aarch64 arch. Let's not exit for this arch
+        # until we figure out why it is failing.
+        %ifnarch aarch64
+            exit 1
+        %endif
+    fi
+%endif
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre central
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-northd.service &>/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-central > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-northd service is running which means old openvswitch-ovn-central
+        # is already installed and it will be cleaned up. So start ovn-northd
+        # service when posttrans central is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-northd
+    fi
+fi
+
+%pre host
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-controller.service &>/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-host > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-controller service is running which means old
+        # openvswitch-ovn-host is installed and it will be cleaned up. So
+        # start ovn-controller service when posttrans host is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-controller
+    fi
+fi
+
+%pre vtep
+if [ $1 -eq 1 ] ; then
+    # Package install.
+    /bin/systemctl status ovn-controller-vtep.service >/dev/null
+    ovn_status=$?
+    rpm -ql openvswitch-ovn-vtep > /dev/null
+    if [[ "$?" = "0" && "$ovn_status" = "0" ]]; then
+        # ovn-controller-vtep service is running which means old
+        # openvswitch-ovn-vtep is installed and it will be cleaned up. So
+        # start ovn-controller-vtep service when posttrans host is called.
+        touch %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
+    fi
+fi
+
+%preun central
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-northd.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-northd.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-northd.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%preun host
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-controller.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-controller.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-controller.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%preun vtep
+%if 0%{?systemd_preun:1}
+    %systemd_preun ovn-controller-vtep.service
+%else
+    if [ $1 -eq 0 ] ; then
+        # Package removal, not upgrade
+        /bin/systemctl --no-reload disable ovn-controller-vtep.service >/dev/null 2>&1 || :
+        /bin/systemctl stop ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%post central
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-northd.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%post host
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-controller.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%post vtep
+%if 0%{?systemd_post:1}
+    %systemd_post ovn-controller-vtep.service
+%else
+    # Package install, not upgrade
+    if [ $1 -eq 1 ]; then
+        /bin/systemctl daemon-reload >dev/null || :
+    fi
+%endif
+
+%postun
+
+%postun central
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-northd.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+    # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-northd.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%postun host
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-controller.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+        # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-controller.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%postun vtep
+%if 0%{?systemd_postun_with_restart:1}
+    %systemd_postun_with_restart ovn-controller-vtep.service
+%else
+    /bin/systemctl daemon-reload >/dev/null 2>&1 || :
+    if [ "$1" -ge "1" ] ; then
+        # Package upgrade, not uninstall
+        /bin/systemctl try-restart ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+%endif
+
+%posttrans central
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-northd ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-northd
+        /bin/systemctl start ovn-northd.service >/dev/null 2>&1 || :
+    fi
+fi
+
+
+%posttrans host
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-controller
+        /bin/systemctl start ovn-controller.service >/dev/null 2>&1 || :
+    fi
+fi
+
+%posttrans vtep
+if [ $1 -eq 1 ]; then
+    # Package install, not upgrade
+    if [ -e %{_localstatedir}/lib/rpm-state/ovn-controller-vtep ]; then
+        rm %{_localstatedir}/lib/rpm-state/ovn-controller-vtep
+        /bin/systemctl start ovn-controller-vtep.service >/dev/null 2>&1 || :
+    fi
+fi
+
+%files
+%{_bindir}/ovn-nbctl
+%{_bindir}/ovn-sbctl
+%{_bindir}/ovn-trace
+%{_bindir}/ovn-detrace
+%{_datadir}/openvswitch/scripts/ovn-ctl
+%{_datadir}/openvswitch/scripts/ovndb-servers.ocf
+%{_mandir}/man8/ovn-ctl.8*
+%{_mandir}/man8/ovn-nbctl.8*
+%{_mandir}/man8/ovn-trace.8*
+%{_mandir}/man1/ovn-detrace.1*
+%{_mandir}/man7/ovn-architecture.7*
+%{_mandir}/man8/ovn-sbctl.8*
+%{_mandir}/man5/ovn-nb.5*
+%{_mandir}/man5/ovn-sb.5*
+%{_prefix}/lib/ocf/resource.d/ovn/ovndb-servers
+%if %{with_python2}
+%{_datadir}/openvswitch/scripts/ovn-bugtool-nbctl-show
+%{_datadir}/openvswitch/scripts/ovn-bugtool-sbctl-lflow-list
+%{_datadir}/openvswitch/scripts/ovn-bugtool-sbctl-show
+%endif
+
+%if %{with ovn_docker}
+%files docker
+%{_bindir}/ovn-docker-overlay-driver
+%{_bindir}/ovn-docker-underlay-driver
+%endif
+
+%files central
+%{_bindir}/ovn-northd
+%{_mandir}/man8/ovn-northd.8*
+%config %{_datadir}/openvswitch/ovn-nb.ovsschema
+%config %{_datadir}/openvswitch/ovn-sb.ovsschema
+%{_unitdir}/ovn-northd.service
+%{_prefix}/lib/firewalld/services/ovn-central-firewall-service.xml
+
+%files host
+%{_bindir}/ovn-controller
+%{_mandir}/man8/ovn-controller.8*
+%{_unitdir}/ovn-controller.service
+%{_prefix}/lib/firewalld/services/ovn-host-firewall-service.xml
+
+%files vtep
+%{_bindir}/ovn-controller-vtep
+%{_mandir}/man8/ovn-controller-vtep.8*
+%{_unitdir}/ovn-controller-vtep.service
+
+%changelog
+* Fri Jul 10 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-53
+- Backport "northd: Fix IPAM IPv4 start address calculation" (#1825701)
+
+* Thu Jul 9 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-52
+- Backport "DNS: Make DNS lookups case insensitive" (#1846849)
+
+* Thu Jul 9 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-51
+- Backport "Rely on unique name for ovn qos meters" (#1848818)
+
+* Thu Jun 25 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-50
+- Backport "Honour router_preference for solicited RA" (#1804595)
+
+* Thu Jun 25 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-48
+- Backport "pinctrl: Support DHCPRELEASE and DHCPINFORM in native OVN dhcp responder." (#1801734)
+
+* Thu Jun 18 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-47
+- Backport "northd: By pass IPv6 Router Adv and Router Solicitation packets from ACL stages." (#1848401)
+- Backport "ovsdb idl: Try committing the pending txn in  ovsdb_idl_loop_run."
+
+* Wed May 27 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-47
+- Backport "ovn-northd: Remove useless flow for GW_REDIRECT" (#1834433)
+- Backport "Revert "Manage ARP process locally in a DVR scenario" (#1834433)
+- Backport "controller: fix ip buffering with static routes" (#1834433)
+- Backport "northd: manage ARP request locally for FIP traffic" (#1834433)
+
+* Thu May 14 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-46
+- Backport "Make is_switch() in lflow.c a util function" (#1835750)
+- Backport "ovn-controller: Minimize SB DB port_binding lookups." (#1835750)
+- Backport "ovn-controller.c: Fix memory leak of local_datapath->ports." (#1835750)
+- Backport "ovn-controller: Remove ports from struct local_datapaths." (#1835750)
+
+* Sat May 09 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-45
+- Backport "northd: Allow /64 after ipv6_prefix" (#1827526)
+
+* Sat Apr 18 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-44
+- Backport "pinctrl: Handle service monitors even if the lport doesn't have IPv4 addresses set." (#1825483)
+
+* Wed Apr 15 2020 Dumitru Ceara <dceara@redhat.com> - 2.11.1-43
+- Backport "ovn-northd: Fix IP local multicast flooding.". (#1814127)
+
+* Fri Apr 10 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-42
+- Backport "ovn-ctl: Provide the option to configure inactive probe from standby to active". (#1803005)
+
+* Thu Apr 09 2020 Dumitru Ceara <dceara@redhat.com> - 2.11.1-41
+- Backport "ovn-controller: Fix potential segfault with "virtual" port bindings." (#1818844)
+- Backport "ovn-controller: Skip vport bindings done through OVS external_ids:iface-id." (#1818844)
+
+* Thu Apr 02 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-40
+- Backport "controller: use LLA IPv6 address as NS source address" (#1815316)
+
+* Tue Mar 24 2020 Dumitru Ceara <dceara@redhat.com> - 2.11.1-39
+- Backport "ovn-northd: Forward ARP requests on localnet ports." (#1816620)
+- Backport "ovn.at: Fix ARP test that fails due to timing." (#1816620)
+
+* Mon Mar 23 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-38
+- Backport "ovn-northd: Don't add arp responder flows for lports with 'unknown' address." (#1805709)
+
+* Fri Mar 13 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-37
+- Backport "ovn-northd: Add lflows to by pass the svc monitor packets from conntrack". (#1813050)
+
+* Fri Mar 6 2020 Ihar Hrachyshka <ihrachys@redhat.com> - 2.11.1-36
+- Backport "Broadcast DHCPREPLY when BROADCAST flag is set" (#1811119)
+
+* Mon Mar 2 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-35
+- Backport "pinctrl: fix IP buffering with connection-tracking" (#1788193)
+- Backport "Manage ARP process locally in a DVR scenario" (#1788193)
+
+* Fri Feb 28 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-34
+- Backport "controller: grant cap_net_admin to ovn-controller" (#1797873)
+
+* Tue Feb 4 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-33
+* Backport "ovn-northd: Address scale issues with DNAT flows." (1795697)
+
+* Thu Jan 30 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-32
+* Backport "[RFE] LB health check : ovn-northd: Consider load balancer active backends in router pipeline" (#1703162)
+
+* Fri Jan 24 2020 Dumitru Ceara <dceara@redhat.com> - 2.11.1-31
+- Backport "tests: Updated expected log message" (#1781223)
+- Backport "nbctl: Log the source of duplicate IP addresses" (#1781223)
+- Backport "northd: Log all dynamic address assignments" (#1781223)
+- Backport "northd: Load config before processing nbdb contents" (#1781223)
+
+* Thu Jan 16 2020 Timothy Redaelli <tredaelli@redhat.com> - 2.11.1-30
+- Add a RHEL-only patch to read the OVS_USER_ID from
+  /etc/openvswitch/default.conf and /etc/sysconfig/openvswitch.
+  This is needed for openvswitch2.11 < 2.11.0-43 compatibility (#1791388)
+
+* Fri Jan 10 2020 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-29
+- Backport "ovn-controller: Don't monitor connection table columns"
+- Backport "Restrict ARP/IPv6 ND replies for LB VIP only on chassis redirect port" (#1788456)
+
+* Thu Jan 09 2020 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-28
+- Backport "pinctrl.c: Fix maybe-uninitialized warnings with old GCC versions" (#1787000)
+- Backport "DNSSL: copy dnssl string in order to avoid truncated value" (#1787000)
+- Backport "RA Route Info Option: copy route info string in order to avoid truncated value" (#1787000)
+
+* Thu Jan 02 2020 Dumitru Ceara <dceara@redhat.com> - 2.11.1-27
+- Backport "ovn-controller: Run I-P engine even when no SB txn is available." (#1787361)
+
+* Thu Jan 02 2020 Dumitru Ceara <dceara@redhat.com> - 2.11.1-26
+- Backport "ovn-controller: Refactor I-P engine_run() tracking." (#1787318)
+- Backport "ovn-controller: Add per node states to I-P engine." (#1787318)
+- Backport "ovn-controller: Add separate I-P engine node for processing ct-zones." (#1787318)
+- Backport "ovn-controller: Fix use of dangling pointers in I-P runtime_data." (#1787318)
+
+* Thu Dec 19 2019 Mark Michelson <mmichels@redhat.com> - 2.11.1-25
+- Do not force crossproducts on string expressions (#1764032)
+
+* Thu Dec 05 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-24
+- Backport "[RFE] Support for load balancing health checks in OVN" (#1703162)
+- Backport "Stateless NAT support (backported due to conflicts)"
+- Backport "Ankur's Vlan backed DVR N-S patches (backported due to conflicts)"
+
+* Tue Dec 03 2019 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-23
+- Backport "Add support to Default Router Preference (PRF) - RFC4191" (#1779212)
+- Backport "Add support for Route Info Option in RA - RFC4191"
+
+* Tue Dec 3 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.1-22
+- Backport "ovn-controller: Consider non-virtual ports first when updating bindings." (#1779112)
+- Backport "ovn-controller: Add missing port group lflow references." (#1778164)
+- Backport "ovn-controller: Add command to trigger an I-P full recompute." (#1779124)
+
+* Tue Nov 26 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.1-21
+- Backport "ovn-northd: Fix get_router_load_balancer_ips() for  mixed address families."
+- Backport "ovn-northd: Limit ARP/ND broadcast domain whenever possible." (#1756945)
+- Backport "ovn-northd: Avoid empty address list when limiting ARP/ND broadcast." (#1756945)
+
+* Tue Nov 19 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.1-20
+- Backport "Skip IPv6 NS packets in router egress SNAT pipeline" (#1773605)
+- Backport "northd: Match IPv4 or IPv6 for MAC resolution"
+- Removed - Requires: openvswitch2.11
+
+* Tue Nov 12 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.1-19
+- Backport "Fix ha chassis failover issues for stale ha chassis entries" (#1762777)
+
+* Tue Nov 12 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.1-18
+- Backport "ovn-northd: Validate dnat_and_snat external_mac/logical_ip." (#1769709)
+
+* Mon Nov 11 2019 Mark Michelson <mmichels@redhat.com> - 2.11.1-17
+- Revert support for openvswitch2.12
+
+* Mon Nov 11 2019 Mark Michelson <mmichels@redhat.com> - 2.11.1-16
+- Update "Requires" to allow for openvswitch2.12
+
+* Mon Nov 11 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.1-15
+- Backport "lflow.c: Fix memory leak of lflow_ref_list_node->ref_name." (#1770953)
+
+* Tue Nov 05 2019 Mark Michelson <mmichels@redhat.com> - 2.11.1-14
+- Backport "Prevent erroneous duplicate IP address messages" (#1769043)
+
+* Mon Nov 04 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.1-13
+- Backport "Fix virtual port binding when the parents are scheduled in same chassis" (#1762341)
+
+* Mon Nov 04 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.1-12
+- Backport "Support IPv6 NAT" (#1768347)
+
+* Sun Nov 3 2019 Mark Michelson <mmichels@redhat.com> - 2.11.1-11
+- Backport "Revert conjunctive match removal patches" (#1764032)
+- Backport "Combine conjunctions with identical matches into one" (#1764032)
+
+* Wed Oct 30 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-10
+- Backport "Add DNSSL support to OVN" (#1764718)
+
+* Wed Oct 30 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-9
+- Backport "Add RDNSS support to OVN" (#1699332)
+
+* Wed Oct 16 2019  Dumitru Ceara <dceara@redhat.com> - 2.11.1-8
+- Backport "ovn-northd: Fix IP multicast flooding to mrouter." (#1757714)
+
+* Thu Oct 10 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.1-7
+- Fixed the patch apply errors.
+
+* Wed Oct 9 2019  Dumitru Ceara <dceara@redhat.com> - 2.11.1-6
+- Backport "ovn-northd: Use HMAP_FOR_EACH when adding multicast lflows" (#1757714)
+- Backport "ovn-northd: Add IGMP Relay support" (#1757714)
+- Backport "ovn-northd: Add static IP multicast flood configuration" (#1757715)
+
+* Wed Oct 2 2019  Dumitru Ceara <dceara@redhat.com> - 2.11.1-5
+- Backport "Learn the mac binding only if required" (#1729846)
+
+* Tue Oct 1 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-4
+- Backport "Introduce localnet egress QoS support" (#1580542)
+
+* Fri Sep 27 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.1-3
+- Backport " Disable conjunction by force cross product for all the fields." (#1756466)
+
+* Mon Sep 16 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.1-2
+- Backport "Northd: add empty_lb controller_event for logical router" (#1743577)
+
+* Sat Sep 14 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.1-1
+- Backport "Exclude inport and outport symbol tables from conjunction" (#1751942)
+
+* Fri Sep 06 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.1-0
+- Sync 2.11.1-0 with upstream 2.12.0 so that we have all the I-P series patches (#1748102)
+
+* Tue Sep 03 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-37
+- Backport firt 3 patches of I-P series (#1748102)
+
+* Fri Aug 23 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-36
+- Backport "Remove ageing check in run_put_mac_binding" (#1745002)
+
+* Fri Aug 23 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.0-35
+- Backport "pinctrl: Fix DNS packet parsing" (#1744991)
+
+* Fri Aug 23 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-34
+- Backport "ovn-controller: Provide the option to configure inactivity probe interval for OpenFlow conn" (#1744962)
+
+* Tue Aug 20 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-33
+- Backport "Make pidfile_is_running more robust against empty pidfiles" (#1741057)
+
+* Tue Aug 06 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-32
+- Backport "Support binding a logical port based on the GARP from the VIF" (#1699350)
+
+* Tue Aug 06 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-31
+- Backport "Fix default L4 default proto reported by ovn-nbctl" (#1734743)
+
+* Mon Aug 05 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-30
+- Backport "Don't emit ICMP need to frag on LRP with no IPv4 address" (#1737341)
+
+* Mon Aug 05 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-29
+- Revert Backport "Support binding a logical port based on the GARP from the VIF" (#1699350)
+
+* Mon Aug 05 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-28
+- Backport " Support binding a logical port based on the GARP from the VIF" (#1699350)
+
+* Mon Aug 05 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-27
+- Backport ovn-northd pause/resume support (#1720728)
+
+* Wed Jul 17 2019  Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-26
+- Backport ovn uindling support (#1730759)
+
+* Wed Jul 17 2019  Numan Siddique <nusiddiq@redhat.com> - 2.11.0-25
+- Backport related to GARP handling for router ips (#1561880)
+
+* Tue Jul 9 2019 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-24
+- Backport related to ip buffering with gw router port issue (#1728318)
+
+* Mon Jul 1 2019 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-23
+- Backport "OVN: add the possibility to specify tunnel dst port" (#1720371)
+
+* Mon Jun 24 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.0-22
+- Backport "ovn-controller: Fix parsing of OVN tunnel IDs" (#1708131)
+
+* Mon Jun 24 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-21
+- Backport "ovn-nbctl.8.xml: Fix typo." (#1720194)
+
+* Mon Jun 17 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-20
+- Backport "ovn: Add support for DHCP option 15 - domain name" (#1721012)
+
+* Thu Jun 06 2019 Timothy Redaelli <tredaelli@redhat.com> - 2.11.0-19
+- Avoid collisions during installation of sources in debuginfo package (#1717933)
+
+* Mon May 27 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.0-18
+- Backport "ovn: Properly set the index for chassis lookup" (#1698462)
+
+* Tue May 14 2019 Dumitru Ceara <dceara@redhat.com> - 2.11.0-17
+- Backport "stopwatch: Free stopwatch packets after processing" (#1698462)
+
+* Fri Apr 26 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-16
+- Fix the ovn-northd sync issue with HA_Chassis group (#1666673)
+
+* Wed Apr 24 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-15
+- Backport "RFE: Limit Geneve to within Transport Zones" (#1702550)
+
+* Wed Apr 24 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-14
+- Backport "RFE: [OVN] Fragmentation support" (#1702331)
+
+* Sat Apr 20 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-13
+- Backport "RFE: OVN - Support Logical ports of type external" (#1666673)
+
+* Thu Apr 18 2019 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-12
+- Backport "OVN: fix DVR Floating IP support" (#1701183)
+
+* Wed Apr 17 2019 Timothy Redaelli <tredaelli@redhat.com> - 2.11.0-11
+- Add 'Obsoletes' to the old OVN openvswitch2.11 sub-packages to allow to upgrade from FDP.A
+
+* Wed Apr 17 2019 Lorenzo Bianconi <lorenzo.bianconi@redhat.com> - 2.11.0-10
+- Backport "OVN: add the possibility to configure a static IPv4/IPv6 address and dynamic MAC"
+
+* Tue Apr 16 2019 Timothy Redaelli <tredaelli@redhat.com> - 2.11.0-9
+- Remove 'Obsoletes' lines like on openvswitch2.11 package
+
+* Tue Apr 16 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-8
+- Fix the 'Provides' to include '%pkgver'
+
+* Tue Mar 26 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-7
+- Backport Fixes for #1677616 (pinctrl thread) and fixes related to IPv6 RA."
+
+* Tue Mar 19 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-6
+- Removed ovn-common package and moved all the related files to main 'ovn' package.
+
+* Tue Mar 19 2019 Timothy Redaelli <tredaelli@redhat.com> - 2.11.0-5
+- Disable ovn-docker subpackage by default
+
+* Thu Mar 07 2019 Mark Michelson <mmichels@redhat.com> - 2.11.0-4
+- Backport "OVN: Add port addresses to IPAM after all ports are joined."
+
+* Sat Mar 02 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-3
+- Backport "ovn-controller: Provide the option to set the datapath-type of br-int" (#1684796)
+
+* Fri Mar 01 2019 Timothy Redaelli <tredaelli@redhat.com> - 2.11.0-2
+- Backport "rhel: Use PIDFile on forking systemd service files" (#1684477)
+
+* Thu Feb 28 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-1
+- Update to official 2.11 release
+
+* Wed Jan 23 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-0.20190117git4e4f80e
+- Update to OVS 2.11 branch
+
+* Thu Jan 17 2019 Numan Siddique <nusiddiq@redhat.com> - 2.11.0-0.20190117gitab66223
+- Update to a snapshot of OVS 2.11 from master
+
+* Thu Dec 20 2018 Numan Siddique <nusiddiq@redhat.com>
+- OVS/OVN split.