Blob Blame History Raw
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