This patch alters the 'ovn-nbctl lsp-set-addresses' command to check if
the addresses being added are duplicates of already-set addresses. In
the case of MAC addresses, it compares to all switch ports across the
logical network. In the case of IP addresses, it only compares to other
switch ports on the logical switch. Test cases have been added for this
detection.

This patch also adds a warning message to ovn-northd if duplicate IPv4
addresses are detected on a switch.

Signed-off-by: Mark Michelson <[email protected]>
---
v1 -> v2:
 Fixed sparse warning from using htons() instead of htonl()
---
 ovn/northd/ovn-northd.c   |   6 +++
 ovn/utilities/ovn-nbctl.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/ovn.at              |  77 ++++++++++++++++++++++++++++++
 3 files changed, 201 insertions(+)

diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 72e25181d..31ea5f410 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -944,6 +944,12 @@ ipam_insert_ip(struct ovn_datapath *od, uint32_t ip)
 
     if (ip >= od->ipam_info.start_ipv4 &&
         ip < (od->ipam_info.start_ipv4 + od->ipam_info.total_ipv4s)) {
+        if (bitmap_is_set(od->ipam_info.allocated_ipv4s,
+                          ip - od->ipam_info.start_ipv4)) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
+            VLOG_WARN_RL(&rl, "Duplicate IP set on switch %s: "IP_FMT,
+                         od->nbs->name, IP_ARGS(htonl(ip)));
+        }
         bitmap_set1(od->ipam_info.allocated_ipv4s,
                     ip - od->ipam_info.start_ipv4);
     }
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index 4f7e7241a..92ff5685a 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -1437,6 +1437,111 @@ nbctl_lsp_get_tag(struct ctl_context *ctx)
     }
 }
 
+static char *
+lsp_contains_duplicate_mac(struct ctl_context *ctx,
+                           const struct nbrec_logical_switch_port *lsp,
+                           const struct eth_addr ea)
+{
+    const struct nbrec_logical_switch_port *lsp_test;
+    NBREC_LOGICAL_SWITCH_PORT_FOR_EACH (lsp_test, ctx->idl) {
+        if (lsp == lsp_test) {
+            continue;
+        }
+        for (size_t i = 0; i < lsp_test->n_addresses; i++) {
+            char *addr = lsp_test->addresses[i];
+            if (is_dynamic_lsp_address(addr)) {
+                addr = lsp_test->dynamic_addresses;
+            }
+            struct eth_addr ea_test;
+            if (ovs_scan(addr, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea_test))
+                && eth_addr_equals(ea, ea_test)) {
+                return xasprintf("duplicate ethernet address "ETH_ADDR_FMT,
+                                 ETH_ADDR_ARGS(ea));
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static char *
+lsp_contains_duplicate_ip(struct lport_addresses *laddrs1,
+                          struct lport_addresses *laddrs2)
+{
+    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);
+            }
+        }
+    }
+
+    for (size_t i = 0; i < laddrs1->n_ipv6_addrs; i++) {
+        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 NULL;
+}
+
+static char *
+lsp_contains_duplicates(struct ctl_context *ctx,
+                        const struct nbrec_logical_switch *ls,
+                        const struct nbrec_logical_switch_port *lsp,
+                        const char *address)
+{
+    struct eth_addr ea;
+
+    char *sub_error;
+    if (ovs_scan(address, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
+        sub_error = lsp_contains_duplicate_mac(ctx, lsp, ea);
+        if (sub_error) {
+            goto err_out;
+        }
+    }
+
+    struct lport_addresses laddrs;
+    if (!extract_lsp_addresses(address, &laddrs)) {
+        return NULL;
+    }
+
+    for (size_t i = 0; i < ls->n_ports; i++) {
+        struct nbrec_logical_switch_port *lsp_test = ls->ports[i];
+        if (lsp_test == lsp) {
+            continue;
+        }
+        for (size_t j = 0; j < lsp_test->n_addresses; j++) {
+            struct lport_addresses laddrs_test;
+            char *addr = lsp_test->addresses[j];
+            if (is_dynamic_lsp_address(addr)) {
+                addr = lsp_test->dynamic_addresses;
+            }
+            if (extract_lsp_addresses(addr, &laddrs_test)) {
+                sub_error = lsp_contains_duplicate_ip(&laddrs, &laddrs_test);
+                destroy_lport_addresses(&laddrs_test);
+                if (sub_error) {
+                    destroy_lport_addresses(&laddrs);
+                    goto err_out;
+                }
+            }
+        }
+    }
+
+    destroy_lport_addresses(&laddrs);
+    return NULL;
+
+err_out: ;
+    char *error = xasprintf("Error on switch %s: %s", ls->name, sub_error);
+    free(sub_error);
+    return error;
+}
+
 static void
 nbctl_lsp_set_addresses(struct ctl_context *ctx)
 {
@@ -1449,6 +1554,13 @@ nbctl_lsp_set_addresses(struct ctl_context *ctx)
         return;
     }
 
+    const struct nbrec_logical_switch *ls;
+    error = lsp_to_ls(ctx->idl, lsp, &ls);
+    if (error) {
+        ctx->error = error;
+        return;
+    }
+
     int i;
     for (i = 2; i < ctx->argc; i++) {
         struct eth_addr ea;
@@ -1463,6 +1575,12 @@ nbctl_lsp_set_addresses(struct ctl_context *ctx)
                       "argument.", ctx->argv[i]);
             return;
         }
+
+        error = lsp_contains_duplicates(ctx, ls, lsp, ctx->argv[i]);
+        if (error) {
+            ctl_error(ctx, "%s", error);
+            return;
+        }
     }
 
     nbrec_logical_switch_port_set_addresses(lsp,
diff --git a/tests/ovn.at b/tests/ovn.at
index e10a7f9ba..b135c6f71 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -11194,3 +11194,80 @@ as hv2 start_daemon ovn-controller
 OVN_CLEANUP([hv1],[hv2])
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- ovn-nbctl duplicate addresses])
+ovn_start
+
+# Set up a switch with some switch ports of varying address types
+ovn-nbctl ls-add sw1
+ovn-nbctl set logical_switch sw1 other_config:subnet=192.168.0.0/24
+
+ovn-nbctl lsp-add sw1 sw1-p1
+ovn-nbctl lsp-add sw1 sw1-p2
+ovn-nbctl lsp-add sw1 sw1-p3
+ovn-nbctl lsp-add sw1 sw1-p4
+
+ovn-nbctl lsp-set-addresses sw1-p1 "00:00:00:00:00:01 10.0.0.1 aef0::1" 
"00:00:00:00:00:02 10.0.0.2 aef0::2"
+ovn-nbctl lsp-set-addresses sw1-p2 "00:00:00:00:00:03 dynamic"
+ovn-nbctl lsp-set-addresses sw1-p3 "dynamic"
+ovn-nbctl lsp-set-addresses sw1-p4 "router"
+ovn-nbctl lsp-set-addresses sw1-p5 "unknown"
+
+ovn-nbctl list logical_switch_port
+
+# Now try to add duplicate addresses on a new port. These should all fail
+ovn-nbctl lsp-add sw1 sw1-p5
+
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:01"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate ethernet address 00:00:00:00:00:01
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:02"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate ethernet address 00:00:00:00:00:02
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:03"], [1], [],
+[ovn-nbctl: Error on switch sw1: duplicate ethernet address 00:00:00:00:00:03
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p5 "00:00:00:00:00:01 dynamic"], 
[1], [],
+[ovn-nbctl: Error on switch sw1: duplicate ethernet address 00:00:00:00:00:01
+])
+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
+])
+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
+])
+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
+])
+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
+])
+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
+])
+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
+])
+
+# Now try re-setting sw1-p1. This should succeed
+AT_CHECK([ovn-nbctl lsp-set-addresses sw1-p1 "00:00:00:00:00:01 10.0.0.1 
aef0::1"])
+
+# Now create a new switch and try setting IP addresses the same as the
+# first switch. This should succeed.
+ovn-nbctl ls-add sw2
+ovn-nbctl lsp-add sw2 sw2-p1
+
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 10.0.0.1"])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 192.168.0.2"])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 192.168.0.3"])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:04 aef0::1"])
+
+# Now try setting duplicate MACs from sw1. These should fail.
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:01"], [1], [],
+[ovn-nbctl: Error on switch sw2: duplicate ethernet address 00:00:00:00:00:01
+])
+AT_CHECK([ovn-nbctl lsp-set-addresses sw2-p1 "00:00:00:00:00:03"], [1], [],
+[ovn-nbctl: Error on switch sw2: duplicate ethernet address 00:00:00:00:00:03
+])
+
+AT_CLEANUP
-- 
2.14.4

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

Reply via email to