[ovs-dev] [PATCH v2 2/2 ovn] External IP based NAT: NORTHD changes to use allowed/disallowed external ip

Ankur Sharma svc.mail.git at nutanix.com
Mon Jun 29 01:34:40 UTC 2020


From: Ankur Sharma <ankur.sharma at nutanix.com>

This patch has northd changes which consumes allowed/disallowed external ip
configuration per NAT rule in logical flow.

Allowed/Disallowed external ip range adds an additional match criteria in
snat/dnat/unsnat/undant logical flow rules.

For example, if an allowed_external_ip address set ("efgh")
is configured for following NAT rule.
TYPE             EXTERNAL_IP        LOGICAL_IP
snat             10.15.24.135       50.0.0.10

Then logical flow will look like following:
..(lr_out_snat)...match=(ip && .... && ip4.dst == $efgh), action=(ct_snat(...);)
...(lr_in_unsnat...)match=(ip && ..... && ip4.src == $efgh), action=(ct_snat;)

Signed-off-by: Ankur Sharma <ankur.sharma at nutanix.com>
---
 northd/ovn-northd.c |  82 +++++++++++++++++++++++++++++++++
 tests/ovn-northd.at | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 209 insertions(+)

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index f49e4f2..3858de9 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -8809,6 +8809,21 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             struct in6_addr ipv6, mask_v6, v6_exact = IN6ADDR_EXACT_INIT;
             bool is_v6 = false;
             bool stateless = lrouter_nat_is_stateless(nat);
+            struct nbrec_address_set *allowed_external_ip =
+                                      nat->allowed_external_ip;
+            struct nbrec_address_set *disallowed_external_ip =
+                                      nat->disallowed_external_ip;
+
+            if (allowed_external_ip && disallowed_external_ip) {
+                static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+                VLOG_INFO_RL(&rl, "Both allowed: %s and disallowed: %s "
+                             "external ips set for NAT, type: %s, "
+                             "logical_ip: %s, external_ip: %s",
+                             allowed_external_ip->name,
+                             disallowed_external_ip->name,
+                             nat->type, nat->logical_ip, nat->external_ip);
+                continue;
+            }
 
             char *error = ip_parse_masked(nat->external_ip, &ip, &mask);
             if (error || mask != OVS_BE32_MAX) {
@@ -8896,6 +8911,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&match, "ip && ip%s.dst == %s",
                                   is_v6 ? "6" : "4",
                                   nat->external_ip);
+                    if (allowed_external_ip || disallowed_external_ip) {
+                        ds_put_format(&match, " && ip%s.src %s $%s",
+                                      is_v6 ? "6" : "4",
+                                      allowed_external_ip? "==" : "!=",
+                                      allowed_external_ip?
+                                      allowed_external_ip->name :
+                                      disallowed_external_ip->name);
+                    }
+
                     if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
                        ds_put_format(&actions, "ip%s.dst=%s; next;",
                                      is_v6 ? "6" : "4", nat->logical_ip);
@@ -8925,6 +8949,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                                       od->l3redirect_port->json_key);
                     }
 
+                    if (allowed_external_ip || disallowed_external_ip) {
+                        ds_put_format(&match, " && ip%s.src %s $%s",
+                                      is_v6 ? "6" : "4",
+                                      allowed_external_ip? "==" : "!=",
+                                      allowed_external_ip?
+                                      allowed_external_ip->name :
+                                      disallowed_external_ip->name);
+                    }
+
                     if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
                         ds_put_format(&actions, "ip%s.dst=%s; next;",
                                       is_v6 ? "6" : "4", nat->logical_ip);
@@ -8953,6 +8986,15 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&match, "ip && ip%s.dst == %s",
                                   is_v6 ? "6" : "4",
                                   nat->external_ip);
+                    if (allowed_external_ip || disallowed_external_ip) {
+                        ds_put_format(&match, " && ip%s.src %s $%s",
+                                      is_v6 ? "6" : "4",
+                                      allowed_external_ip? "==" : "!=",
+                                      allowed_external_ip?
+                                      allowed_external_ip->name :
+                                      disallowed_external_ip->name);
+                    }
+
                     ds_clear(&actions);
                     if (dnat_force_snat_ip) {
                         /* Indicate to the future tables that a DNAT has taken
@@ -8996,6 +9038,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                         ds_put_format(&match, " && is_chassis_resident(%s)",
                                       od->l3redirect_port->json_key);
                     }
+
+                    if (allowed_external_ip || disallowed_external_ip) {
+                        ds_put_format(&match, " && ip%s.src %s $%s",
+                                      is_v6 ? "6" : "4",
+                                      allowed_external_ip? "==" : "!=",
+                                      allowed_external_ip?
+                                      allowed_external_ip->name :
+                                      disallowed_external_ip->name);
+                    }
+
                     ds_clear(&actions);
 
                     if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
@@ -9076,6 +9128,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&match, " && is_chassis_resident(%s)",
                                   od->l3redirect_port->json_key);
                 }
+
+                if (allowed_external_ip || disallowed_external_ip) {
+                    ds_put_format(&match, " && ip%s.dst %s $%s",
+                                  is_v6 ? "6" : "4",
+                                  allowed_external_ip? "==" : "!=",
+                                  allowed_external_ip?
+                                  allowed_external_ip->name :
+                                  disallowed_external_ip->name);
+                }
+
                 ds_clear(&actions);
                 if (distributed) {
                     ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
@@ -9105,6 +9167,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                     ds_put_format(&match, "ip && ip%s.src == %s",
                                   is_v6 ? "6" : "4",
                                   nat->logical_ip);
+
+                    if (allowed_external_ip || disallowed_external_ip) {
+                        ds_put_format(&match, " && ip%s.dst %s $%s",
+                                      is_v6 ? "6" : "4",
+                                      allowed_external_ip? "==" : "!=",
+                                      allowed_external_ip?
+                                      allowed_external_ip->name :
+                                      disallowed_external_ip->name);
+                    }
+
                     ds_clear(&actions);
 
                     if (!strcmp(nat->type, "dnat_and_snat") && stateless) {
@@ -9145,6 +9217,16 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
                         ds_put_format(&match, " && is_chassis_resident(%s)",
                                       od->l3redirect_port->json_key);
                     }
+
+                    if (allowed_external_ip || disallowed_external_ip) {
+                        ds_put_format(&match, " && ip%s.dst %s $%s",
+                                      is_v6 ? "6" : "4",
+                                      allowed_external_ip? "==" : "!=",
+                                      allowed_external_ip?
+                                      allowed_external_ip->name :
+                                      disallowed_external_ip->name);
+                    }
+
                     ds_clear(&actions);
                     if (distributed) {
                         ds_put_format(&actions, "eth.src = "ETH_ADDR_FMT"; ",
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 80bda0a..4d574b2 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -1140,6 +1140,133 @@ ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.1
 
 AT_CLEANUP
 
+AT_SETUP([ovn -- check allowed/disallowed external dnat, snat and dnat_and_snat rules])
+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 create Address_Set name=allowed_range addresses=\"1.1.1.1\"
+ovn-nbctl create Address_Set name=disallowed_range addresses=\"2.2.2.2\"
+
+# SNAT with ALLOWED_IPs
+ovn-nbctl lr-nat-add R1 snat  172.16.1.1 50.0.0.11
+ovn-nbctl --is-allowed lr-nat-update-ext-ip R1 snat 50.0.0.11 allowed_range
+
+OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_out_snat | wc -l`])
+
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_snat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_unsnat | grep "ip4.dst == 172.16.1.1" | grep "ip4.src == $allowed_range" | wc -l], [0], [1
+])
+
+# SNAT with DISALLOWED_IPs
+ovn-nbctl lr-nat-del R1 snat  50.0.0.11
+ovn-nbctl lr-nat-add R1 snat  172.16.1.1 50.0.0.11
+ovn-nbctl lr-nat-update-ext-ip R1 snat 50.0.0.11 disallowed_range
+
+OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_out_snat | \
+wc -l`])
+
+ovn-nbctl show R1
+ovn-sbctl dump-flows R1
+
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_snat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_unsnat | grep "ip4.dst == 172.16.1.1" | grep "ip4.src != $disallowed_range" | wc -l], [0], [1
+])
+
+# Stateful FIP with ALLOWED_IPs
+ovn-nbctl lr-nat-del R1 snat  50.0.0.11
+ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.2 50.0.0.11
+ovn-nbctl --is-allowed lr-nat-update-ext-ip R1 dnat_and_snat 172.16.1.2 allowed_range
+ovn-nbctl show R1
+ovn-sbctl dump-flows R1
+
+OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_out_snat | \
+wc -l`])
+
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_snat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_unsnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_undnat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_dnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src == $allowed_range" | wc -l], [0], [1
+])
+
+# Stateful FIP with DISALLOWED_IPs
+ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.2
+ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.2 50.0.0.11
+ovn-nbctl lr-nat-update-ext-ip R1 dnat_and_snat 172.16.1.2 disallowed_range
+ovn-nbctl show R1
+ovn-sbctl dump-flows R1
+
+OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_out_snat | \
+wc -l`])
+
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_snat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_unsnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_undnat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_dnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src != $disallowed_range" | wc -l], [0], [1
+])
+
+# Stateless FIP with DISALLOWED_IPs
+ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.2
+ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat  172.16.1.2 50.0.0.11
+ovn-nbctl --is-allowed lr-nat-update-ext-ip R1 dnat_and_snat 172.16.1.2 allowed_range
+ovn-nbctl show R1
+ovn-sbctl dump-flows R1
+
+OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_out_snat | \
+wc -l`])
+
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_snat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_unsnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_undnat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst == $allowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_dnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src == $allowed_range" | wc -l], [0], [1
+])
+
+# Stateful FIP with DISALLOWED_IPs
+ovn-nbctl lr-nat-del R1 dnat_and_snat  172.16.1.2
+ovn-nbctl --stateless lr-nat-add R1 dnat_and_snat  172.16.1.2 50.0.0.11
+ovn-nbctl lr-nat-update-ext-ip R1 dnat_and_snat 172.16.1.2 disallowed_range
+ovn-nbctl show R1
+ovn-sbctl dump-flows R1
+
+OVS_WAIT_UNTIL([test 3 = `ovn-sbctl dump-flows R1 | grep lr_out_snat | \
+wc -l`])
+
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_snat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_unsnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_out_undnat | grep "ip4.src == 50.0.0.11" | grep "ip4.dst != $disallowed_range" | wc -l], [0], [1
+])
+AT_CHECK([ovn-sbctl dump-flows R1 | grep lr_in_dnat | grep "ip4.dst == 172.16.1.2" | grep "ip4.src != $disallowed_range" | wc -l], [0], [1
+])
+
+AT_CLEANUP
+
 AT_SETUP([ovn -- check Load balancer health check and Service Monitor sync])
 AT_SKIP_IF([test $HAVE_PYTHON = no])
 ovn_start
-- 
1.8.3.1



More information about the dev mailing list