Add helpers for parsing masked MAC in format of MAC/MAC and
MAC/prefix. In additions to utils that convert MAC into prefix
len and in reverse, creating MAC mask from prefix len. This
will be used for the extended port_security parsing.

Signed-off-by: Ales Musil <[email protected]>
---
 lib/ovn-util.c   | 49 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/ovn-util.h   | 18 ++++++++++++++++++
 tests/ovn.at     | 49 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-ovn.c | 22 ++++++++++++++++++++++
 4 files changed, 138 insertions(+)

diff --git a/lib/ovn-util.c b/lib/ovn-util.c
index 71098d478..c9de6567c 100644
--- a/lib/ovn-util.c
+++ b/lib/ovn-util.c
@@ -1737,3 +1737,52 @@ is_partial_uuid_match(const struct uuid *uuid, const 
char *match)
     s2 = strip_leading_zero(s2);
     return !strncmp(s1, s2, strlen(s2));
 }
+
+/* Parses string 's', which must be a MAC address with an optional mask or
+ * mask prefix length.  Stores the MAC address into '*ea' and the mask prefix
+ * length into '*len' (if 's' does not contain a mask, all-one-bits
+ * is assumed).
+ *
+ * Returns true if successful, otherwise false and the '*ea' is zeroed out in
+ * case. */
+bool
+eth_addr_parse_masked(const char *s, struct eth_addr *ea, unsigned int *plen)
+{
+    int n = 0;
+
+    if (!ovs_scan_len(s, &n, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*ea))) {
+        *ea = eth_addr_zero;
+        return false;
+    }
+
+    /* There isn't any mask provided. */
+    if (s[n] != '/') {
+        *plen = 48;
+        return true;
+    }
+
+    struct eth_addr mask;
+    if (ovs_scan_len(s, &n, "/"ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mask))) {
+        int prefix = eth_addr_get_prefix_len(mask);
+        if (!eth_addr_equals(mask, eth_addr_create_mask(prefix))) {
+            *ea = eth_addr_zero;
+            return false;
+        }
+
+        *plen = prefix;
+        return true;
+    }
+
+    int prefix;
+    if (ovs_scan_len(s, &n, "/%d", &prefix)) {
+        if (prefix < 0 || prefix > 48) {
+            *ea = eth_addr_zero;
+            return false;
+        }
+        *plen = prefix;
+        return true;
+    }
+
+    *ea = eth_addr_zero;
+    return false;
+}
diff --git a/lib/ovn-util.h b/lib/ovn-util.h
index 74931ed29..d256cfbde 100644
--- a/lib/ovn-util.h
+++ b/lib/ovn-util.h
@@ -672,6 +672,24 @@ is_uuid_with_prefix(const char *uuid)
      return uuid[0] == '0' && (uuid[1] == 'x' || uuid[1] == 'X');
 }
 
+static inline struct eth_addr
+eth_addr_create_mask(unsigned int len)
+{
+    struct eth_addr mac;
+    eth_addr_from_uint64((UINT64_MAX << (48 - len)), &mac);
+
+    return mac;
+}
+
+static inline unsigned int
+eth_addr_get_prefix_len(struct eth_addr mac)
+{
+    uint64_t n = (UINT64_C(0xffff) << 48) | eth_addr_to_uint64(mac);
+    return 48 - ctz64(n);
+}
+
+bool eth_addr_parse_masked(const char *, struct eth_addr *, unsigned int *);
+
 bool is_partial_uuid_match(const struct uuid *uuid, const char *match);
 
 /* Utilities around properly handling exit command. */
diff --git a/tests/ovn.at b/tests/ovn.at
index 8ffe469c8..52d47e36b 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -2411,6 +2411,55 @@ check ovstest test-sparse-array add
 check ovstest test-sparse-array remove-replace
 AT_CLEANUP
 
+AT_SETUP([Parse MAC])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:xx], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:ff:ff:ff 01:02:03:04:05:06/48
+])
+
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/-1], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/-40], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/49], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/128], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/48], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:ff:ff:ff 01:02:03:04:05:06/48
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/40], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:ff:ff:00 01:02:03:04:05:06/40
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:00/40], [0], [dnl
+01:02:03:04:05:00/ff:ff:ff:ff:ff:00 01:02:03:04:05:00/40
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/25], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:80:00:00 01:02:03:04:05:06/25
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:00:00:00/25], [0], [dnl
+01:02:03:00:00:00/ff:ff:ff:80:00:00 01:02:03:00:00:00/25
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 01:02:03:04:05:06/0], [0], [dnl
+01:02:03:04:05:06/00:00:00:00:00:00 01:02:03:04:05:06/0
+])
+
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/ff:ff:ff:ff:ff:xx], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/ff:ff:ff:ff:0f:00], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/01:02:03:04:05:00], [1])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/ff:ff:ff:ff:ff:00], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:ff:ff:00 01:02:03:04:05:06/40
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/ff:ff:ff:ff:00:00], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:ff:00:00 01:02:03:04:05:06/32
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/ff:ff:ff:00:00:00], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:00:00:00 01:02:03:04:05:06/24
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/ff:ff:ff:ff:ff:f0], [0], [dnl
+01:02:03:04:05:06/ff:ff:ff:ff:ff:f0 01:02:03:04:05:06/44
+])
+AT_CHECK([ovstest test-ovn parse-eth-addr 
01:02:03:04:05:06/00:00:00:00:00:00], [0], [dnl
+01:02:03:04:05:06/00:00:00:00:00:00 01:02:03:04:05:06/0
+])
+AT_CLEANUP
+
 AT_BANNER([OVN end-to-end tests])
 
 OVN_FOR_EACH_NORTHD([
diff --git a/tests/test-ovn.c b/tests/test-ovn.c
index fae7e7bd5..5fe1788bb 100644
--- a/tests/test-ovn.c
+++ b/tests/test-ovn.c
@@ -1466,6 +1466,22 @@ test_parse_actions(struct ovs_cmdl_context *ctx 
OVS_UNUSED)
     flow_collector_ids_destroy(&collector_ids);
     exit(ok ? EXIT_SUCCESS : EXIT_FAILURE);
 }
+
+static void
+test_parse_eth_addr(struct ovs_cmdl_context *ctx)
+{
+    unsigned int plen;
+    struct eth_addr mac;
+
+    if (!eth_addr_parse_masked(ctx->argv[1], &mac, &plen)) {
+        ovs_assert(eth_addr_equals(mac, eth_addr_zero));
+        exit(EXIT_FAILURE);
+    }
+
+    printf(ETH_ADDR_FMT "/" ETH_ADDR_FMT " " ETH_ADDR_FMT "/%u\n",
+           ETH_ADDR_ARGS(mac), ETH_ADDR_ARGS(eth_addr_create_mask(plen)),
+           ETH_ADDR_ARGS(mac), plen);
+}
 
 static unsigned int
 parse_relops(const char *s)
@@ -1552,6 +1568,9 @@ exhaustive N\n\
 parse-actions\n\
   Parses OVN actions from stdin and prints the equivalent OpenFlow actions\n\
   on stdout.\n\
+parse-eth-addr\n\
+  Parses masked MAC address from stdin and prints the equivalent MAC/plen \n\
+  and MAC/mask to stdout\n\
 ",
            program_name, program_name);
     exit(EXIT_SUCCESS);
@@ -1677,6 +1696,9 @@ test_ovn_main(int argc, char *argv[])
         /* Actions. */
         {"parse-actions", NULL, 0, 0, test_parse_actions, OVS_RO},
 
+        /* Utils. */
+        {"parse-eth-addr", NULL, 1, 1, test_parse_eth_addr, OVS_RO},
+
         {NULL, NULL, 0, 0, NULL, OVS_RO},
     };
     struct ovs_cmdl_context ctx;
-- 
2.52.0

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

Reply via email to