On Mon, Feb 9, 2026 at 4:13 PM Ales Musil <[email protected]> wrote:

> 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.
>
> Acked-by: Dumitru Ceara <[email protected]>
> Acked-by: Lorenzo Bianconi <[email protected]>
> Signed-off-by: Ales Musil <[email protected]>
> ---
> v4: Rebase on top of latest main.
> v5: Rebase on top of latest main.
>     Add Dumitru's ack.
>     Add Lorenzo's ack.
> ---
>  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 8a065e3c2..46b837c02 100644
> --- a/lib/ovn-util.c
> +++ b/lib/ovn-util.c
> @@ -1762,3 +1762,52 @@ shuffled_range(size_t n)
>
>      return indices;
>  }
> +
> +/* 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 0ba0d1c26..402a7b155 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 802e6d0da..c2d113f8e 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -2443,6 +2443,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 e88d3d45e..f35bd980c 100644
> --- a/tests/test-ovn.c
> +++ b/tests/test-ovn.c
> @@ -1474,6 +1474,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)
> @@ -1560,6 +1576,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);
> @@ -1685,6 +1704,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.53.0
>
>
Recheck-request: github-robot
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to