Match the drop flow for stateless dnat_and_snat flow in S_ROUTER_IN_GW_REDIRECT
stage just if allowed_ext_ips or exempted_ext_ips conditions do not
match since it breaks the hairping scenario with stateless nat.
Fix the northd documentation.

Fixes: c0224a14f ("northd: fix stateless nat with allowed_ext_ips")
Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2094980
Signed-off-by: Lorenzo Bianconi <[email protected]>
---
 northd/northd.c         | 43 ++++++++++++++++++++++++++++-------------
 northd/ovn-northd.8.xml | 20 ++++++++++++++-----
 tests/system-ovn.at     | 15 ++++++++++++++
 3 files changed, 60 insertions(+), 18 deletions(-)

diff --git a/northd/northd.c b/northd/northd.c
index 0207f6ce1..5264dbd71 100644
--- a/northd/northd.c
+++ b/northd/northd.c
@@ -12035,6 +12035,7 @@ build_gateway_redirect_flows_for_lrouter(
     }
     for (size_t i = 0; i < od->n_l3dgw_ports; i++) {
         const struct ovsdb_idl_row *stage_hint = NULL;
+        bool add_def_flow = true;
 
         if (od->l3dgw_ports[i]->nbrp) {
             stage_hint = &od->l3dgw_ports[i]->nbrp->header_;
@@ -12053,22 +12054,38 @@ build_gateway_redirect_flows_for_lrouter(
         ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT, 50,
                                 ds_cstr(match), ds_cstr(actions),
                                 stage_hint);
-    }
+        for (int j = 0; j < od->n_nat_entries; j++) {
+            const struct ovn_nat *nat = &od->nat_entries[j];
 
-    for (int i = 0; i < od->n_nat_entries; i++) {
-        const struct ovn_nat *nat = &od->nat_entries[i];
+            if (!lrouter_nat_is_stateless(nat->nb) ||
+                strcmp(nat->nb->type, "dnat_and_snat")) {
+                continue;
+            }
 
-        if (!lrouter_nat_is_stateless(nat->nb) ||
-            strcmp(nat->nb->type, "dnat_and_snat")) {
-           continue;
-        }
+            if (nat->nb->allowed_ext_ips || nat->nb->exempted_ext_ips) {
+                struct nbrec_address_set  *as = nat->nb->allowed_ext_ips
+                    ? nat->nb->allowed_ext_ips : nat->nb->exempted_ext_ips;
+                struct ds match_ext = DS_EMPTY_INITIALIZER;
 
-        ds_clear(match);
-        ds_put_format(match, "ip && ip%s.dst == %s",
-                      nat_entry_is_v6(nat) ? "6" : "4",
-                      nat->nb->external_ip);
-        ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 100,
-                      ds_cstr(match), "drop;");
+                ds_put_format(&match_ext, "%s && ip%s.src == $%s",
+                              ds_cstr(match), nat_entry_is_v6(nat) ? "6" : "4",
+                              as->name);
+                ovn_lflow_add_with_hint(lflows, od, S_ROUTER_IN_GW_REDIRECT,
+                                        75, ds_cstr(&match_ext),
+                                        ds_cstr(actions), stage_hint);
+
+                if (add_def_flow) {
+                    ds_clear(&match_ext);
+                    ds_put_format(&match_ext, "ip && ip%s.dst == %s",
+                                  nat_entry_is_v6(nat) ? "6" : "4",
+                                  nat->nb->external_ip);
+                    ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 70,
+                                  ds_cstr(&match_ext), "drop;");
+                    add_def_flow = false;
+                }
+                ds_destroy(&match_ext);
+            }
+        }
     }
 
     /* Packets are allowed by default. */
diff --git a/northd/ovn-northd.8.xml b/northd/ovn-northd.8.xml
index 1f7022490..ad1a66cd4 100644
--- a/northd/ovn-northd.8.xml
+++ b/northd/ovn-northd.8.xml
@@ -3018,8 +3018,7 @@ icmp6 {
           <code>ip &amp;&amp; ip6.dst == <var>B</var></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>.
+          options, then the action would be <code>next;</code>.
         </p>
 
         <p>
@@ -3059,7 +3058,7 @@ icmp6 {
               action <code>ct_snat_in_czone;</code> to unSNAT in the common
               zone.  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>.
+              would be <code>next;</code>.
             </p>
 
             <p>
@@ -4235,6 +4234,18 @@ icmp6 {
         gateway chassis.
       </li>
 
+      <li>
+        For each <code>dnat_and_snat</code> NAT rule with
+        <code>stateless=true</code> and <code>allowed_ext_ips</code>
+        or <code>exempted_ext_ips</code> configured, a similar priority-75
+        flow is programmed with an additional match
+        <code>ip4.src == <var>allowed_ext_ips</var></code> or
+        <code>ip4.src == <var>exempted_ext_ips</var></code>.
+        Moreover a priority-70 drop flow is added with match
+        <code>ip4.dst == <var>B</var></code> where <var>B</var> is the NAT
+        rule external IP. A specular flow is added for IPv6 traffic.
+      </li>
+
       <li>
         A priority-0 logical flow with match <code>1</code> has actions
         <code>next;</code>.
@@ -4415,8 +4426,7 @@ nd_ns {
           is the logical router gateway port, with an action
           <code>ct_dnat_in_czone;</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>.
+          options, then the action would be <code>next;</code>.
         </p>
 
         <p>
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
index 4bf22593a..df2da3408 100644
--- a/tests/system-ovn.at
+++ b/tests/system-ovn.at
@@ -6741,6 +6741,21 @@ NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 
172.18.2.10 | FORMAT_PING], \
 [0], [dnl
 3 packets transmitted, 3 received, 0% packet loss, time 0ms
 ])
+
+dnat_and_snat_uuid=$(fetch_column nb:NAT _uuid external_ip=172.18.2.10)
+ovn-nbctl set NAT $dnat_and_snat_uuid options:stateless=true
+
+# A ping from vm1 should hairpin in lr1 and successfully DNAT to vm2
+NS_CHECK_EXEC([vm1], [ping -q -c 3 -i 0.3 -w 2 172.18.2.10 | FORMAT_PING], \
+[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+# A ping from vm2 should hairpin in lr1 and successfully DNAT to vm2
+NS_CHECK_EXEC([vm2], [ping -q -c 3 -i 0.3 -w 2 172.18.2.10 | FORMAT_PING], \
+[0], [dnl
+3 packets transmitted, 3 received, 0% packet loss, time 0ms
+])
+
 kill $(pidof ovn-controller)
 
 as ovn-sb
-- 
2.35.3

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to