On Thu, Feb 12, 2026 at 1:00 PM Dumitru Ceara <[email protected]> wrote:

> On 2/11/26 10:37 AM, Ales Musil 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.
> > v6: Rebase on top of latest main.
> > v7: Rebase on top of latest main.
> > v8: Rebase on top of latest main.
> >     Address small nit from Dumitru.
> > ---
>
> Hi Ales,
>
> >  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)
>
> I guess we both forgot about this in v6-v8, the comment talks about
> 'len' but the argument is called 'plen':
>
> https://mail.openvswitch.org/pipermail/ovs-dev/2026-February/430065.html
>
> With that addressed:
> Acked-by: Dumitru Ceara <[email protected]>
>
> Thanks,
> Dumitru
>
> > +{
> > +    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..3c89eeba0 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;
>
>
Thank you Lorenzo and Dumitru,

I took care of the nit and merged this into main.

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

Reply via email to