5f9769
From 8788ac191a4e0689f0287695c181fe1a781b0d31 Mon Sep 17 00:00:00 2001
5f9769
From: Dumitru Ceara <dceara@redhat.com>
5f9769
Date: Fri, 15 Jan 2021 19:26:13 +0100
5f9769
Subject: [PATCH 1/2] Support configuring Load Balancer hairpin source IP.
5f9769
5f9769
In case traffic that gets load balanced is DNAT-ed to a backend IP that
5f9769
happens to be the source of the traffic then OVN performs an additional
5f9769
SNAT to ensure that return traffic is directed through OVN.
5f9769
5f9769
Until now the load balancer VIP was chosen as SNAT IP.  However, in
5f9769
specific scenarios, the CMS may prefer a different IP, e.g., a single
5f9769
cluster-wide IP.  This commit adds support, through the newly added
5f9769
Load_Balancer.option 'hairpin_snat_ip', to allow the CMS to explicitly
5f9769
chose a SNAT IP.
5f9769
5f9769
Due to the fact that now traffic that was hairpinned might need to be
5f9769
SNAT-ed to different IPs for different load balancers that share the
5f9769
same VIP address value we need to also explicitly match on L4 protocol
5f9769
and ports in the 'OFTABLE_CT_SNAT_FOR_VIP' table.
5f9769
5f9769
Signed-off-by: Dumitru Ceara <dceara@redhat.com>
5f9769
Signed-off-by: Numan Siddique <numans@ovn.org>
5f9769
(cherry picked from upstream commit cc4d5520064f294d2b011be10ec5ff5f1a85bfd0)
5f9769
5f9769
Conflicts:
5f9769
	NEWS
5f9769
5f9769
Change-Id: Ie5ba5d9f3811ee577377e3e2cd700d2949a174da
5f9769
---
5f9769
 NEWS                |   3 +
5f9769
 controller/lflow.c  |  53 +++++++++------
5f9769
 lib/lb.c            |  26 ++++++++
5f9769
 lib/lb.h            |  11 ++++
5f9769
 lib/ovn-util.c      |  21 ++++++
5f9769
 lib/ovn-util.h      |   1 +
5f9769
 northd/ovn-northd.c |   9 +--
5f9769
 ovn-nb.xml          |   8 +++
5f9769
 ovn-sb.ovsschema    |   9 ++-
5f9769
 ovn-sb.xml          |   8 +++
5f9769
 tests/ovn-northd.at |   6 ++
5f9769
 tests/ovn.at        | 182 ++++++++++++++++++++++++++++++++++++++--------------
5f9769
 12 files changed, 261 insertions(+), 76 deletions(-)
5f9769
5f9769
diff --git a/NEWS b/NEWS
5f9769
index e89c5f4..57a9ba9 100644
5f9769
--- a/NEWS
5f9769
+++ b/NEWS
5f9769
@@ -10,6 +10,9 @@ Post-v20.12.0
5f9769
     "ovn-installed".  This external-id is set by ovn-controller only after all
5f9769
     openflow operations corresponding to the OVS interface being added have
5f9769
     been processed.
5f9769
+  - Add a new option to Load_Balancer.options, "hairpin_snat_ip", to allow
5f9769
+    users to explicitly select which source IP should be used for load
5f9769
+    balancer hairpin traffic.
5f9769
 
5f9769
 OVN v20.12.0 - 18 Dec 2020
5f9769
 --------------------------
5f9769
diff --git a/controller/lflow.c b/controller/lflow.c
5f9769
index 9f6aece..946c1e0 100644
5f9769
--- a/controller/lflow.c
5f9769
+++ b/controller/lflow.c
5f9769
@@ -1189,26 +1189,30 @@ add_lb_vip_hairpin_flows(struct ovn_controller_lb *lb,
5f9769
     struct match hairpin_reply_match = MATCH_CATCHALL_INITIALIZER;
5f9769
 
5f9769
     if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
5f9769
-        ovs_be32 ip4 = in6_addr_get_mapped_ipv4(&lb_backend->ip);
5f9769
+        ovs_be32 bip4 = in6_addr_get_mapped_ipv4(&lb_backend->ip);
5f9769
+        ovs_be32 vip4 = lb->hairpin_snat_ips.n_ipv4_addrs
5f9769
+                        ? lb->hairpin_snat_ips.ipv4_addrs[0].addr
5f9769
+                        : in6_addr_get_mapped_ipv4(&lb_vip->vip);
5f9769
 
5f9769
         match_set_dl_type(&hairpin_match, htons(ETH_TYPE_IP));
5f9769
-        match_set_nw_src(&hairpin_match, ip4);
5f9769
-        match_set_nw_dst(&hairpin_match, ip4);
5f9769
-
5f9769
-        match_set_dl_type(&hairpin_reply_match,
5f9769
-                          htons(ETH_TYPE_IP));
5f9769
-        match_set_nw_src(&hairpin_reply_match, ip4);
5f9769
-        match_set_nw_dst(&hairpin_reply_match,
5f9769
-                         in6_addr_get_mapped_ipv4(&lb_vip->vip));
5f9769
+        match_set_nw_src(&hairpin_match, bip4);
5f9769
+        match_set_nw_dst(&hairpin_match, bip4);
5f9769
+
5f9769
+        match_set_dl_type(&hairpin_reply_match, htons(ETH_TYPE_IP));
5f9769
+        match_set_nw_src(&hairpin_reply_match, bip4);
5f9769
+        match_set_nw_dst(&hairpin_reply_match, vip4);
5f9769
     } else {
5f9769
+        struct in6_addr *bip6 = &lb_backend->ip;
5f9769
+        struct in6_addr *vip6 = lb->hairpin_snat_ips.n_ipv6_addrs
5f9769
+                                ? &lb->hairpin_snat_ips.ipv6_addrs[0].addr
5f9769
+                                : &lb_vip->vip;
5f9769
         match_set_dl_type(&hairpin_match, htons(ETH_TYPE_IPV6));
5f9769
-        match_set_ipv6_src(&hairpin_match, &lb_backend->ip);
5f9769
-        match_set_ipv6_dst(&hairpin_match, &lb_backend->ip);
5f9769
+        match_set_ipv6_src(&hairpin_match, bip6);
5f9769
+        match_set_ipv6_dst(&hairpin_match, bip6);
5f9769
 
5f9769
-        match_set_dl_type(&hairpin_reply_match,
5f9769
-                          htons(ETH_TYPE_IPV6));
5f9769
-        match_set_ipv6_src(&hairpin_reply_match, &lb_backend->ip);
5f9769
-        match_set_ipv6_dst(&hairpin_reply_match, &lb_vip->vip);
5f9769
+        match_set_dl_type(&hairpin_reply_match, htons(ETH_TYPE_IPV6));
5f9769
+        match_set_ipv6_src(&hairpin_reply_match, bip6);
5f9769
+        match_set_ipv6_dst(&hairpin_reply_match, vip6);
5f9769
     }
5f9769
 
5f9769
     if (lb_backend->port) {
5f9769
@@ -1254,6 +1258,7 @@ add_lb_vip_hairpin_flows(struct ovn_controller_lb *lb,
5f9769
 static void
5f9769
 add_lb_ct_snat_vip_flows(struct ovn_controller_lb *lb,
5f9769
                          struct ovn_lb_vip *lb_vip,
5f9769
+                         uint8_t lb_proto,
5f9769
                          struct ovn_desired_flow_table *flow_table)
5f9769
 {
5f9769
     uint64_t stub[1024 / 8];
5f9769
@@ -1277,10 +1282,16 @@ add_lb_ct_snat_vip_flows(struct ovn_controller_lb *lb,
5f9769
 
5f9769
     if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
5f9769
         nat->range_af = AF_INET;
5f9769
-        nat->range.addr.ipv4.min = in6_addr_get_mapped_ipv4(&lb_vip->vip);
5f9769
+        nat->range.addr.ipv4.min =
5f9769
+            lb->hairpin_snat_ips.n_ipv4_addrs
5f9769
+            ? lb->hairpin_snat_ips.ipv4_addrs[0].addr
5f9769
+            : in6_addr_get_mapped_ipv4(&lb_vip->vip);
5f9769
     } else {
5f9769
         nat->range_af = AF_INET6;
5f9769
-        nat->range.addr.ipv6.min = lb_vip->vip;
5f9769
+        nat->range.addr.ipv6.min
5f9769
+            = lb->hairpin_snat_ips.n_ipv6_addrs
5f9769
+            ? lb->hairpin_snat_ips.ipv6_addrs[0].addr
5f9769
+            : lb_vip->vip;
5f9769
     }
5f9769
     ofpacts.header = ofpbuf_push_uninit(&ofpacts, nat_offset);
5f9769
     ofpact_finish(&ofpacts, &ct->ofpact);
5f9769
@@ -1288,12 +1299,16 @@ add_lb_ct_snat_vip_flows(struct ovn_controller_lb *lb,
5f9769
     struct match match = MATCH_CATCHALL_INITIALIZER;
5f9769
     if (IN6_IS_ADDR_V4MAPPED(&lb_vip->vip)) {
5f9769
         match_set_dl_type(&match, htons(ETH_TYPE_IP));
5f9769
-        match_set_ct_nw_dst(&match, nat->range.addr.ipv4.min);
5f9769
+        match_set_ct_nw_dst(&match, in6_addr_get_mapped_ipv4(&lb_vip->vip));
5f9769
     } else {
5f9769
         match_set_dl_type(&match, htons(ETH_TYPE_IPV6));
5f9769
         match_set_ct_ipv6_dst(&match, &lb_vip->vip);
5f9769
     }
5f9769
 
5f9769
+    match_set_nw_proto(&match, lb_proto);
5f9769
+    match_set_ct_nw_proto(&match, lb_proto);
5f9769
+    match_set_ct_tp_dst(&match, htons(lb_vip->vip_port));
5f9769
+
5f9769
     uint32_t ct_state = OVS_CS_F_TRACKED | OVS_CS_F_DST_NAT;
5f9769
     match_set_ct_state_masked(&match, ct_state, ct_state);
5f9769
 
5f9769
@@ -1349,7 +1364,7 @@ consider_lb_hairpin_flows(const struct sbrec_load_balancer *sbrec_lb,
5f9769
                                      flow_table);
5f9769
         }
5f9769
 
5f9769
-        add_lb_ct_snat_vip_flows(lb, lb_vip, flow_table);
5f9769
+        add_lb_ct_snat_vip_flows(lb, lb_vip, lb_proto, flow_table);
5f9769
     }
5f9769
 
5f9769
     ovn_controller_lb_destroy(lb);
5f9769
diff --git a/lib/lb.c b/lib/lb.c
5f9769
index 2517c02..e11ac00 100644
5f9769
--- a/lib/lb.c
5f9769
+++ b/lib/lb.c
5f9769
@@ -170,6 +170,24 @@ void ovn_northd_lb_vip_destroy(struct ovn_northd_lb_vip *vip)
5f9769
     free(vip->backends_nb);
5f9769
 }
5f9769
 
5f9769
+static void
5f9769
+ovn_lb_get_hairpin_snat_ip(const struct uuid *lb_uuid,
5f9769
+                           const struct smap *lb_options,
5f9769
+                           struct lport_addresses *hairpin_addrs)
5f9769
+{
5f9769
+    const char *addresses = smap_get(lb_options, "hairpin_snat_ip");
5f9769
+
5f9769
+    if (!addresses) {
5f9769
+        return;
5f9769
+    }
5f9769
+
5f9769
+    if (!extract_ip_address(addresses, hairpin_addrs)) {
5f9769
+        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
5f9769
+        VLOG_WARN_RL(&rl, "bad hairpin_snat_ip %s in load balancer "UUID_FMT,
5f9769
+                     addresses, UUID_ARGS(lb_uuid));
5f9769
+    }
5f9769
+}
5f9769
+
5f9769
 struct ovn_northd_lb *
5f9769
 ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb,
5f9769
                      struct hmap *ports,
5f9769
@@ -224,6 +242,9 @@ ovn_northd_lb_create(const struct nbrec_load_balancer *nbrec_lb,
5f9769
         ds_chomp(&sel_fields, ',');
5f9769
         lb->selection_fields = ds_steal_cstr(&sel_fields);
5f9769
     }
5f9769
+
5f9769
+    ovn_lb_get_hairpin_snat_ip(&nbrec_lb->header_.uuid, &nbrec_lb->options,
5f9769
+                               &lb->hairpin_snat_ips);
5f9769
     return lb;
5f9769
 }
5f9769
 
5f9769
@@ -260,6 +281,7 @@ ovn_northd_lb_destroy(struct ovn_northd_lb *lb)
5f9769
     free(lb->vips);
5f9769
     free(lb->vips_nb);
5f9769
     free(lb->selection_fields);
5f9769
+    destroy_lport_addresses(&lb->hairpin_snat_ips);
5f9769
     free(lb->dps);
5f9769
     free(lb);
5f9769
 }
5f9769
@@ -289,6 +311,9 @@ ovn_controller_lb_create(const struct sbrec_load_balancer *sbrec_lb)
5f9769
      * correct value.
5f9769
      */
5f9769
     lb->n_vips = n_vips;
5f9769
+
5f9769
+    ovn_lb_get_hairpin_snat_ip(&sbrec_lb->header_.uuid, &sbrec_lb->options,
5f9769
+                               &lb->hairpin_snat_ips);
5f9769
     return lb;
5f9769
 }
5f9769
 
5f9769
@@ -299,5 +324,6 @@ ovn_controller_lb_destroy(struct ovn_controller_lb *lb)
5f9769
         ovn_lb_vip_destroy(&lb->vips[i]);
5f9769
     }
5f9769
     free(lb->vips);
5f9769
+    destroy_lport_addresses(&lb->hairpin_snat_ips);
5f9769
     free(lb);
5f9769
 }
5f9769
diff --git a/lib/lb.h b/lib/lb.h
5f9769
index 42c580b..dfce51c 100644
5f9769
--- a/lib/lb.h
5f9769
+++ b/lib/lb.h
5f9769
@@ -20,6 +20,7 @@
5f9769
 #include <sys/types.h>
5f9769
 #include <netinet/in.h>
5f9769
 #include "openvswitch/hmap.h"
5f9769
+#include "ovn-util.h"
5f9769
 
5f9769
 struct nbrec_load_balancer;
5f9769
 struct sbrec_load_balancer;
5f9769
@@ -37,6 +38,11 @@ struct ovn_northd_lb {
5f9769
     struct ovn_northd_lb_vip *vips_nb;
5f9769
     size_t n_vips;
5f9769
 
5f9769
+    struct lport_addresses hairpin_snat_ips; /* IP (v4 and/or v6) to be used
5f9769
+                                              * as source for hairpinned
5f9769
+                                              * traffic.
5f9769
+                                              */
5f9769
+
5f9769
     size_t n_dps;
5f9769
     size_t n_allocated_dps;
5f9769
     const struct sbrec_datapath_binding **dps;
5f9769
@@ -89,6 +95,11 @@ struct ovn_controller_lb {
5f9769
 
5f9769
     struct ovn_lb_vip *vips;
5f9769
     size_t n_vips;
5f9769
+
5f9769
+    struct lport_addresses hairpin_snat_ips; /* IP (v4 and/or v6) to be used
5f9769
+                                              * as source for hairpinned
5f9769
+                                              * traffic.
5f9769
+                                              */
5f9769
 };
5f9769
 
5f9769
 struct ovn_controller_lb *ovn_controller_lb_create(
5f9769
diff --git a/lib/ovn-util.c b/lib/ovn-util.c
5f9769
index 2136f90..b647106 100644
5f9769
--- a/lib/ovn-util.c
5f9769
+++ b/lib/ovn-util.c
5f9769
@@ -232,6 +232,27 @@ extract_ip_addresses(const char *address, struct lport_addresses *laddrs)
5f9769
     return false;
5f9769
 }
5f9769
 
5f9769
+/* Extracts at most one IPv4 and at most one IPv6 address from 'address'
5f9769
+ * which should be of the format 'IP1 [IP2]'.
5f9769
+ *
5f9769
+ * Return true if at most one IPv4 address and at most one IPv6 address
5f9769
+ * is found in 'address'.  IPs must be host IPs, i.e., no unmasked bits.
5f9769
+ *
5f9769
+ * The caller must call destroy_lport_addresses().
5f9769
+ */
5f9769
+bool extract_ip_address(const char *address, struct lport_addresses *laddrs)
5f9769
+{
5f9769
+    if (!extract_ip_addresses(address, laddrs) ||
5f9769
+            laddrs->n_ipv4_addrs > 1 ||
5f9769
+            laddrs->n_ipv6_addrs > 1 ||
5f9769
+            (laddrs->n_ipv4_addrs && laddrs->ipv4_addrs[0].plen != 32) ||
5f9769
+            (laddrs->n_ipv6_addrs && laddrs->ipv6_addrs[0].plen != 128)) {
5f9769
+        destroy_lport_addresses(laddrs);
5f9769
+        return false;
5f9769
+    }
5f9769
+    return true;
5f9769
+}
5f9769
+
5f9769
 /* Extracts the mac, IPv4 and IPv6 addresses from the
5f9769
  * "nbrec_logical_router_port" parameter 'lrp'.  Stores the IPv4 and
5f9769
  * IPv6 addresses in the 'ipv4_addrs' and 'ipv6_addrs' fields of
5f9769
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
5f9769
index 679f47a..a711363 100644
5f9769
--- a/lib/ovn-util.h
5f9769
+++ b/lib/ovn-util.h
5f9769
@@ -72,6 +72,7 @@ bool extract_addresses(const char *address, struct lport_addresses *,
5f9769
                        int *ofs);
5f9769
 bool extract_lsp_addresses(const char *address, struct lport_addresses *);
5f9769
 bool extract_ip_addresses(const char *address, struct lport_addresses *);
5f9769
+bool extract_ip_address(const char *address, struct lport_addresses *);
5f9769
 bool extract_lrp_networks(const struct nbrec_logical_router_port *,
5f9769
                           struct lport_addresses *);
5f9769
 bool extract_sbrec_binding_first_mac(const struct sbrec_port_binding *binding,
5f9769
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
5f9769
index 62d45f9..d9bcd6f 100644
5f9769
--- a/northd/ovn-northd.c
5f9769
+++ b/northd/ovn-northd.c
5f9769
@@ -3606,6 +3606,7 @@ build_ovn_lbs(struct northd_context *ctx, struct hmap *datapaths,
5f9769
         sbrec_load_balancer_set_name(lb->slb, lb->nlb->name);
5f9769
         sbrec_load_balancer_set_vips(lb->slb, &lb->nlb->vips);
5f9769
         sbrec_load_balancer_set_protocol(lb->slb, lb->nlb->protocol);
5f9769
+        sbrec_load_balancer_set_options(lb->slb, &lb->nlb->options);
5f9769
         sbrec_load_balancer_set_datapaths(
5f9769
             lb->slb, (struct sbrec_datapath_binding **)lb->dps,
5f9769
             lb->n_dps);
5f9769
@@ -8593,15 +8594,10 @@ get_force_snat_ip(struct ovn_datapath *od, const char *key_type,
5f9769
         return false;
5f9769
     }
5f9769
 
5f9769
-    if (!extract_ip_addresses(addresses, laddrs) ||
5f9769
-        laddrs->n_ipv4_addrs > 1 ||
5f9769
-        laddrs->n_ipv6_addrs > 1 ||
5f9769
-        (laddrs->n_ipv4_addrs && laddrs->ipv4_addrs[0].plen != 32) ||
5f9769
-        (laddrs->n_ipv6_addrs && laddrs->ipv6_addrs[0].plen != 128)) {
5f9769
+    if (!extract_ip_address(addresses, laddrs)) {
5f9769
         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
5f9769
         VLOG_WARN_RL(&rl, "bad ip %s in options of router "UUID_FMT"",
5f9769
                      addresses, UUID_ARGS(&od->key));
5f9769
-        destroy_lport_addresses(laddrs);
5f9769
         return false;
5f9769
     }
5f9769
 
5f9769
@@ -13852,6 +13848,7 @@ main(int argc, char *argv[])
5f9769
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_name);
5f9769
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_vips);
5f9769
     add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_protocol);
5f9769
+    add_column_noalert(ovnsb_idl_loop.idl, &sbrec_load_balancer_col_options);
5f9769
     add_column_noalert(ovnsb_idl_loop.idl,
5f9769
                        &sbrec_load_balancer_col_external_ids);
5f9769
 
5f9769
diff --git a/ovn-nb.xml b/ovn-nb.xml
5f9769
index 105d869..86aa438 100644
5f9769
--- a/ovn-nb.xml
5f9769
+++ b/ovn-nb.xml
5f9769
@@ -1644,6 +1644,14 @@
5f9769
         Please note using --reject option will disable empty_lb
5f9769
         SB controller event for this load balancer.
5f9769
       </column>
5f9769
+
5f9769
+      <column name="options" key="hairpin_snat_ip">
5f9769
+        IP to be used as source IP for packets that have been hair-pinned
5f9769
+        after load balancing.  The default behavior when the option is not set
5f9769
+        is to use the load balancer VIP as source IP.  This option may have
5f9769
+        exactly one IPv4 and/or one IPv6 address on it, separated by a space
5f9769
+        character.
5f9769
+      </column>
5f9769
     </group>
5f9769
   
5f9769
 
5f9769
diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema
5f9769
index b418434..0d20f08 100644
5f9769
--- a/ovn-sb.ovsschema
5f9769
+++ b/ovn-sb.ovsschema
5f9769
@@ -1,7 +1,7 @@
5f9769
 {
5f9769
     "name": "OVN_Southbound",
5f9769
-    "version": "20.14.0",
5f9769
-    "cksum": "1412040198 25748",
5f9769
+    "version": "20.15.0",
5f9769
+    "cksum": "539683023 25965",
5f9769
     "tables": {
5f9769
         "SB_Global": {
5f9769
             "columns": {
5f9769
@@ -482,6 +482,11 @@
5f9769
                     "type": {"key": {"type": "uuid",
5f9769
                                      "refTable": "Datapath_Binding"},
5f9769
                              "min": 0, "max": "unlimited"}},
5f9769
+                "options": {
5f9769
+                     "type": {"key": "string",
5f9769
+                              "value": "string",
5f9769
+                              "min": 0,
5f9769
+                              "max": "unlimited"}},
5f9769
                 "external_ids": {
5f9769
                     "type": {"key": "string", "value": "string",
5f9769
                              "min": 0, "max": "unlimited"}}},
5f9769
diff --git a/ovn-sb.xml b/ovn-sb.xml
5f9769
index 980a096..2f251bd 100644
5f9769
--- a/ovn-sb.xml
5f9769
+++ b/ovn-sb.xml
5f9769
@@ -4238,6 +4238,14 @@ tcp.flags = RST;
5f9769
       Datapaths to which this load balancer applies to.
5f9769
     </column>
5f9769
 
5f9769
+    <group title="Load_Balancer options">
5f9769
+    <column name="options" key="hairpin_snat_ip">
5f9769
+      IP to be used as source IP for packets that have been hair-pinned after
5f9769
+      load balancing.  This value is automatically populated by
5f9769
+      ovn-northd.
5f9769
+    </column>
5f9769
+    </group>
5f9769
+
5f9769
     <group title="Common Columns">
5f9769
       <column name="external_ids">
5f9769
         See External IDs at the beginning of this document.
5f9769
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
5f9769
index d52aeed..c02c5d7 100644
5f9769
--- a/tests/ovn-northd.at
5f9769
+++ b/tests/ovn-northd.at
5f9769
@@ -2131,6 +2131,12 @@ echo
5f9769
 echo "__file__:__line__: check that datapath sw1 has lb0 and lb1 set in the load_balancers column."
5f9769
 check_column "$lb0_uuid $lb1_uuid" sb:datapath_binding load_balancers external_ids:name=sw1
5f9769
 
5f9769
+
5f9769
+echo
5f9769
+echo "__file__:__line__: Set hairpin_snat_ip on lb1 and check that SB DB is updated."
5f9769
+check ovn-nbctl --wait=sb set Load_Balancer lb1 options:hairpin_snat_ip="42.42.42.42 4242::4242"
5f9769
+check_column "$lb1_uuid" sb:load_balancer _uuid name=lb1 options='{hairpin_snat_ip="42.42.42.42 4242::4242"}'
5f9769
+
5f9769
 echo
5f9769
 echo "__file__:__line__: Delete load balancer lb1 an check that datapath sw1's load_balancers are updated accordingly."
5f9769
 
5f9769
diff --git a/tests/ovn.at b/tests/ovn.at
5f9769
index 9f2e152..14072ec 100644
5f9769
--- a/tests/ovn.at
5f9769
+++ b/tests/ovn.at
5f9769
@@ -20742,8 +20742,9 @@ build_tcp_syn() {
5f9769
 
5f9769
 send_ipv4_pkt() {
5f9769
     local hv=$1 inport=$2 eth_src=$3 eth_dst=$4
5f9769
-    local ip_src=$5 ip_dst=$6 ip_proto=$7 ip_len=$8 ip_chksum=$9
5f9769
-    local l4_payload=${10}
5f9769
+    local ip_src=$5 ip_dst=$6 ip_proto=$7 ip_len=$8
5f9769
+    local l4_payload=$9
5f9769
+    local hp_ip_src=${10}
5f9769
     local hp_l4_payload=${11}
5f9769
     local outfile=${12}
5f9769
 
5f9769
@@ -20751,8 +20752,10 @@ send_ipv4_pkt() {
5f9769
 
5f9769
     local eth=${eth_dst}${eth_src}0800
5f9769
     local hp_eth=${eth_src}${eth_dst}0800
5f9769
-    local ip=4500${ip_len}00004000${ip_ttl}${ip_proto}${ip_chksum}${ip_src}${ip_dst}
5f9769
-    local hp_ip=4500${ip_len}00004000${ip_ttl}${ip_proto}${ip_chksum}${ip_dst}${ip_src}
5f9769
+    local ip=4500${ip_len}00004000${ip_ttl}${ip_proto}0000${ip_src}${ip_dst}
5f9769
+    ip=$(ip4_csum_inplace $ip)
5f9769
+    local hp_ip=4500${ip_len}00004000${ip_ttl}${ip_proto}0000${hp_ip_src}${ip_src}
5f9769
+    hp_ip=$(ip4_csum_inplace ${hp_ip})
5f9769
     local packet=${eth}${ip}${l4_payload}
5f9769
     local hp_packet=${hp_eth}${hp_ip}${hp_l4_payload}
5f9769
 
5f9769
@@ -20764,15 +20767,16 @@ send_ipv6_pkt() {
5f9769
     local hv=$1 inport=$2 eth_src=$3 eth_dst=$4
5f9769
     local ip_src=$5 ip_dst=$6 ip_proto=$7 ip_len=$8
5f9769
     local l4_payload=$9
5f9769
-    local hp_l4_payload=${10}
5f9769
-    local outfile=${11}
5f9769
+    local hp_ip_src=${10}
5f9769
+    local hp_l4_payload=${11}
5f9769
+    local outfile=${12}
5f9769
 
5f9769
     local ip_ttl=40
5f9769
 
5f9769
     local eth=${eth_dst}${eth_src}86dd
5f9769
     local hp_eth=${eth_src}${eth_dst}86dd
5f9769
     local ip=60000000${ip_len}${ip_proto}${ip_ttl}${ip_src}${ip_dst}
5f9769
-    local hp_ip=60000000${ip_len}${ip_proto}${ip_ttl}${ip_dst}${ip_src}
5f9769
+    local hp_ip=60000000${ip_len}${ip_proto}${ip_ttl}${hp_ip_src}${ip_src}
5f9769
     local packet=${eth}${ip}${l4_payload}
5f9769
     local hp_packet=${hp_eth}${hp_ip}${hp_l4_payload}
5f9769
 
5f9769
@@ -20814,18 +20818,35 @@ ovn-nbctl lsp-add sw sw-rtr                       \
5f9769
 
5f9769
 ovn-nbctl --wait=hv sync
5f9769
 
5f9769
-# Inject IPv4 TCP packet from lsp.
5f9769
+ovn-sbctl dump-flows > sbflows
5f9769
+AT_CAPTURE_FILE([sbflows])
5f9769
 > expected
5f9769
+
5f9769
+# Inject IPv4 TCP packet from lsp.
5f9769
 tcp_payload=$(build_tcp_syn 84d0 1f90 05a7)
5f9769
 hp_tcp_payload=$(build_tcp_syn 84d0 0fc9 156e)
5f9769
 send_ipv4_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
     $(ip_to_hex 42 42 42 1) $(ip_to_hex 88 88 88 88) \
5f9769
-    06 0028 35f5 \
5f9769
-    ${tcp_payload} ${hp_tcp_payload} \
5f9769
+    06 0028 \
5f9769
+    ${tcp_payload} \
5f9769
+    $(ip_to_hex 88 88 88 88) ${hp_tcp_payload} \
5f9769
     expected
5f9769
 
5f9769
-ovn-sbctl dump-flows > sbflows
5f9769
-AT_CAPTURE_FILE([sbflows])
5f9769
+# Check that traffic is hairpinned.
5f9769
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
5f9769
+
5f9769
+# Change LB Hairpin SNAT IP.
5f9769
+# Also flush conntrack to avoid reusing an existing entry.
5f9769
+as hv1 ovs-appctl dpctl/flush-conntrack
5f9769
+ovn-nbctl --wait=hv set load_balancer lb-ipv4-tcp options:hairpin_snat_ip="88.88.88.87"
5f9769
+# Inject IPv4 TCP packet from lsp.
5f9769
+hp_tcp_payload=$(build_tcp_syn 84d0 0fc9 156f)
5f9769
+send_ipv4_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
+    $(ip_to_hex 42 42 42 1) $(ip_to_hex 88 88 88 88) \
5f9769
+    06 0028 \
5f9769
+    ${tcp_payload} \
5f9769
+    $(ip_to_hex 88 88 88 87) ${hp_tcp_payload} \
5f9769
+    expected
5f9769
 
5f9769
 # Check that traffic is hairpinned.
5f9769
 OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
5f9769
@@ -20835,8 +20856,25 @@ udp_payload=$(build_udp 84d0 0fc8 6666)
5f9769
 hp_udp_payload=$(build_udp 84d0 07e5 6e49)
5f9769
 send_ipv4_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
     $(ip_to_hex 42 42 42 1) $(ip_to_hex 88 88 88 88) \
5f9769
-    11 001e 35f4 \
5f9769
-    ${udp_payload} ${hp_udp_payload} \
5f9769
+    11 001e \
5f9769
+    ${udp_payload} \
5f9769
+    $(ip_to_hex 88 88 88 88) ${hp_udp_payload} \
5f9769
+    expected
5f9769
+
5f9769
+# Check that traffic is hairpinned.
5f9769
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
5f9769
+
5f9769
+# Change LB Hairpin SNAT IP.
5f9769
+# Also flush conntrack to avoid reusing an existing entry.
5f9769
+as hv1 ovs-appctl dpctl/flush-conntrack
5f9769
+ovn-nbctl --wait=hv set load_balancer lb-ipv4-udp options:hairpin_snat_ip="88.88.88.87"
5f9769
+# Inject IPv4 UDP packet from lsp.
5f9769
+hp_udp_payload=$(build_udp 84d0 07e5 6e4a)
5f9769
+send_ipv4_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
+    $(ip_to_hex 42 42 42 1) $(ip_to_hex 88 88 88 88) \
5f9769
+    11 001e \
5f9769
+    ${udp_payload} \
5f9769
+    $(ip_to_hex 88 88 88 87) ${hp_udp_payload} \
5f9769
     expected
5f9769
 
5f9769
 # Check that traffic is hairpinned.
5f9769
@@ -20848,7 +20886,25 @@ hp_tcp_payload=$(build_tcp_syn 84d0 0fc9 4fc0)
5f9769
 send_ipv6_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
     42000000000000000000000000000001 88000000000000000000000000000088 \
5f9769
     06 0014 \
5f9769
-    ${tcp_payload} ${hp_tcp_payload} \
5f9769
+    ${tcp_payload} \
5f9769
+    88000000000000000000000000000088 ${hp_tcp_payload} \
5f9769
+    expected
5f9769
+
5f9769
+# Check that traffic is hairpinned.
5f9769
+OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
5f9769
+
5f9769
+# Change LB Hairpin SNAT IP.
5f9769
+# Also flush conntrack to avoid reusing an existing entry.
5f9769
+as hv1 ovs-appctl dpctl/flush-conntrack
5f9769
+ovn-nbctl --wait=hv set load_balancer lb-ipv6-tcp options:hairpin_snat_ip="8800::0087"
5f9769
+
5f9769
+# Inject IPv6 TCP packet from lsp.
5f9769
+hp_tcp_payload=$(build_tcp_syn 84d0 0fc9 4fc1)
5f9769
+send_ipv6_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
+    42000000000000000000000000000001 88000000000000000000000000000088 \
5f9769
+    06 0014 \
5f9769
+    ${tcp_payload} \
5f9769
+    88000000000000000000000000000087 ${hp_tcp_payload} \
5f9769
     expected
5f9769
 
5f9769
 # Check that traffic is hairpinned.
5f9769
@@ -20860,12 +20916,27 @@ hp_udp_payload=$(build_udp 84d0 07e5 a89b)
5f9769
 send_ipv6_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
     42000000000000000000000000000001 88000000000000000000000000000088 \
5f9769
     11 000a \
5f9769
-    ${udp_payload} ${hp_udp_payload} \
5f9769
+    ${udp_payload} \
5f9769
+    88000000000000000000000000000088 ${hp_udp_payload} \
5f9769
     expected
5f9769
 
5f9769
-# Check that traffic is hairpinned.
5f9769
+Check that traffic is hairpinned.
5f9769
 OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [expected])
5f9769
 
5f9769
+# Change LB Hairpin SNAT IP.
5f9769
+# Also flush conntrack to avoid reusing an existing entry.
5f9769
+as hv1 ovs-appctl dpctl/flush-conntrack
5f9769
+ovn-nbctl --wait=hv set load_balancer lb-ipv6-udp options:hairpin_snat_ip="8800::0087"
5f9769
+
5f9769
+# Inject IPv6 UDP packet from lsp.
5f9769
+hp_udp_payload=$(build_udp 84d0 07e5 a89b)
5f9769
+send_ipv6_pkt hv1 hv1-vif1 000000000001 000000000100 \
5f9769
+    42000000000000000000000000000001 88000000000000000000000000000088 \
5f9769
+    11 000a \
5f9769
+    ${udp_payload} \
5f9769
+    88000000000000000000000000000087 ${hp_udp_payload} \
5f9769
+    expected
5f9769
+
5f9769
 OVN_CLEANUP([hv1])
5f9769
 AT_CLEANUP
5f9769
 
5f9769
@@ -23156,7 +23227,7 @@ priority=100,tcp,metadata=0x1,nw_src=42.42.42.1,nw_dst=88.88.88.88,tp_src=4041 a
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8-], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=68], [0], [dnl
5f9769
@@ -23190,8 +23261,8 @@ priority=100,tcp,metadata=0x1,nw_src=52.52.52.52,nw_dst=88.88.88.90,tp_src=4042
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8-], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=68], [0], [dnl
5f9769
@@ -23227,8 +23298,8 @@ priority=100,tcp,metadata=0x1,nw_src=52.52.52.52,nw_dst=88.88.88.90,tp_src=4042
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8-], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 check ovn-nbctl --wait=hv ls-lb-add sw0 lb-ipv4-udp
5f9769
@@ -23256,8 +23327,9 @@ priority=100,udp,metadata=0x1,nw_src=42.42.42.1,nw_dst=88.88.88.88,tp_src=2021 a
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=68 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
@@ -23275,8 +23347,9 @@ priority=100,udp,metadata=0x1,nw_src=42.42.42.1,nw_dst=88.88.88.88,tp_src=2021 a
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 check ovn-nbctl --wait=hv ls-lb-add sw0 lb-ipv6-tcp
5f9769
@@ -23306,9 +23379,10 @@ priority=100,udp,metadata=0x1,nw_src=42.42.42.1,nw_dst=88.88.88.88,tp_src=2021 a
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=68 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
@@ -23328,9 +23402,10 @@ priority=100,udp,metadata=0x1,nw_src=42.42.42.1,nw_dst=88.88.88.88,tp_src=2021 a
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 check ovn-nbctl --wait=hv ls-lb-add sw0 lb-ipv6-udp
5f9769
@@ -23362,9 +23437,11 @@ priority=100,udp6,metadata=0x1,ipv6_src=4200::1,ipv6_dst=8800::88,tp_src=2021 ac
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=68 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
@@ -23386,9 +23463,11 @@ priority=100,udp6,metadata=0x1,ipv6_src=4200::1,ipv6_dst=8800::88,tp_src=2021 ac
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 check ovn-nbctl --wait=hv ls-lb-add sw1 lb-ipv6-udp
5f9769
@@ -23423,10 +23502,12 @@ priority=100,udp6,metadata=0x2,ipv6_src=4200::1,ipv6_dst=8800::88,tp_src=2021 ac
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x2 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x2 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=68 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
@@ -23449,10 +23530,12 @@ priority=100,udp6,metadata=0x2,ipv6_src=4200::1,ipv6_dst=8800::88,tp_src=2021 ac
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x2 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x2 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.90,ct_nw_proto=6,ct_tp_dst=8080,tcp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.90))
5f9769
 ])
5f9769
 
5f9769
 as hv2 ovs-vsctl del-port hv2-vif1
5f9769
@@ -23501,9 +23584,10 @@ priority=100,udp6,metadata=0x2,ipv6_src=4200::1,ipv6_dst=8800::88,tp_src=2021 ac
5f9769
 ])
5f9769
 
5f9769
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=70 | grep -v NXST | cut -d ' ' -f8- | sort], [0], [dnl
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ipv6,metadata=0x2 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
-priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ip,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=17,ct_tp_dst=4040,udp6,metadata=0x2 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_ipv6_dst=8800::88,ct_nw_proto=6,ct_tp_dst=8080,tcp6,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=8800::88))
5f9769
+priority=100,ct_state=+trk+dnat,ct_nw_dst=88.88.88.88,ct_nw_proto=17,ct_tp_dst=4040,udp,metadata=0x1 actions=ct(commit,zone=NXM_NX_REG12[[0..15]],nat(src=88.88.88.88))
5f9769
 ])
5f9769
 
5f9769
 check ovn-nbctl --wait=hv ls-del sw0
5f9769
-- 
5f9769
1.8.3.1
5f9769